自然语言处理 ( Natural Language Processing, NLP)
前言
自然语言处理 ( Natural Language Processing, NLP) 是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法,用于分析理解机器与人之间的交互,常用的领域有:实体识别、文本纠错、情感分析、文本分类、关键词提取、自动摘要提取等方面。
本文将从分词、词频、词向量等基础领域开始讲解自然语言处理的原理,讲解 One-Hot、TF-IDF、PageRank 等算法及 LDA、LDiA、LSA 等语义分析的原理。
介绍 Word2vec、GloVe、Embedding 等常用词嵌入及 NLTK、Jieba 等分词工具的应用。
一、自然语言处理的概念
二、分词器的原理及应用
三、词向量算法原理
四、文本相似度分析
五、通过主题转换进行语义分析
六、词嵌入的应用
回到目录
回到目录
回到目录
回到目录
》
SVD 公式表示如下,m 为词汇中的分词数量,n 为文档数量,p为库的主题数量。通过 SVD 算法,可以把包含大量分词的文章划分成多个主题的专栏。
其中向量 U 是 分词-主题 矩阵,它给出分词所具有的上下文信息,代表分词与主题的相互关系,也被称为 “ 左奇异向量 ”。
向量 S 是主题奇异值的对象线方阵,例如有 6 个主题的文档库 S 值就会是一个 6*6 的矩阵。
向量 V 是 主题-文档 矩阵,它建立了新主题与文档之间的关系,也被称为 “ 右奇异向量 ”。
5.3 TruncatedSVD 模型
在 sk-learn 库中提供了sklearn.decomposition.TruncatedSVD 模型用于进行 SVD 分析。SVD 是无监督模型,通过SVD 可以把多维的数量进行主题转换实现降维,常被用于情感分析和垃圾信息处理。
1 class TruncatedSVD(TransformerMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_components=2, *, algorithm="randomized", n_iter=5, 4 random_state=None, tol=0.): 5 self.algorithm = algorithm 6 self.n_components = n_components 7 self.n_iter = n_iter 8 self.random_state = random_state 9 self.tol = tol
参数说明
- algorithm:str 类型 {'arpack', 'randomized'} 之一,默认值为 “randomized”,用于选择 SVD 算法。arpack 为 SciPy中 ARPACK 包装器( "scipy.sparse.linalg.svds"); randomized 为算法由于Halko 中的随机算法(randomized)
- n_components:int 类型,默认值为 2,选择主题数量
- n_iter: int 类型,默认值为 5,运算时的迭代次数
- randow_state:int ,RandomState 实例或 None,默认值为 None。在随机初始化 svd 期间使用,传递一个 int 以获得可重现的结果多个函数调用。
- tol: float 类型,默认值为 0.0。当 algorithm 为 arpack 时使用,选择机器精度。当 algorithm 为 randomized 算法时自动忽略此设置。
转换主题时可先利用 TfidfVectorizer 将数据进行 TF-IDF 向量化,然后使用 TruncatedSVD 模型设置转换输出的主题类型数量,对主题的相关数据进行情感分析。
下面例子是从今日头条下载的资料,里面包含了财经、运动、娱乐、文化等多个主题。首先利用 jieba 进行分词,然后使用 TD-IDF 进行向量化处理,然后使用 TruncatedSVD 模型把 30000 多个分词进行主题化处理,转换成 10 个 components。
假设财经主题 finance 包含有 ['股份','银行','股票','投资','股市','黄金','市场','证券','科技','有限公司'] 等常用关键字,在 componets 中找到对应 finance 主题的关键字向量,对其进行情感分析。通过 svdVectorsDisplay()可分别显示金融主题最大正值内容和最小负值内容。
1 # 金融主题的关键字 2 finance=['股份','银行','股票','投资','股市','黄金','市场','证券','科技','有限公司'] 3 4 # 利用 jieba 转换命令格式 5 def getWords(): 6 file=open('C://Users/Leslie/Desktop/toutiao/news.txt','r',1024,'utf-8').read() 7 sentences=np.array(file.split('\n')) 8 # jieba 分词 9 list=[jieba.lcut(sentence) for sentence in sentences] 10 # 转换中文分词格式 11 words=[' '.join(word) for word in list] 12 return words 13 14 # 训练 TF-IDF 向量 15 def getTfidfVector(): 16 tfidf = TfidfVectorizer() 17 # 获取分词 18 words=getWords() 19 # 训练模型,返回 TF-IDF 向量 20 vector=tfidf.fit_transform(words) 21 return tfidf,vector 22 23 def svdComponent(): 24 # 获取 TF-IDF 向量 25 tfidf,vectors=getTfidfVector() 26 # 建立 SVD 模型 27 svd=TruncatedSVD(n_components=10,n_iter=10) 28 # 获取 TF-IDF 向量,训练 SVD 模型 29 svd=svd.fit(vectors) 30 svd_vectors=svd.transform(vectors) 31 # 显示主题 32 keys=tfidf.vocabulary_.keys() 33 # 获取相关主题的 components 向量 34 dataframeComponents=pd.DataFrame(svd.components_,columns=keys) 35 # 按照 component 5 进行相关性排序 36 dataframeVectors = pd.DataFrame(svd_vectors).sort_values(5, ascending=False).head(10) 37 svdComponentDisplay(dataframeComponents) 38 svdVectorsDisplay(dataframeVectors) 39 40 def svdComponentDisplay(dataframe): 41 # 获取与 finance 金融有关的关键词主题向量 42 topic = dataframe[finance] * 10000 43 # 显示与金融主题相关的SVD模型component主题 44 pd.options.display.max_columns = 10 45 # 打印主题向量及向量总值 46 print(topic) 47 print(topic.T.sum()) 48 49 def svdVectorsDisplay(dataframe): 50 print(dataframe) 51 words=getWords() 52 # 金融主题 finance 关键字相关性语句 53 for row in dataframe.iterrows(): 54 index = row[0] 55 print(words[index]) 56 57 if __name__=='__main__': 58 svdComponent()
运行结果
从运行结果可以看出,component 4 和 component 5 对金融主题的正向情感最高,尝试打印主题 5 正向情感前10个最大值的内容
component 7 负向情感主题最高,打印主题 7 负向情感前 10 个最小值的内容,可见内容大部分是娱乐主题内容,与金融主题无关。
类似地也可以用娱乐关键字 entertainment ['电影', '范冰冰','电影节','活动','复仇者','粉丝','娱乐圈','明星'] 作为主题调用 svdComponentDisplay()方法进行数据筛选
进行主题转换后查看 component 0 的娱乐正向情感信息,可见内容基本上都是关于娱乐信息
类似地查看 component 2 娱乐负向情感数量,大部分都是关于金融类的信息
5.4 LDA 线性判别分析
线性判别分析(Linear Discriminant Analysis,简称 LDA)是一种经典的数据主题分析方法,它与 LSA 最大区别在于 LDA 属于监督学习模型,而 LSA 是无监督学习模型。LDA 的主要思想是将一个高维空间中的数据投影到一个较低维的空间中,且投影后要保证各个类别的类内方差小而类间均值差别大,这意味着同一类的高维数据投影到低维空间后相同类别会尽量聚在一起,而不同类别之间相距较远。LDA 模型与 PCA 模型有点类似,然而最大区别在于:PCA方法寻找的是数据变化的主轴方向,从而根据主轴判别分析寻找的是用来有效分类的方向。这对样本数据的主要变化信息非常有效,然而却忽略了次要变化的信息。
而 LDA 模型是将高维样本数据投影到低维度的向量空间,根据投影后的向量进行分类判断。投影后希望每一种类别数据的投影点尽可能的接近,而不同类别的数据的类别中心之间的距离尽可能的大。
下图就是将二维数据投影到一维直线上,里面显示出 PCA 与 LDA 投影的区别: 5.5 LinearDiscriminantAnalysis 模型 在 sklearn 库中提供了 sklearn.discriminant_analysis.LinearDiscriminantAnalysis 模型进行 LDA 线性分析。
1 class LinearDiscriminantAnalysis(LinearClassifierMixin, 2 TransformerMixin, 3 BaseEstimator): 4 def __init__(self, solver='svd', shrinkage=None, priors=None, 5 n_components=None, store_covariance=False, tol=1e-4, 6 covariance_estimator=None):
参数说明
- solver : str 类型 ['svd','lsqr',‘eigen’ ] 之一,默认为 ‘svd' ,选择LDA超平面特征矩阵使用的方法。可以选择的方法有奇异值分解"svd",最小二乘"lsqr"和特征分解"eigen"。一般来说特征数非常多的时候推荐使用svd,而特征数不多的时候推荐使用eigen。如果使用 svd,则不能指定正则化参数shrinkage进行正则化。
- shrinkage:float 类型,或 [ 'auto',' None'] 正则化参数,默认为 None 。可以增强LDA分类的泛化能力,如果仅仅只是为了降维,则一般可以忽略这个参数。"auto" 代表让算法自己决定是否正则化。也可在 [0,1] 之间的值进行交叉验证调参。该参数只在 solver 为"lsqr"和 "eigen" 时有效, 'svd' 时自动作废。
- priors :array 数组类型,默认为None。例如 [ n_class , ] ,用于定义类别权重,可以在做分类模型时指定不同类别的权重,进而影响分类模型建立。降维时一般不需要关注这个参数。
- n_components:int 类型,默认为 None ,即我们进行LDA降维时降到的维数。需要值必须小于输入数据的维度减一。
- store_covariance:bool 类型,默认为 False,是否额外计算每个类别的协方差矩阵。
- tol:float 类型,默认为 1e-4,用它指定了用于SVD算法中评判迭代收敛的阈值。
- warm_start:bool 类型,默认值为 False,当设置为True时,重用之前调用的解决方案作为初始化,否则,只需要删除前面的解决方案
- covariance_estimator:str 类型,[ 'covariance_estimator' 或 None ] 之一, 默认为None。如果不是 None,则使用 covariance_estimator 来估计协方差矩阵,而不是依赖于协方差估计器(具有潜在的收缩率)。对象应具有拟合方法和 covariance_ 属性,如 sklearn.covariance 中的估计器。如果为 None,则使用收缩率参数驱动估计值。
下面例子将使用 LinearDiscriminantAnalysis 模型对科技类文本和娱乐类文本进行分析,为了避免训练时间过长,所以只拿了 4000 条数据进行训练。把数据转化为 TF-IDF 向量后,使用 LDA 模型进行训练,只由只有 2 类,所以 n_components 设置为 1 即可。最后查看测试结果,准确率已经在 90% 以上。
1 # 科技、娱乐的两个文本路径 2 paths=['C://Users/Leslie/Desktop/toutiao/news_finance.txt', 3 'C://Users/Leslie/Desktop/toutiao/news_entertainment.txt'] 4 # 科技类信息标记为0,娱乐类信息标记为1 5 result = [] 6 7 # 利用 jieba 转换命令格式 8 def getWords(): 9 data = [] 10 type = 0 11 # 获取路径中的两类文件 12 for path in paths: 13 file=open(path,'r',1024,'utf-8').read() 14 #分行读取,由于运行时间较长,所以只拿前2000条数据 15 sentences=np.array(file.split('\n'))[:2000] 16 # jieba 分词,记录分类结果 17 for sentence in sentences: 18 data.append(jieba.lcut(sentence)) 19 result.append(type) 20 type+=1 21 # 转换中文分词格式 22 words=[' '.join(word) for word in data] 23 return words 24 25 # 训练 TF-IDF 向量 26 def getTfidfVector(): 27 tfidf = TfidfVectorizer() 28 # 获取分词 29 words=getWords() 30 # 训练模型,返回 TF-IDF 向量 31 vector=tfidf.fit_transform(words) 32 return vector.toarray() 33 34 def ldaTest(): 35 # 把 TF-IDF 向量切分为训练数据与测试数据 36 X_train,X_test,y_train,y_test=train_test_split(getTfidfVector(),result,random_state=22) 37 # 由于只是二分类,n_components 为 1 即可 38 lda=LDA(n_components=1) 39 # 训练模型 40 lda.fit(X_train,y_train) 41 # 输出准确率 42 y_model=lda.predict(X_test) 43 print('准确率为:{0}'.format(accuracy_score(y_test,y_model))) 44 45 if __name__=='__main__': 46 ldaTest()
运行结果
5.6 LDiA 隐性狄利克雷分布
隐性狄利克雷分布 ( Latent Dirichlet Allocation,简称 LDiA)与 LSA 类似也是一种无监督学习模型,但与相对于 LSA 的线性模型不同的是 LDiA 可以将文档集中每篇文档的主题按照概率分布的形式给出,从而更精确地统计出词与主题的关系。
LDiA 假设每篇文章都是由若干个主题线性混合而成的,每个主题都是由若干个分词组合而成,文章中每个主题的概率与权重以及每个分词被分配到主题的概率都满足 “ 狄利克雷概率 ” 的分布特征,估计这个也算法命名的原因。其计算公式如下:
当中 B (α)为
5.7 LatentDirichletAllocation 模型
在 sklearn 库中提供了 sklearn.decomposition.LatentDirichletAllocation 模型进行 LDiA 分析
1 class LatentDirichletAllocation(TransformerMixin, BaseEstimator): 2 @_deprecate_positional_args 3 def __init__(self, n_components=10, *, doc_topic_prior=None, 4 topic_word_prior=None, learning_method='batch', 5 learning_decay=.7, learning_offset=10., max_iter=10, 6 batch_size=128, evaluate_every=-1, total_samples=1e6, 7 perp_tol=1e-1, mean_change_tol=1e-3, max_doc_update_iter=100, 8 n_jobs=None, verbose=0, random_state=None):
参数说明
- n_components:int 类型,默认为 10 ,即我们进行LDA降维时降到的维数。需要值必须小于输入数据的维度减一。
- doc_topic_prior: float 类型,默认为None, 即狄利克雷概率计算公式中的 θ 参数。 如果为 None 即 θ 默认为 1/ n_components。
- topic_word_prior: float 类型,默认为None, 狄利克雷概率计算公式中的 α 参数 。如果为 None 即 α 默认为 1/ n_components。
- learning_method: str 类型 {'batch', 'online'}之一,默认为 'batch',代表用于更新_component 的方法。如果数据量非常大时 ’online' 会比 ‘batch’ 运行更快。
- learning_decay:float 类型,默认值为 0.7 ,控制学习时的速率。仅在 learning 为 "online"时有效,取值一般在 [ 0.5, 1.0] 之间
- learning_offset:float 类型,默认值为10.0,用于降低学习早期迭代的权重。仅在 learning 为 "online"时有效,取值要大于1。
- max_iter:int 类型,默认值为 10,部分求解器需要通过迭代实现,这个参数指定了模型优化的最大迭代次数。
- batch_size: int 类型,默认为128,EM 迭代算法时每次选择的文本数,仅在 learning 为 "online" 时有效
- evaluate_every: int 类型,默认为-1。影响 fit 方法的运行,为 0 或负数时不会对训练数据的模型指标。它可能帮助改善数据的收敛性,但也会影响训练的效率,或许会延长训练时间
- total_samples : int 类型, 默认为 1e6,输入的文档总数,只在方法 partial_fit() 中有效
- perp_tol:float 类型,默认为 1e-1, 指批量学习中的容忍度,仅在 evaluate_every 大于0时有效。
- mean_change_tol: float 类型,默认为 1e-3 , 即E步更新变分参数的阈值,所有变分参数更新小于阈值则E步结束,转入M步
- max_doc_update_iter: int 类型,默认为 100,即E步更新变分参数的最大迭代次数,如果E步迭代次数达到阈值,则转入M步。
- n_jobs:默认为 None,CPU 并行数。若设置为 -1 的时候,则用所有 CPU 的内核运行程序。
- verbose:日志冗长度,int类型。默认为0。就是不输出训练过程,1的时候偶尔输出结果,大于1,对于每个子模型都输出。
- random_state:随机数种子,推荐设置一个任意整数,同一个随机值,模型可以复现。
LDiA 与 LSA 相似属于无监督学习模型,常被用于情感分析与垃圾过滤等领域。下面例子将结合 LDiA 与 LDA 模型的特点,先将信息进行主题转换,把 4000 个短信转换成200个主题,再进行信息分类。
还是以上面的科技类文本和娱乐类文本作为例子,先进行 TF-IDF 向量转换,再经过 LDiA 主题转换,最后使用 LDA 进行训练测试。
1 # 科技、娱乐的两个文本路径 2 paths=['C://Users/Leslie/Desktop/toutiao/news_finance.txt', 3 'C://Users/Leslie/Desktop/toutiao/news_entertainment.txt'] 4 # 科技类信息标记为0,娱乐类信息标记为1 5 result = [] 6 7 # 利用 jieba 转换命令格式 8 def getWords(): 9 data = [] 10 type = 0 11 # 获取路径中的两类文件 12 for path in paths: 13 file=open(path,'r',1024,'utf-8').read() 14 #分行读取,为了避免训练时间过长,只获取 4000 行数据 15 sentences=np.array(file.split('\n'))[2000:4000] 16 # jieba 分词,记录分类结果 17 for sentence in sentences: 18 data.append(jieba.lcut(sentence)) 19 result.append(type) 20 type+=1 21 # 转换中文分词格式 22 words=[' '.join(word) for word in data] 23 return words 24 25 # 训练 TF-IDF 向量 26 def getLdiaVector(): 27 tfidf = TfidfVectorizer() 28 # 获取分词 29 words=getWords() 30 # 训练模型,返回 TF-IDF 向量 31 vector=tfidf.fit_transform(words) 32 # 训练 LDiA 模型,转换为 200个主题 33 ldia=LDiA(n_components=200,doc_topic_prior=2e-3,topic_word_prior=1e-3,random_state=42) 34 return ldia.fit_transform(vector) 35 36 def ldaTest(): 37 # 把 TF-IDF 向量切分为训练数据与测试数据 38 X_train,X_test,y_train,y_test=train_test_split(getLdiaVector(),result,random_state=22) 39 # 由于只是二分类,n_components 为 1 即可 40 lda=LDA(n_components=1) 41 # 训练模型 42 lda.fit(X_train,y_train) 43 # 输出准确率 44 y_model=lda.predict(X_test) 45 print('准确率为:{0}'.format(accuracy_score(y_test,y_model))) 46 47 if __name__=='__main__': 48 ldaTest()
运行结果
虽然准确率只有 87%,远远不如直接使用 LDA 模型,但 LDiA 模型依然可以帮助用户从一个小型的训练集中泛化出模型,处理不同词的组合。
回到目录
https://dumps.wikimedia.org/zhwiki/latest/ 网上可以找到最新的中文语料库,可以根据需求下载。由于下载的是 *.bz2 的压缩文件,而包含简体/繁体多种字型。所以读取时首先利用 WikiCorpu 要对文件进行解压,由于中文单词与国外有所区别,所以完成解压后,需要利用 jieba 进行分词处理,处理期间可通过 zhconv 把繁体字转换成简体字,完成转换后保存数据。
格式转换后可开始对 Word2Vec 模型进行预训练,由于数据量通常比较大,建议完成预训练后使用 Word2Vec.save (path) 方法保存模型,方便下次直接使用 Word2Vec.load(path) 重新加载。路径最好通过 os.path 生成,直接写入绝对路径容易报错。
1 # 定议下载后压缩文件的路径,解压转换为简体的新文本路径 2 wikipath = 'E://Tools/words/word2vec/zhwiki-latest-pages-articles.xml.bz2' 3 filepath = 'E://Tools/words/word2vec/wiki.simple.txt' 4 modelpath = 'E://Python_Projects/ANN/venv/word2ver_wiki_cn.model' 5 6 if __name__=='__main__': 7 convert() 8 saveModel() 9 10 def saveModel(): 11 # 通过 os.path 获取路径避免引起LineSentence路径错误 12 sentencesPath = os.path.abspath(filepath) 13 modelPath = os.path.abspath(modelpath) 14 # 生成逐行读取对象 LineSentence 15 sentences = LineSentence(sentencesPath) 16 # 建议 word2vec 对象进行学习 17 model = Word2Vec(sentences, window=8, min_count=5, workers=10) 18 # 保存模型 19 model.save(modelPath) 20 21 def convert(): 22 # 定义写入文件对象 23 write=open(filepath,'w',10240,'utf-8') 24 # 读取 bz2 压缩文件 25 wiki = WikiCorpus(wikipath) 26 # 分行读取 27 for sentences in wiki.get_texts(): 28 data='' 29 # 分句读取 30 for sentence in sentences: 31 # 把繁体字转换为简体字 32 simpleSentence=zhconv.convert(sentence,'zh-cn') 33 # 通过 jieba 进行分词 34 for word in jieba.lcut(simpleSentence): 35 data+=word+' ' 36 # 换行 37 data+='\n' 38 # 写入文件 39 write.write(data)
训练 Word2vec 有两种方法 Skip-gram 方法和 CBOW(continuous bag-of-words)连续词袋,可以通过 sg 参数选择算法,0 为 skip-gram, 1 为 CBOW,默认使用 skip- gram
Skip-gram 算法是通过输入单词预测周边的词
CBOW 算法则是基于邻近的词预测目标词
6.2.2 KeyedVectors 词向量的常用方法
完成 Word2Vec 模型的预训练后,可以通过 Word2Vec.load(path) 重新加载训练好的模型, 通过 Word2Vec.wv 可获取训练后的词向量对象 KeyedVectors。
KeyedVectors 词向量对象有下面几种常用的方法
6.2.2.1 获取 keyedVectors 向量值
通过 wv.vectors 可以获取模型的全局向量,通过 wv [ '向量名‘ ] 可以获取对应的向量,由于 vector_size 默认维度是100,所以每个向量也是一个 1 * 100 的数组 。
1 modelPath=os.path.abspath('E://Python_Projects/ANN/venv/word2ver_wiki_cn.model') 2 model=Word2Vec.load(modelPath) 3 print( model.wv[ '朱元璋' ])
运行结果
若要进行组合向量查询,可直接通过向量叠加完成,例如若要查询 “唐朝的名诗及其作者” 等可以通过下面的等式完成
vector= wv['唐朝']+wv['诗人‘]+wv[’作品']
若发现结果中不仅包含有诗人和作品,还有其他朝代等信息,即可通过减法排除
vector= wv['唐朝']+wv['诗人‘]+wv[’作品']-wv['朝代']
6.2.2.2 向量相邻词 most_similar
方法 wv. most_similar( positive=None, negative=None, topn=10)可根据给定的向量查询其相邻的词,其中 positive 是代表捕捉相关的向量词组合,topn是默认返回前10个词,negative 是代表要排除的向量词。从例子中可以看,以 “唐朝” 查询到的大多都是不同的朝代。但排除 “朝代”关系后,显示的变成唐朝人物 “罗弘信” 等,地区 “魏州” 等信息。选择了“唐朝”和 ”皇帝“ 再排除 “朝代” 信息后,还有会有 “唐玄宗”,“唐高宗”,“武则天” 等皇帝信息。
1 modelPath=os.path.abspath('E://Python_Projects/ANN/venv/word2ver_wiki_cn.model') 2 model=Word2Vec.load(modelPath) 3 4 list0=model.wv.most_similar(positive=['唐朝'],topn=10) 5 print(list0) 6 list1=model.wv.most_similar(positive=['唐朝'],negative=['朝代'],topn=10) 7 print(list1) 8 list2=model.wv.most_similar(positive=['唐朝','皇帝'],topn=10) 9 print(list2)
运行结果
6.2.2.3 检测不相关词 doesnt_match
通过 wv.doesnt_match(words) 可监测多个词组合中不相关的词,例如通过 wv.doesnt_match(['唐朝','李世民','诗词','计算机']),系统会测试出 计算机
6.2.2.4 余弦相似度计算 most_similar
通过 wv.most_similarity(w1,w2) 可以计算出给定两个词之间的余弦相似充,例如通过 wv.similarity('唐太宗','李世民'),计算出的相邻度为 0.78733486
6.2.2.5 词频查询 expandos
在 gensim 4.0 以上版本,系统已用 wv.expandos 代替 wv. vocab,可以通过此属性可查询每个分词的数量等信息
6.2.3 Word2Vec 在 Embedding 层的应用
在模型的 Embedding 层中,可以使用预训练的 word2vec 使用提升模型的准确性。通过 https://www.cluebenchmarks.com/ 网站下载分类测试数据,里面有 16 大类的今日头条APP里面的文档。以 paths 数组记录不同类型的文本路径,利用 jieba 进行分词处理,然后调用 getWord()把分词进行编码处理,处理后的分词及编码记录在 dict 全局变量 words 中。再通过 getEmbedding() 方法,把预训练后的分量加入入 Embedding 层。把该层的 trainable 设置为 False ,让训练时数据不会影响 word2vec 的向量值。最后进行模型测试,可以看到简单的三层模型测试数据准确率可达到 80% 以上,已经相当不错了。
1 # 科技、娱乐、文学等16类文本路径 2 paths=['C://Users/Leslie/Desktop/toutiao/news_finance.txt', 3 'C://Users/Leslie/Desktop/toutiao/news_entertainment.txt', 4 'C://Users/Leslie/Desktop/toutiao/news_culture.txt', 5 ..........] 6 # 记录类型标记 0,1,2,3... 7 result = [] 8 # 记录分词与其对应编码 9 words = {} 10 # 定义 maxlen 超过100个截取 11 maxlen=100 12 # 定义向量主题数 13 vector_size=100 14 15 # 利用 jieba 转换命令格式 16 def getWords(): 17 data = [] 18 type = 0 19 # 获取路径中的16类文件 20 for path in paths: 21 file=open(path,'r',1024,'utf-8').read() 22 #分行读取 23 sentences=np.array(file.split('\n'))[:5000] 24 # jieba 分词,记录分类结果 25 for sentence in sentences: 26 data.append(jieba.lcut(sentence)) 27 result.append(type) 28 type+=1 29 return data 30 31 # 自编码 32 def getEncode(): 33 # 获取所有分词 34 sentences=getWords() 35 encode=list() 36 index=0 37 # 循环所有句子 38 for sentence in sentences: 39 array = [] 40 # 把分词转换成编码 41 for key in sentence: 42 if key not in words: 43 words[key]=index 44 index+=1 45 array.append(words[key]) 46 # 记录每个句子的编码 47 encode.append(array) 48 # 返回自编码 49 return encode 50 51 # 获取word2vec中对应的向量生成 Embedding 数组 52 def getEmbedding(): 53 # 加载训练好的 word2vec 模型 54 modelPath=os.path.abspath('E://Python_Projects/ANN/venv/word2ver_wiki_cn.model') 55 model=Word2Vec.load(modelPath) 56 # 向量初始化 57 embedding=np.zeros((len(words), vector_size)) 58 # 若word2vec有此分词则加载此向量,若没有则设置为0 59 for key,value in words.items(): 60 if model.wv.__contains__(key): 61 embedding[value]=model.wv.get_vector(key) 62 return embedding 63 64 # 生成Model 65 def getModel(): 66 model=Sequential() 67 model.add(Embedding(len(words),vector_size,input_length=maxlen)) 68 model.add(Flatten()) 69 model.add(Dense(500,activation='relu')) 70 model.add(Dense(100,activation='relu')) 71 model.add(Dense(16,activation='sigmoid')) 72 model.compile(optimizer=optimizers.Adam(0.003), 73 loss=losses.sparse_categorical_crossentropy, 74 metrics=['acc']) 75 model.summary() 76 return model 77 78 def test(): 79 # 获取句子的分词编码 80 encodes=getEncode() 81 # 获取模型 82 model=getModel() 83 # 在 Embedding 层加入预训练好的 word2vec 模型 84 model.layers[0].set_weights([getEmbedding()]) 85 # 训练时不修改 word2vec 模型中的向量 86 model.layers[0].trainable=False 87 # 分拆训练数据与测试数据 88 X_train,X_test,y_train,y_test=train_test_split(encodes,np.array(result),random_state=60) 89 X_train=preprocessing.sequence.pad_sequences(X_train,maxlen=maxlen) 90 X_test=preprocessing.sequence.pad_sequences(X_test,maxlen=maxlen) 91 # 输出准确率 92 callback= keras.callbacks.TensorBoard(log_dir='logs') 93 history=model.fit(X_train,y_train,epochs=20,batch_size=500,callbacks=callback) 94 print(history) 95 model.fit(X_test,y_test) 96 97 if __name__=='__main__': 98 test()
运行结果
Tensorborad 准确率
6.3 GloVe 词嵌入
除了 Word2Vec 库,另一个常用的库就是 GloVe(Global Vectors for Word Representation),它是由斯坦福大学研究人员在 2014 年开发的。这种嵌入方法基于全词频统计的方式对分词进行了全局矩阵因式分解,它可以直接把单词表达成实数向量,这些向量捕捉到了单词之间一些语义特性,比如相似性(similarity)、类比性(analogy)等。通过对向量的运算,比如欧几里得距离或者cosine相似度,可以计算出两个单词之间的语义相似性。
GloVe 库的应用与 Word2Vec 类似,可以直接通过网络下载语料库,对 GloVe 进行预训练,然后保存模型,再把从 Embedding 层注入预训练好的向量,对数量进行测试。若要在 window 10 或以上版本中使用 Glove,建议使用 glove_python 包,首先必须先安装 GCC (可通过 Homebrew 下载对应版本)和 Visual Studio Build Tool 14 以上版本,然后通过 pip install glove_python 执行安装(也可链接 GitHub:glove_python-0.1.0-cp37-cp37m-win_amd64.zip 直接下载安装包)。
GloVe 常用方法如下
1 #准备数据集 2 sentense = [['摘要','人工智能','AI','开发'],['我们','是','机器人'],......] 3 corpus_model = Corpus() 4 corpus_model.fit(sentense, window=10) 5 #训练 6 glove = Glove(no_components=100, learning_rate=0.05) 7 glove.fit(corpus_model.matrix, epochs=10, 8 no_threads=1, verbose=True) 9 glove.add_dictionary(corpus_model.dictionary) 10 #模型保存 11 glove.save('glove.model') 12 glove = Glove.load('glove.model') 13 #语料保存 14 corpus_model.save('corpus.model') 15 corpus_model = Corpus.load('corpus.model')
完成训练后,使用 Glove.load() 就可以重新加载,然后把向量加载到 Embedding 层,使用方式与 Word2Vec 非常类似,在此就不再做重复介绍。
利用 gensim.scripts.glove2word2vec 还可以把 glove 向量转化为 Word2Vec 向量使用
1 # 用于转换并加载glove预训练词向量 2 from gensim.test.utils import datapath, get_tmpfile 3 from gensim.models import KeyedVectors 4 # 将glove转换为word2vec 5 from gensim.scripts.glove2word2vec import glove2word2vec 6 7 path='文件夹路径' 8 glove_file=datapath(os.path.join(path, "glove.txt")) 9 word2vec_file=get_tmpfile(os.path.join(path,"word2vec.txt")) 10 glove2word2vec(glove_file, word2vec_file)
回到目录
本章总结
本文介绍了 NLP 自然语言处理的理论与实现方法,以 One-Hot、TF-IDF、PageRank 为基础的算法,讲述 LDA、LDiA、LSA 等语义分析的原理。介绍 Jieba 分词工具的中文文本中的应用,以及Word2Vec、GloVe 等预训练模型。
其实本文讲述的 NLP 在 Embedding 中的应用只是冰山一角,自然语言处理在循环神经网络 RNN 中才能真正发挥其优势。在下篇文章 《 TensorFlow 2.0 深度学习实战 —— 循环神经网络 RNN 》 会从实用的角度更深入地介绍 NLP 的应用场景,敬请留意。
希望本篇文章对相关的开发人员有所帮助,由于时间仓促,错漏之处敬请点评。
对 .Python 开发有兴趣的朋友欢迎加入QQ群:790518786 共同探讨 !
对 JAVA 开发有兴趣的朋友欢迎加入QQ群:174850571 共同探讨!
对 .NET 开发有兴趣的朋友欢迎加入QQ群:162338858 共同探讨 !
AI人功智能相关文章