什么是word2vec?
Word2Vec是google在2013年推出的一个NLP工具,它的特点是能够将单词转化为向量来表示,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系。用词向量来表示词并不是Word2Vec的首创,在很久之前就出现了。最早的词向量采用One-Hot编码,又称为一位有效编码,每个词向量维度大小为整个词汇表的大小,对于每个具体的词汇表中的词,将对应的位置置为1。比如我们有下面的5个词组成的词汇表:
采用One-Hot编码方式来表示词向量非常简单,但缺点也是显而易见的,一方面我们实际使用的词汇表很大,经常是百万级以上,这么高维的数据处理起来会消耗大量的计算资源与时间。另一方面,One-Hot编码中所有词向量之间彼此正交,没有体现词与词之间的相似关系。
Distributed representation可以解决One-Hot编码存在的问题,它的思路是通过训练,将原来One-Hot编码的每个词都映射到一个较短的词向量上来,而这个较短的词向量的维度可以由我们自己在训练时根据任务需要来自己指定。
下图是采用Distributed representation的一个例子,我们将词汇表里的词用"Royalty","Masculinity", "Femininity"和"Age"4个维度来表示,King这个词对应的词向量可能是(0.99,0.99,0.05,0.7)。当然在实际情况中,我们并不能对词向量的每个维度做一个很好的解释。
Word2Vec的原理
Word2Vec 的训练模型本质上是只具有一个隐含层的神经元网络(如下图)。
它的输入是采用One-Hot编码的词汇表向量,它的输出也是One-Hot编码的词汇表向量。使用所有的样本,训练这个神经元网络,等到收敛之后,从输入层到隐含层的那些权重,便是每一个词的采用Distributed Representation的词向量。比如,上图中单词的Word embedding后的向量便是矩阵 Wv x N 的第i行的转置。这样我们就把原本维数为V的词向量变成了维数为N的词向量(N远小于V),并且词向量间保留了一定的相关关系。
gensim实现word2vec模型(实战)
本次实战案例选取的数据集是四大名著中的三国演义。
首先使用jieba进行中文分词,
import jieba import re import warnings warnings.filterwarnings('ignore') with open("sanguo.txt", 'r',encoding='utf-8')as f: # 读入文本 lines = [] for line in f: #分别对每段分词 temp = jieba.lcut(line) #结巴分词 精确模式 words = [] for i in temp: #过滤掉所有的标点符号 i = re.sub("[\s+\.\!\/_,$%^*(+\"\'””《》]+|[+——!,。?、~@#¥%……&*():;‘]+", "", i) if len(i) > 0: words.append(i) if len(words) > 0: lines.append(words) print(lines[0:5])#预览前5行分词结果
分词之后使用gensim库中的word2vec构建模型,
from gensim.models import Word2Vec # 调用Word2Vec训练 参数:size: 词向量维度;window: 上下文的宽度,min_count为考虑计算的单词的最低词频阈值 model = Word2Vec(lines,vector_size = 20, window = 2 , min_count = 3, epochs=7, negative=10,sg=1) print("孔明的词向量:\n",model.wv.get_vector('孔明')) print("\n和孔明相关性最高的前20个词语:") model.wv.most_similar('孔明', topn = 20)# 与孔明最相关的前20个词语
从结果中我们看出,与孔明相关性最高的是玄德、云长、孟获,也符合小说中的角色关系。
接着我们将模型可视化,
import numpy as np from sklearn.decomposition import PCA # 将词向量投影到二维空间 rawWordVec = [] word2ind = {} for i, w in enumerate(model.wv.index_to_key): rawWordVec.append(model.wv[w]) # 词向量 word2ind[w] = i # {词语:序号} rawWordVec = np.array(rawWordVec) X_reduced = PCA(n_components=2).fit_transform(rawWordVec) # PCA降2维 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] #解决中文显示 plt.rcParams['axes.unicode_minus'] = False #解决符号无法显示 # 绘制星空图 # 绘制所有单词向量的二维空间投影 fig = plt.figure(figsize = (15, 10)) ax = fig.gca() ax.set_facecolor('white') ax.plot(X_reduced[:, 0], X_reduced[:, 1], '.', markersize = 1, alpha = 0.3, color = 'black') # 绘制几个特殊单词的向量 words = ['孙权', '刘备', '曹操', '周瑜', '诸葛亮', '司马懿','汉献帝'] for w in words: if w in word2ind: ind = word2ind[w] xy = X_reduced[ind] plt.plot(xy[0], xy[1], '.', alpha =1, color = 'orange',markersize=10) plt.text(xy[0], xy[1], w, alpha = 1, color = 'red')
最后,我们还可以做个类比关系实验,
比如,孔明是玄德的军师,那么曹操的军师是谁呢?
# 玄德-孔明=?-曹操 words = model.wv.most_similar(positive=['玄德', '曹操'], negative=['孔明']) words
我们可以看到 排在最前面的是司马懿,也对上了原小说关系。
曹操跟魏国的关系很近(虽然他不是开国皇帝),那么跟蜀国关系最近的谁呢?
# 曹操-魏=?-蜀 words = model.wv.most_similar(positive=['曹操', '蜀'], negative=['魏']) words
我们看到排在最前面的是玄德,也应证了原小说。