自然语言处理(NaturalLanguageProcessing,NLP)是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法,用于分析理解机器与人之间的交互,常用的领域有:实体识别、文本纠错、情感分析、文本分类、关键词提取、自动摘要提取等方面。本文将从分词、词频、词向量等基础领域开始讲解自然语言处理的原理,讲解One-Hot、TF-IDF、PageRank等算法及LDA、LDiA、LSA等语义分析的原理。介绍Word2vec、GloVe、Embedding等常用词嵌入及NLTK、Jieba等分词工具的应用。
目录
一、自然语言处理的概念
1.1自然语言处理的起源
(1)机器翻译
机器翻译是利用计算机将某一种语言文本自动翻译成另一种语言文本的方法,它基于语言规则,利用统计的统计原理进度混合计算,得出最终结果。最常见于百度翻译、金山iciba翻译、有道翻译、google翻译等。
(2)自动问答
自动问答通过计算机对人提出的问题的理解,利用自动推理等手段,在有关知识资源中自动求解答案并做出相应的回答。它利用语词提取、关键字分析、摘要分析等方式提取问题的核心主干,然后利用NLP分析数据选择出最合适的答案。常见的例子有在线问答ask.com、百度知道、yahoo回答等。
(3)语音处理
语言处理(speechprocessing)可以把将输入语音信号自动转换成书面文字或计算机命令,然后对任务进行操作处理。常见的应用场景有汽车的语言识别、餐厅智能点餐、机场火车站的智能预订航班、智能机器人等。
(4)情感分析从大量文档中检索出用户的情感方向,对商品评价、服务评价等的满意进行分析,对用户进行商品服务推荐。在京东、淘宝等各大的购物平台很常用。
1.2自然语言处理的阶段
自然语言实现一般都通过以下几个阶段:文本读取、分词、清洗、标准化、特征提取、建模。首先通过文本、新闻信息、网络爬虫等渠道获取大量的文字信息。然后利用分词工具对文本进行处理,把语句分成若干个常用的单词、短语,由于各国的语言特征有所区别,所以NLP也会有不同的库支撑。对分好的词库进行筛选,排除掉无用的符号、停用词等。再对词库进行标准化处理,比如英文单词的大小写、过去式、进行式等都需要进行标准化转换。然后进行特征提取,利用tf-idf、word2vec等工具包把数据转换成词向量。最后建模,利用机器学习、深度学习等成熟框架进行计算。
下面将对这几个处理流程进行逐一介绍。
二、分词器的原理及应用
2.1分词器的基本原理
在自然语言处理的过程中,把切分文件是流水线的第一步,它能够把文本拆分为更小的文本块或词语片段多块信息,每块信息都可以被看成是一个元素,这此元素出现的频率可以直接被看作为文本的向量。最常见的方法就是使用split方法。
data='NLPstandsforNaturalLanguageProcessing.'
data.split()
结果['NLP','stands','for','Natural','Language','Processing.']
你可能已经看到,直接对语句进行拆分可以会把标点符号‘.’也带进数组。还有一些无用的操作符‘.?!’等,最后势必会影响输出的结果。想要实现这类最简单的数据清洗,可以使用正则表达式来解决。
data='NLPisthestudyofexcellentcommunication–bothwithyourself,andwithothers.'
data=re.split(r'[-\s.,!]',data)
当想去除一些无用的停用词(例如'a,A')、对词库进行标准化处理(例如词干还原,把进行式building转化成build,把过去式began转化为begin)还有大小写转换时,可以使用成熟的库来完成。
多国的语言都有差异,所以分词器的处理方式也有区别,下面将介绍英语单词与中文词汇比较常用的分词器NLTK和Jieba。
2.2NLTK库基础功能介绍
2.2.1分句SentencesSegment
例如有一段文本里面包含三个句子,希望把它分成一个一个的句子。此时可以使用NLTK中的punktsentencesegmenter。
1sent_tokenizer=nltk.data.load('tokenizers/punkt/english.pickle')2paragraph="ThefirsttimeIheardthatsongwasinHawaiionradio.”+3"Iwasjustakid,andloveditverymuch!Whatafantasticsong!"4sentences=sent_tokenizer.tokenize(paragraph)5print(sentences)运行结果
['ThefirsttimeIheardthatsongwasinHawaiionradio.','Iwasjustakid,andloveditverymuch!','Whatafantasticsong!']
2.2.2分词WordPunctTokenizer
1fromnltk.tokenizeimportWordPunctTokenizer2sentence="AreyouoldenoughtorememberMichaelJacksonattending”+3“theGrammyswithBrookeShieldsandWebstersatonhislapduringtheshow"4words=WordPunctTokenizer().tokenize(sentence)5print(words)运行结果
['Are','you','old','enough','to','remember','Michael','Jackson','attending','the','Grammys','with','Brooke','Shields','and','Webster','sat','on','his','lap','during','the','show','']
2.2.3正则表达式RegexpTokenizer
最简单的方法去掉一些从文档中存在的\n\t等符号
1fromnltk.tokenizeimportRegexpTokenizer2sentence='ThomasJeffersonbegan\nbuilding\tMonticelloattheageof26.'3tokenizer=RegexpTokenizer(r'\w+|$[0-9.]+|\S+')4print(tokenizer.tokenize(sentence))运行结果
['Thomas','Jefferson','began','building','Monticello','at','the','age','of','26','.']
2.2.4分词TreebankWordTokenizer
TreebankWordTokenizer拥有比WordPunctTokenizer更强大的分词功能,它可以把don't等缩写词分为["do","n't"]
1fromnltk.tokenizeimportTreebankWordTokenizer2sentence="Sorry!Idon'tknow."3tokenizer=TreebankWordTokenizer()4print(tokenizer.tokenize(sentence))运行结果
['Sorry','!','I','do',"n't",'know','.']
2.2.5词汇统一化
2.2.5.1转换大小写
词汇统一化最常用的就是把大小进行统一化处理,因为很多搜索工具包都会把大小写的词汇例如City和city视为不同的两个词,所以在处理词汇时需要进行大小写转换。当中最简单直接的方法就是直接使用str.lower()方法。
2.2.5.2词干还原
当单词中存在复数,过去式,进行式的时候,其词干其实是一样的,例如gave,giving词干都是give。相同的词干其实当中的意思是很接近的,通过词干还原可以压缩单词的数据,减少系统的消耗。NLTK中提供了3个常用的词干还原工具:porter、lancaster、snowball,其使用方法相类似,下面用porter作为例子。可以playingboysgrounded都被完美地还原,但对thistable等单词也会产生歧义,这是因为被原后的单词不一定合法的单词。
1fromnltk.stemimportporteraspt23words=['wolves','playing','boys','this','dog','the',4'beaches','grounded','envision','table','probably']5stemmer=pt.PorterStemmer()6forwordinwords:7pt_stem=stemmer.stem(word)8print(pt_stem)运行结果
2.2.5.3词形并归
想要对相同语义词根的不同拼写形式都做出统一回复的话,那么词形归并工具就很有用,它会减少必须要回复的词的数目,即语言模型的维度。例如可以good、goodness、better等都归属于同一处理方式。通过wordnet.lemmatize(word,pos)方法可指定词性,与常用的英语单词类似,n为名词v为动词a为形容词等等。指定词性后还可以通过posterStemmer.stem()还原词干。
1stemmer=PorterStemmer()2wordnet=WordNetLemmatizer()3word1=wordnet.lemmatize('boys',pos='n')4print(word1)56word2=wordnet.lemmatize('goodness',pos='a')7word2=stemmer.stem(word2)8print(word2)运行结果
2.2.6停用词
在词库中往往会存在部分停用词,使用nltk.corpus.stopwords可以找到NLTK中包含的停用词
1stopword=stopwords.raw('english').replace('\n','')2print(stopword)运行结果
通过对比可以对文件中的停用词进行过滤
1words=['the','playing','boys','this','dog','a',]2stopword=stopwords.raw('english').replace('\n','')3words=[wordforwordinwordsifwordnotinstopword]4print(words)运行结果
['playing','boys','dog']
2.2.3把词汇扩展到n-gram
上面例子中基本使用单个词作用分词的方式,然而很多短语例如:icecream,makesense,lookthrough等都会多个单词组合使用,相同的单词在不同的短语中会有不同的意思。因此,分词也需要有n-gram的能力。针对这个问题NTLK提供了ngrams函数,它可以按实现2-gram、3-gram等多类型的元素划分。
1sentence='Buildthewaythatworksbestforyou'+\2'withsupportforallyourgo-tointegrations'+\3'includingSlackJiraandmore.'4words=sentence.split()5print(list(ngrams(words,2)))运行结果
2.3Jieba库基础功能介绍
NLTK库有着强大的分词功能,然而它并不支中文,中文无论从语法、格式、结构上都有很大的差别,下面介绍一个常用的中文库Jieba的基础功能。
2.3.1分词jieba.cut
jieba.cut是最常用的分词方法,返回值为generator。jieba.lcut与jieba.cut类似,区别在于jieba.lcut直接返回list。在数据量比较大时,jieba.cut会更节省内存。
1defcut(self,sentence,cut_all=False,HMM=True,2use_paddle=False):使用例子
1sentence='嫦娥四号着陆器地形地貌相机对玉兔二号巡视器成像'2word1=jieba.cut(sentence,False)3print(list(word1))4word2=jieba.cut(sentence,True)5print(list(word2))运行结果
2.3.2搜索分词jieba.cut_for_search
jieba.cut_for_search与jieba.cut精确模式类似,只是在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词,返回值为generator。jieba.lcut_for_search与jieba.cut_for_search类似,但返回值为list。
1defcut_for_search(self,2sentence:Any,3HMM:bool=True)->Generator[str,Any,None]使用例子
1word1=jieba.cut_for_search('尼康Z7II是去年底全新升级的一款全画幅微单相机',False)2print(list(word1))3word2=jieba.cut_for_search('尼康Z7II是去年底全新升级的一款全画幅微单相机',True)4print(list(word2))运行结果
2.2.3载入新词jieba.load_userdict
通过此方法可以预先载入自定义的用词,令分词更精准。文本中一个词占一行,每一行分三部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。
例如:设定word.txt文本
阿里云1n云计算1n
1word1=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司')2print(list(word1))3jieba.load_userdict('C://Users/Leslie/Desktop/word.txt')4word2=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司')5print(list(word2))运行结果
2.2.4动态调节词典
通过jieba.add_word和jieba.del_word这两个方法也可以动态地调节词典
1defadd_word(self,word,freq=None,tag=None):jieba.add_word可以把自定义词加入词典,当中freq为词频,tag为词性。
1defdel_word(self,word):相反,通过jieba.del_word可以动态删除加载的自定义词
1word1=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司')2print(list(word1))3jieba.add_word('阿里云')4jieba.add_word('云计算')5word2=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司')6print(list(word2))7jieba.del_word('阿里云')8word3=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司')9print(list(word3))运行结果
2.2.5词节词频jieba.suggest_freq
此方法可调节单个词语的词频,使其能(或不能)被分出来。注意:自动计算的词频在使用HMM新词发现功能时可能无效。
1defsuggest_freq(self,segment,tune=False):下面的例子就是把“阿里云”这个词拆分的过程
word1=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司')print(list(word1))jieba.suggest_freq('阿里云',True)word2=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司',False,False)print(list(word2))jieba.suggest_freq(('阿里','云'),True)word3=jieba.cut('阿里云是全球领先的云计算及人工智能科技公司',False,False)print(list(word3))运行结果
2.2.6标注词性jieba.posseg
通过posseg.cut可以查看标注词性,除此以外还可以jieba.posseg.POSTokenizer新建自定义分词器
1words=jieba.posseg.cut('阿里云是全球领先的云计算及人工智能科技公司')2forword,flaginwords:3print(word,flag)运行结果
2.2.7使用jieba计算词频
三、词向量
完成分词的工作后,在进行运算前,先要对数据进行向量化,常用的词向量有one-hot独热向量、tf-idf向量和embedding词嵌入等,下面将进一步介绍。
3.1one-hot独热向量
one-hot独热向量是比较容易理解的一种词向量,它是把词汇表中的词的数量与词位置都进行记录,每个语句中所有信息都没有丢失,这也是one-hot的优点。下面的例子先将词句按照原顺序进行分词,分词完成后,[10000000]为第一个词“珠穆朗玛峰”,[01000000]为每二个词“上”,如此类推。然后把词组进行重排列作用测试数据['上','如此','星空','是','珠穆朗玛峰','的','的','迷人'],查看测试数据的one-hot向量。通过测试结果可以看过,one-hot对数据进行了全面记录,测试数据中每个词出现的顺序和次数都被完整地记录下来。
1defgetWords():2#对句子进行分词3sentence='珠穆朗玛峰上的星空是如此的迷人'4words=jieba.lcut(sentence)5print('【原始语句】:{0}'.format(sentence))6print('【分词数据】:{0}'.format(words))7returnwords89defgetTestWords():10#把词集进行重新排序后作为测试数据11words=getWords().copy()12words.sort()13print('【测试数据】:{0}'.format(words))14returnwords1516defone_hot_test():17#获取分词后数据集18words=getWords()19#获取测试数据集20testWords=getTestWords()21size=len(words)22onehot_vectors=np.zeros((size,size),int)23#获取测试数据one_hot向量24fori,wordinenumerate(testWords):25onehot_vectors[i,words.index(word)]=126returnonehot_vectors2728if__name__=='__main__':29print(one_hot_test())运行结果
3.2TF-IDF向量
3.2.1TF词频
3.2.2IDF逆文本频率
而IDF逆文本频率指数是代表词语在文档中的稀缺程度,它等总文件数目除以包含该词语之文件的数目,再将得到的商取以10为底的对数得到。例如有1000篇文件,其中有30篇包含了“向量”这个词,那IDF为log(1000/30),考虑到当文件不存这个词时分母会为0,所以默认情况下会为分母加1,即log(1000/30+1)。如此类推,下面是IDF的计算公式:
3.2.3TF-IDF计算
TF-IDF顾名思义就是代表TF与IDF的乘积
3.3TfidfVectorizer简介
上面例子通过python手动实现TF-IDF的计算,其实在sklearn中已有TfidfVectorizer类支持TF-IDF运算,它包含大量的常用方法,使计算起来变得特别简单,下面简单介绍一下。
1classTfidfVectorizer(CountVectorizer):2@_deprecate_positional_args3def__init__(self,*,input='content',encoding='utf-8',4decode_error='strict',strip_accents=None,lowercase=True,5preprocessor=None,tokenizer=None,analyzer='word',6stop_words=None,token_pattern=r"(u)\b\w\w+\b",7ngram_range=(1,1),max_df=1.0,min_df=1,8max_features=None,vocabulary=None,binary=False,9dtype=np.float64,norm='l2',use_idf=True,smooth_idf=True,10sublinear_tf=False):参数说明
常用方法
TfidfVectorizer模型可以直接通过fit_transform方法直接计算出TF-IDF向量,无需进行繁琐的公式运算。还可在建立模型时设置如停用词,n-gram,编码类型等多个常用的运算条件。
1corpus=[2'Inadditiontohavingarowcontext',3'Usuallyasmallertextfield',4'TheTFIDFideaheremightbecalculatingsomerarenessofwords',5'Thelargercontextmightbetheentiretextcolumn',6]78defstopWord():9#读取停用词10stopword=[line.strip()forlineinopen('C://Users/Leslie/Desktop/stopword.txt','r',1024,'utf8')11.readlines()]12returnstopword1314deftfidfVectorizerTest():15words=corpus16#建立tfidf模型17vertorizer=tfidfVectorizer(stop_words=stopWord(),ngram_range=(1,2))18#训练与运算19model=vertorizer.fit_transform(words)20#显示分词21print(vertorizer.vocabulary_)22#显示向量23print(model)2425if__name__=='__main__':26tfidfVectorizerTest()运行结果
TfidfVectorizer毕竟是针对外语单词格式所设计,所以用到中文时需要把句子转换成类似外语的格式。行利用jieba先进行分词,然后重新组合成句子,在每个分词后加上空格。
1corpus=[2'北京冬奥会完美落下帷幕',3'冬奥生态内容方面的表现给用户留下深刻印象',4'全平台创作者参与冬奥内容创作',5'此次冬奥会对于中国是一次重要的里程碑时刻',6]78defstopWord():9#读取停用词10stopword=[line.strip()forlineinopen('C://Users/Leslie/Desktop/stopword.txt','r',1024,'utf8')11.readlines()]12returnstopword1314defgetWord():15#转换集合格式后再进行分词16list=[jieba.lcut(sentence)forsentenceincorpus]17#在每个词中添加空格符18word=[''.join(word)forwordinlist]19returnword2021deftfidfVectorizerTest():22words=getWord()23#打印转换格式后的分词24print(str.replace('格式转换:{0}\n'.format(words),',','\n\t\t'))25#建立模型26vertorizer=tfidfVectorizer(stop_words=stopWord())27#模型训练28model=vertorizer.fit_transform(words)29print('分词:{0}\n'.format(vertorizer.vocabulary_))30print(model)3132if__name__=='__main__':33tfidfVectorizerTest()运行结果
3.4浅谈PageRank算法
除了TF-IDF算法,还有一种较为常用的PageRank算法。它是由Mihalcea与Tarau于提出,其思想与TF-IDF有所区别,它是通过词之间的相邻关系构建网络,然后用迭代计算每个节点的rank值,排序rank值即可得到关键词。公式如下,其中PR(Vi)表示结点Vi的rank值,In(Vi)表示结点Vi的前驱结点集合,Out(Vj)表示结点Vj的后继结点集合,d为dampingfactor用于做平滑,其原理在此暂不作详细讲解。
TextRank算法与TF-IDF算法均严重依赖于分词结果,如果某词在分词时被切分成了两个词,那么在做关键词提取时无法将两个词黏合在一起(TextRank有部分黏合效果,但需要这两个词均为关键词)。因此是否添加标注关键词进自定义词典,将会造成准确率、召回率大相径庭。TextRank虽然考虑到了词之间的关系,但是仍然倾向于将频繁词作为关键词,其效果并不一定优于TF-IDF。
四、文本相似度分析
4.1余弦相似度定义
完成分词后利用TF-IDF算法把分词成功转换成向量,便可以开始对向量进行计算,最常用的方法是余弦相似度计算。为了更好地理解,假设在二维空间有向量doc1(x1,y1)和向量doc2(x2,y2),可以简单地理为分词word1,word2在doc1中的词频为x1,y1,在doc2中的词频为x2,y2。
根据欧几里得点积公式
可推算出余弦相似度计算公式
4.2余弦相似度计算
在sklearn.metrics.pairwise中提供了余弦相似度计算的函数cosine_similarity和余弦距离计算函数cosine_distances可以通过简单的运算计算出余弦相信度
4.2.1cosine_similarity函数
1defcosine_similarity(X,Y=None,dense_output=True):当中X代表第一个对比值,Y代表第二个对比值,若Y为None时则会对X输入的数组作相似性对比。当dense_output为True则无论输入是否稀疏,都将返回密集输出。若dense_output为False时,如果两个输入数组都是稀疏的,则输出是稀疏的。
1x0=np.array([0.895,0.745]).reshape(1,-1)2y0=np.array([0.568,0.445]).reshape(1,-1)3similarity0=cosine_similarity(x0,y0)4print('similarity0is:\n{0}\n'.format(similarity0))56x1=np.arange(4).reshape(2,2)7similarity1=cosine_similarity(x1)8print('similarity1is:\n{0}\n'.format(similarity1))910x2=np.arange(10).reshape(2,5)11y2=np.arange(15).reshape(3,5)12similarity2=cosine_similarity(x2,y2)13print('similarity2is:\n{0}\n'.format(similarity2))运行结果
4.2.2cosine_distances函数
1defcosine_distances(X,Y=None):cosine_distances用法与cosine_similarity类似,只是cosine_distances返回的是余弦的距离,余弦相似度越大,余弦距离越小
1x0=np.array([0.895,0.745]).reshape(1,-1)2y0=np.array([0.568,0.445]).reshape(1,-1)3distances0=cosine_distances(x0,y0)4print('distances0is:\n{0}\n'.format(distances0))56x1=np.arange(4).reshape(2,2)7distances1=cosine_distances(x1)8print('distances1is:\n{0}\n'.format(distances1))910x2=np.arange(10).reshape(2,5)11y2=np.arange(15).reshape(3,5)12distances2=cosine_distances(x2,y2)13print('distances2is:\n{0}\n'.format(distances2))运行结果
4.3文本相似度计算
五、通过主题转换进行语义分析
5.1LSA隐性语义分析的定义
5.2SVD奇异值分解原理
其中向量U是分词-主题矩阵,它给出分词所具有的上下文信息,代表分词与主题的相互关系,也被称为“左奇异向量”。
向量S是主题奇异值的对象线方阵,例如有6个主题的文档库S值就会是一个6*6的矩阵。
向量V是主题-文档矩阵,它建立了新主题与文档之间的关系,也被称为“右奇异向量”。
5.3TruncatedSVD模型
在sk-learn库中提供了sklearn.decomposition.TruncatedSVD模型用于进行SVD分析。SVD是无监督模型,通过SVD可以把多维的数量进行主题转换实现降维,常被用于情感分析和垃圾信息处理。
1classTruncatedSVD(TransformerMixin,BaseEstimator):2@_deprecate_positional_args3def__init__(self,n_components=2,*,algorithm="randomized",n_iter=5,4random_state=None,tol=0.):5self.algorithm=algorithm6self.n_components=n_components7self.n_iter=n_iter8self.random_state=random_state9self.tol=tol参数说明
从运行结果可以看出,component4和component5对金融主题的正向情感最高,尝试打印主题5正向情感前10个最大值的内容
component7负向情感主题最高,打印主题7负向情感前10个最小值的内容,可见内容大部分是娱乐主题内容,与金融主题无关。
进行主题转换后查看component0的娱乐正向情感信息,可见内容基本上都是关于娱乐信息
类似地查看component2娱乐负向情感数量,大部分都是关于金融类的信息
5.4LDA线性判别分析
5.5LinearDiscriminantAnalysis模型
1classLinearDiscriminantAnalysis(LinearClassifierMixin,2TransformerMixin,3BaseEstimator):4def__init__(self,solver='svd',shrinkage=None,priors=None,5n_components=None,store_covariance=False,tol=1e-4,6covariance_estimator=None):参数说明
5.6LDiA隐性狄利克雷分布
当中B(α)为
5.7LatentDirichletAllocation模型
在sklearn库中提供了sklearn.decomposition.LatentDirichletAllocation模型进行LDiA分析
1classLatentDirichletAllocation(TransformerMixin,BaseEstimator):2@_deprecate_positional_args3def__init__(self,n_components=10,*,doc_topic_prior=None,4topic_word_prior=None,learning_method='batch',5learning_decay=.7,learning_offset=10.,max_iter=10,6batch_size=128,evaluate_every=-1,total_samples=1e6,7perp_tol=1e-1,mean_change_tol=1e-3,max_doc_update_iter=100,8n_jobs=None,verbose=0,random_state=None):参数说明
LDiA与LSA相似属于无监督学习模型,常被用于情感分析与垃圾过滤等领域。下面例子将结合LDiA与LDA模型的特点,先将信息进行主题转换,把4000个短信转换成200个主题,再进行信息分类。还是以上面的科技类文本和娱乐类文本作为例子,先进行TF-IDF向量转换,再经过LDiA主题转换,最后使用LDA进行训练测试。
虽然准确率只有87%,远远不如直接使用LDA模型,但LDiA模型依然可以帮助用户从一个小型的训练集中泛化出模型,处理不同词的组合。
六、词嵌入的应用
6.1词嵌入原理
在机器学习中会利用TF-IDF等向量进行计算,而在Tensorflow中常用词嵌入的方式进行计算。获取词嵌入的方式有两种,一种是通过词向量进行模型训练学习得来。另一种通过预训练模型把预先计算好词嵌入,然后将其加入模型中,也称为预训练词嵌入。常用的预训练词嵌入有Word2doc、GloVe、Doc2vec等。
Tensorflow中准备Embedding层进行词嵌入,相比起传统的one-hot编码,它提供了低维度高密集型的词向量,其主要参数如下,其中最常用到的是input_dim,output_dim,input_length这3个参数,input_dim是代表最大可插入的分词个数据,output_dim是代表对分词特征分析的维度,这个参数需要根据分词数量而定,input_length是限制单个测试对象的最大分词数量,若单个测试对象超出此单词数系统将会自动截取。
1@keras_export('keras.layers.Embedding')2classEmbedding(Layer):3def__init__(self,4input_dim,5output_dim,6embeddings_initializer='uniform',7embeddings_regularizer=None,8activity_regularizer=None,9embeddings_constraint=None,10mask_zero=False,11input_length=None,12**kwargs):参数说明:
要使用Embedding层首先要对数据进行一下转换,例如上面例子的中文分词原来为下面格式
现在需要把文本转换为编码的格式,然后才能作为Embedding的输入数据
下面的例子继续使用科技、娱乐两类文档作为测试数据。先利用jieba作分词处理,然后调用getEncode方法进行自编码,把中文单词字符串转换成数字编码,再建立Model使用Embedding嵌入词进行测试。注意通过词嵌入后需要进行Flatten拉直后再进行计算。
在此例子中使用了10000个单词,由于是短文本,所以把input_length设置为10个单词,而且只通过一个Dense层,准确率已经达到90%以上。
6.2Word2vec原理与应用
Word2vec同Google的TomasMikolov于2013年研发,它是通过无监督学习训练而成,因此训练数据不需要人工组织、结构化和标注,这对于NLP来说可以说是非常完美。Word2vec与其他词向量相似,以向量来衡量词语之间的相似性以及相邻的词汇是否相识,这是基于语言学的“距离相似性”原理。“距离相似性”可以用词向量的几何关系可以代表这些词的关系,用两个词之前的距离长短来衡量词之间的关系。如果把词向量的多维关系转化为二维映射或者会更容易理解,如下图dog、cat、rabbit的相对更为接近,所以被是认为有一定的关系。
6.2.1Word2vec模型
在gensim库中,包含了最常用的gensim.models.word2vec.Word2Vec模型。在使用Word2vec模型前,首先要对模型进行预训练,由于各国有不同的文化差异,所以需要准备不同的语料库,词料库信息越全面,训练出来的模型准确性就会越高。
1classWord2Vec(utils.SaveLoad):2def__init__(3self,sentences=None,corpus_file=None,vector_size=100,alpha=0.025,window=5,min_count=5,4max_vocab_size=None,sample=1e-3,seed=1,workers=3,min_alpha=0.0001,5sg=0,hs=0,negative=5,ns_exponent=0.75,cbow_mean=1,hashfxn=hash,epochs=5,null_word=0,6trim_rule=None,sorted_vocab=1,batch_words=MAX_WORDS_IN_BATCH,7compute_loss=False,callbacks=(),comment=None,max_final_vocab=None,shrink_windows=True,8)参数说明
Skip-gram算法是通过输入单词预测周边的词
CBOW算法则是基于邻近的词预测目标词
6.2.2KeyedVectors词向量的常用方法
完成Word2Vec模型的预训练后,可以通过Word2Vec.load(path)重新加载训练好的模型,通过Word2Vec.wv可获取训练后的词向量对象KeyedVectors。KeyedVectors词向量对象有下面几种常用的方法
6.2.2.1获取keyedVectors向量值
通过wv.vectors可以获取模型的全局向量,通过wv['向量名‘]可以获取对应的向量,由于vector_size默认维度是100,所以每个向量也是一个1*100的数组。
1modelPath=os.path.abspath('E://Python_Projects/ANN/venv/word2ver_wiki_cn.model')2model=Word2Vec.load(modelPath)3print(model.wv['朱元璋'])运行结果
若要进行组合向量查询,可直接通过向量叠加完成,例如若要查询“唐朝的名诗及其作者”等可以通过下面的等式完成
vector=wv['唐朝']+wv['诗人‘]+wv[’作品']
若发现结果中不仅包含有诗人和作品,还有其他朝代等信息,即可通过减法排除
vector=wv['唐朝']+wv['诗人‘]+wv[’作品']-wv['朝代']
6.2.2.2向量相邻词most_similar
1modelPath=os.path.abspath('E://Python_Projects/ANN/venv/word2ver_wiki_cn.model')2model=Word2Vec.load(modelPath)34list0=model.wv.most_similar(positive=['唐朝'],topn=10)5print(list0)6list1=model.wv.most_similar(positive=['唐朝'],negative=['朝代'],topn=10)7print(list1)8list2=model.wv.most_similar(positive=['唐朝','皇帝'],topn=10)9print(list2)运行结果
6.2.2.4余弦相似度计算most_similar
通过wv.most_similarity(w1,w2)可以计算出给定两个词之间的余弦相似充,例如通过wv.similarity('唐太宗','李世民'),计算出的相邻度为0.78733486
6.2.2.5词频查询expandos
在gensim4.0以上版本,系统已用wv.expandos代替wv.vocab,可以通过此属性可查询每个分词的数量等信息
6.2.3Word2Vec在Embedding层的应用
Tensorborad准确率
6.3GloVe词嵌入
除了Word2Vec库,另一个常用的库就是GloVe(GlobalVectorsforWordRepresentation),它是由斯坦福大学研究人员在2014年开发的。这种嵌入方法基于全词频统计的方式对分词进行了全局矩阵因式分解,它可以直接把单词表达成实数向量,这些向量捕捉到了单词之间一些语义特性,比如相似性(similarity)、类比性(analogy)等。通过对向量的运算,比如欧几里得距离或者cosine相似度,可以计算出两个单词之间的语义相似性。
GloVe常用方法如下
1#准备数据集2sentense=[['摘要','人工智能','AI','开发'],['我们','是','机器人'],......]3corpus_model=Corpus()4corpus_model.fit(sentense,window=10)5#训练6glove=Glove(no_components=100,learning_rate=0.05)7glove.fit(corpus_model.matrix,epochs=10,8no_threads=1,verbose=True)9glove.add_dictionary(corpus_model.dictionary)10#模型保存11glove.save('glove.model')12glove=Glove.load('glove.model')13#语料保存14corpus_model.save('corpus.model')15corpus_model=Corpus.load('corpus.model')完成训练后,使用Glove.load()就可以重新加载,然后把向量加载到Embedding层,使用方式与Word2Vec非常类似,在此就不再做重复介绍。利用gensim.scripts.glove2word2vec还可以把glove向量转化为Word2Vec向量使用