目录
1 声明:
清华开的本门课程在Word2Vec这块并没单独开放一期视频,因此本人参考了B站上一个UP主的分享简单进行了Word2Vec代码实战。
2 代码链接:
文件链接:https://pan.baidu.com/s/1swwl8FD6ITO0mIEwSyTGCw 提取码:nnnn
3 参考链接:
词向量 | word2vec | 理论讲解+代码 | 文本分析【python-gensim】
4 实战步骤:
4.1 数据选取:
这里我们选取《三国演义》上卷进行Word2vec 训练。
4.2 数据预处理以及分词:
4.2.1 数据预处理:
实际场景下需要对文本中的标点符号、异常字符以及停用词去除掉以得到相对干净的数据,这里我们保留字母、数字和空格外其他的所有字符,有需要的同学可以自己加入停用词并更细粒度清理数据。
4.2.2 为什么要分词?
- 缓解一词多义问题:比如“哈哈哈”和“哈士奇”中的【“哈”】含义截然不同,如果模型在训练过程中没有见过“哈士奇”,且按最小单元字的量级去处理时,可能就会把“哈士奇”错误的识别成一种欢快的气氛词,而非一种动物实体。
- 提供更高level的文本特征给模型:从特征的角度来理解,词序列会包含的部分实体、意图等信息,特征的信息量往往大于字序列。因此,好的分词可以给NLP模型提供质量更好的特征,从而使得搜索引擎对文本的理解可以从“字序列”升级为“词序列”。
4.2.3 分词:
jieba库是一款优秀的 Python 第三方中文分词库,jieba 支持三种分词模式:精确模式、全模式和搜索引擎模式,本文使用jieba库用于中文文本的分词
#精确模式
jieba.lcut(text, cut_all=False)
#全模式
jieba.lcut(text, cut_all=True)
#搜索引擎模式
jieba.lcut_for_search(text)
三种模式(切分方法)的区别如下图所示:
# 分词
f = open("sanguo.txt", 'r',encoding='utf-8') #读入文本
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行分词结果
前五行的分词结果如下图所示:
4.3 模型训练:
gensim库中封装好了完整的Word2Vec模型,在这里我们直接调用该模型函数即可,其参数的含义可以阅读下表:
参数 | 说明 |
---|---|
sentences | 可以是一个list,对于大语料集,建议使用BrownCorpus,Text8Corpus或lineSentence构建。 |
vector_size | word向量的维度,默认为100。大的size需要更多的训练数据,但是效果会更好。推荐值为几十到几百。 |
alpha | 学习率 |
window | 表示当前词与预测词在一个句子中的最大距离是多少。 |
min_count | 可以对字典做截断。词频少于min_count次数的单词会被丢弃掉,默认值为5。 |
max_vocab_size | 设置词向量构建期间的RAM限制。如果所有独立单词个数超过这个,则就消除掉其中最不频繁的一个。每一千万个单词需要大约1GB的RAM。设置成None则没有限制。 |
sample | 高频词汇的随机降采样的配置阈值,默认为1e-3,范围是(0,1e-5) |
seed | 用于随机数发生器。与初始化词向量有关。 |
workers | 参数控制训练的并行数。 |
sg | 用于设置训练算法,默认为0,对应CBOW算法;sg=1则采用skip-gram算法。 |
hs | 如果为1则会采用hierarchica·softmax技巧。如果设置为0(default),则negative sampling会被使用。 |
negative | 如果>0,则会采用negative samping,用于设置多少个noise words。 |
cbow_mean | 如果为0,则采用上下文词向量的和,如果为1(default)则采用均值。只有使用CBOW的时候才起作用。 |
hashfxn | hash函数来初始化权重。默认使用python的hash函数。 |
epochs | 迭代次数,默认为5。 |
trim_rule | 用于设置词汇表的整理规则,指定那些单词要留下,哪些要被删除。可以设置为None(min_count会被使用)或者一个接受()并返回RULE_DISCARD,utils。RULE_KEEP或者utils。RULE_DEFAULT的函数。 |
sorted_vocab | 如果为1(default),则在分配word index 的时候会先对单词基于频率降序排序。 |
batch_words | 每一批的传递给线程的单词的数量,默认为10000 |
min_alpha | 随着训练的进行,学习率线性下降到min_alpha |
此表转载自gensim.models.word2vec() 参数详解_word2vec vector_size-CSDN博客
# 调用Word2Vec训练
# 参数:size: 词向量维度;window: 上下文的宽度,min_count为考虑计算的单词的最低词频阈值
model = Word2Vec(lines,vector_size = 20, window = 2 , min_count = 3, epochs=7, negative=10)
print("孔明的词向量:\n",model.wv.get_vector('孔明'))
print("\n和孔明相关性最高的前20个词语:")
similar_words = model.wv.most_similar('孔明', topn = 20)# 与孔明最相关的前20个词语
for word, similarity in similar_words:
print(word, similarity)
孔明的词向量:
显示和孔明相关性最高的前20个词语:
4.4 可视化:
4.4.1 PCA降维:
由于之前我们设置的词向量维度是20,所以不能直观地进行可视化,需要进行降维,本文选取PCA(主成分分析)进行降维:
# 将词向量投影到二维空间
rawWordVec = []
word2ind = {}
for i, w in enumerate(model.wv.index_to_key): #index_to_key 序号,词语
rawWordVec.append(model.wv[w]) #词向量
word2ind[w] = i #{词语:序号}
rawWordVec = np.array(rawWordVec)
X_reduced = PCA(n_components=2).fit_transform(rawWordVec)
#降维之前20维
# 输出降维前的词向量
print("降维前的词向量:")
print(rawWordVec)
#降维之后2维
# 输出降维后的词向量
print("\n降维后的词向量:")
print(X_reduced)
降维前的词向量(20维):
降维后的词向量(2维):
4.4.2 绘制星空图:
# 绘制星空图
# 绘制所有单词向量的二维空间投影
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 = ['孙权', '刘备', '曹操', '周瑜', '诸葛亮', '司马懿','汉献帝']
# 设置中文字体 否则乱码
zhfont1 = matplotlib.font_manager.FontProperties(fname='./华文仿宋.ttf', size=16)
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, fontproperties = zhfont1, alpha = 1, color = 'red')
plt.show()
黑色散点指的是所有词语,通过特殊单词向量的显示,我们可以看出刘备和诸葛亮挨的比较紧凑,其余的也差不多,说明模型的训练效果还是不错的。
4.5 类比关系实验:
# 类比关系实验
# 玄德-孔明=?-曹操
words = model.wv.most_similar(positive=['玄德', '曹操'], negative=['孔明'])
print(words)
# 曹操-魏=?-蜀
words = model.wv.most_similar(positive=['曹操', '蜀'], negative=['魏'])
print(words)
最后的结果如下图所示(哈哈,第一个匹配的可能效果不是很好)