词向量与word2vec分析

词嵌入(word embedding)、word2vec(cbow、skip-gram)与gensim使用

Posted by grt1stnull on 2017-05-31

0x00.前言

前一阵子参加了科赛的一个数据的比赛,涉及了自然语言处理(nlp)。所以写一个总结,解释一下词向量(word embedding)、word2vec模型以及词向量的构造。

0x01.词向量

就像图片编码处理后进行模型训练一样,我们也要对文本进行处理,把它抽象为一个矩阵或者向量。通常我们处理的不是一段话、一个句子,而是一个单词或词语。对其进行处理的目的是将单词或词语转化为对计算机友好的数据格式,我们把处理后的向量叫做词向量或词嵌入。

对词语的处理有两种方法,一种是将词语映射为一个独一无二的稀疏矩阵,类似于它的编号,这种方法叫做One-hot Representation。但是这种方法存在着“词汇鸿沟”现象与维度灾难。词汇鸿沟是指,单纯的看两个词语的特征向量,我们发现不了它们的任何联系,任何两个词语间都是孤立的。但是我们知道,看到两个词语的时候,我们可以清楚的认识到两个单词间的联系。

另一种方法是叫做distributed Representations,将单词抽象为词向量,每个词语都由一个多维的特征向量表示,我们可以通过计算余弦相似度比较两个词语间的联系。词向量的训练过程是无监督学习过程,我们只需要提供语料,比较流行的算法模型是word2vec。

0x02.word2vec及其模型

word2vec算法有两种重要模型Skip-gram(Continuous Skip-gram Model)与CBOW(Continuous Bag-of-Words Model),两个算法的区别是前者利用一个单词预测其上下文单词,而后者正相反,是从词语已知上下文中预测这一词语。

对于Skip-gram与CBOW模型,word2vec给出了两套实现框架,分别是Hierarchial Softmax和Negative Sampling。两种框架的区别在于,前者使用利用词语出现次数构造的哈夫曼树计算频率,而后者使用随机负采样,目的是提高训练速度与改善词向量质量。

对于CBOW模型,已知词语上下文Context(w)与词语w,目标函数可得如下对数似然函数:

而Skip-gram模型输入输出正相反,其目标函数为:

根据框架选择的不同,利用哈夫曼树或随机负采样可以得到概率,之后带入目标函数。

得到对数似然函数采用梯度下降法使其最大化。进行梯度计算及更新公式(学习率)。

0x03.使用gensim构造词向量

python没有叫做word2vec的库,我们使用得是gensim库。gensim在python中只实现了skip-gram算法,不过这并不影响我们使用。

1.语料收集

这里我的语料来自于题目所给的数据。有如下几个步骤

  1. 取出所有的字符
  2. 对符号、字母等过滤,得到只有汉字的一个txt文件
  3. 使用jieba对其进行分词,以空格相间

1) 字符清理

只保留汉字就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#coding:utf-8
import os
import re
# 输入文件 与 输出文件
output = open('output2.txt', 'w')
inp = open('log2.txt', 'r')
# 按行读取文件
for line in inp.readlines():
# 正则过滤
#ss = re.findall('[^\d\n\s*\r\u4e00-\u9fa5]', line)
p = re.compile(ur'[\u4e00-\u9fa5]+')
#ss = re.findall('[^a-zA-Z]', line)
ss = p.findall(unicode(line, "utf-8"))
# 写入输出文件
output.write("".join(ss).encode("utf-8"))
output.write('\n')
# 关闭文件
inp.close()
output.close()

2) jieba分词

jieba对汉语分词效果很好,想要进一步了解可以查看官方文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#coding:utf-8
import jieba
# 多线程、本机支持不好就禁用了
#jieba.enable_parallel()
# 相邻词语间隔
space = ' '
# 输入文件 与 输出文件
output = open('words.txt', 'w')
inp = open('output.txt', 'r')
for line in inp.readlines():
# jieba分词
seg_list = jieba.cut(line)
# 写入输出文件
output.write(space.join(seg_list) + '\n')
# 关闭文件
inp.close()
output.close()

2.训练模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#coding:utf-8
import logging
import os.path
import sys
import multiprocessing
from gensim.corpora import WikiCorpus
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
# 输入文件
inp = 'words3.txt'
outp1 = 'web_words.model'
outp2 = 'web_words.vector'
# Word2Vec函数的参数:
# size 表示特征向量维度,默认100
# window 表示当前词与预测词在一个句子中的最大距离
# min_count 词频少于min_count次数的单词会被丢弃掉, 默认值为5
model = Word2Vec(LineSentence(inp), size=400, window=5, min_count=5,\
workers=multiprocessing.cpu_count())
# 默认格式model
model.save(outp1)
# 原始c版本model
model.wv.save_word2vec_format(outp2, binary=False)

3.基本使用

训练好模型进行的小测试,感觉很不错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding:utf-8
import gensim
# 编码格式
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# 加载模型
model = gensim.models.Word2Vec.load('web_words.model')
# 特征向量维度
size = model.vector_size
# 单词的向量
v = model[u'少女']
print size
print v

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
400
[ 8.43580186e-01 -9.96889651e-01 -7.99913585e-01 -4.39080328e-01
1.77503848e+00 1.29446602e+00 -1.52770126e+00 1.51806796e+00
-7.54987478e-01 2.55844021e+00 7.50788331e-01 3.79817247e-01
-3.28724474e-01 2.10721001e-01 1.66165423e+00 -1.33865356e-01
-5.39526880e-01 -4.30585086e-01 1.06060803e+00 -3.13318431e-01
3.54138672e-01 5.72560787e-01 9.99186516e-01 1.33570850e+00
-2.47879148e-01 1.30511200e+00 8.60800505e-01 -7.64900982e-01
1.26472020e+00 8.23687077e-01 3.07441205e-01 -7.32565522e-01
4.31946605e-01 -8.97184789e-01 -2.72827983e-01 1.64680278e+00
-9.39997792e-01 -1.68242455e+00 3.67325902e-01 -1.03376389e-01
1.15108991e+00 1.31034935e+00 -2.02769589e+00 -6.89099431e-01
-1.51745272e+00 -8.21902156e-01 -1.76097643e+00 -4.95533198e-01
1.21536779e+00 -1.40373683e+00 -1.36783874e+00 1.69045836e-01
5.21723509e-01 -6.93929017e-01 8.98100674e-01 -3.01276743e-01
4.29140925e-01 -8.04761231e-01 3.44539672e-01 -2.78323054e-01
7.61275738e-02 -7.97224462e-01 -4.74477857e-01 -1.58943701e+00
1.73392504e-01 1.54953849e+00 -1.43597603e+00 -5.84415853e-01
1.11646068e+00 7.99975395e-02 1.73107982e-01 1.03152871e+00
-1.42563516e-02 -2.74376720e-01 9.66395199e-01 -1.08919930e+00
1.20555353e+00 -7.49185562e-01 -5.16100824e-01 6.94500446e-01
-2.00204790e-01 3.70599806e-01 9.45616603e-01 1.78066242e+00
-2.31012732e-01 2.18667269e+00 -7.55105376e-01 1.20586944e+00
-1.84096193e+00 1.47042429e+00 -1.54893851e+00 2.14809775e-01
-9.05168414e-01 -8.84964705e-01 1.00996995e+00 6.90825358e-02
-1.17379665e+00 -8.71409893e-01 -2.98106521e-01 -4.36888635e-01
5.83857417e-01 2.00399256e+00 -3.39457452e-01 5.74710071e-01
-5.80044925e-01 1.25820243e+00 -6.68906450e-01 -2.27513528e+00
-1.95195866e+00 -1.39131975e+00 7.28424013e-01 -6.16876185e-01
9.51394796e-01 6.15295470e-01 -1.00253403e+00 9.03217912e-01
6.12991191e-02 2.91212648e-02 -7.35920072e-01 8.58711898e-01
1.68337727e+00 3.14759016e-01 -3.84985417e-01 1.23079610e+00
7.63045177e-02 -1.86154455e-01 9.24728036e-01 -1.63472295e+00
-1.45414364e+00 -1.97648728e+00 1.16380000e+00 1.79543650e+00
-3.13000560e+00 1.63300264e+00 1.32793307e+00 1.16838443e+00
1.72551259e-01 -5.13504028e-01 8.05045187e-01 -5.61231613e-01
-2.12595153e+00 -8.84528279e-01 1.26554120e+00 1.08733094e+00
-9.27044630e-01 -4.26214278e-01 8.35291803e-01 -6.07960582e-01
-8.45824718e-01 9.59472060e-01 6.95870042e-01 -3.91528457e-01
1.57924187e+00 9.34813440e-01 8.38734150e-01 -1.40014827e+00
-2.92787887e-02 8.75473201e-01 -5.84763288e-01 -1.99537560e-01
9.13132668e-01 6.40124321e-01 -5.09210706e-01 3.32897812e-01
3.09894621e-01 -5.85409343e-01 1.63564682e+00 -1.58412129e-01
-5.84037542e-01 -1.53046978e+00 -1.80295396e+00 1.18067396e+00
-8.94384742e-01 -1.00935853e+00 -1.06740487e+00 9.87019315e-02
1.33844817e+00 -4.84928191e-01 -1.01971292e+00 -1.40371168e+00
-2.42987067e-01 2.21512347e-01 -1.20145929e+00 -5.20043969e-01
-1.44045278e-01 -1.60914969e+00 1.63498008e+00 -6.49034619e-01
-2.05779767e+00 1.92865205e+00 -5.35783648e-01 -5.73674500e-01
2.10478687e+00 -2.56345212e-01 7.60093093e-01 8.38537812e-01
-7.95001328e-01 -9.30995643e-01 -7.99197197e-01 -1.54063523e+00
-6.08063042e-01 2.07473993e-01 4.34703082e-01 -3.71754885e-01
1.02676904e+00 2.78252929e-01 1.52795482e+00 1.37919769e-01
-9.99383926e-01 5.88999748e-01 -7.33785272e-01 -1.66489530e+00
-6.96008623e-01 8.26062500e-01 2.76657869e-03 4.62220579e-01
-1.11987019e+00 -7.05150962e-01 8.52191806e-01 1.05809963e+00
1.25231361e+00 -1.37434757e+00 -5.61459124e-01 1.31524777e+00
7.08946109e-01 6.96632192e-02 -3.72218597e-03 3.93214859e-02
1.26100421e+00 -1.70912647e+00 1.29855648e-01 -1.22835410e+00
-1.85985083e-03 -3.01825792e-01 -2.44489932e+00 9.17255342e-01
-1.04218018e+00 9.62443769e-01 2.05759668e+00 -6.04564250e-01
2.74809033e-01 9.81030405e-01 7.17494130e-01 -3.39406252e-01
1.61588025e+00 7.88029492e-01 3.04335326e-01 -1.42919585e-01
-2.54244238e-01 2.89612436e+00 1.22055089e+00 -2.00335488e-01
1.39601871e-01 -7.40230918e-01 -8.03827226e-01 8.28680277e-01
-8.35719764e-01 3.28945518e-01 -4.77222830e-01 -1.17715454e+00
-1.77932358e+00 -7.43216813e-01 7.03337491e-02 -2.20332235e-01
-7.12516904e-02 -1.91951990e+00 4.60949093e-01 -1.58758390e+00
-1.29770130e-01 -2.06872511e+00 3.72601241e-01 -1.72245955e+00
-8.92038196e-02 1.08535755e+00 4.55891132e-01 3.53040874e-01
6.61257088e-01 -2.06458852e-01 -8.01300645e-01 -2.75559616e+00
-4.32139009e-01 -7.10852563e-01 7.30033457e-01 -1.57075572e+00
1.29583406e+00 1.20143223e+00 -1.06521583e+00 1.70795631e+00
1.33439767e+00 1.92839491e+00 3.64060625e-02 6.86546504e-01
-2.44799590e+00 -8.10643673e-01 -5.33080161e-01 1.11595452e+00
6.28994942e-01 -8.93258214e-01 -8.42106864e-02 1.45618945e-01
-8.96800518e-01 1.59965074e+00 1.86706913e+00 -9.35686052e-01
1.47664523e+00 -1.73507184e-02 8.07405770e-01 1.42838001e+00
-5.87339342e-01 3.76376659e-01 8.61623049e-01 1.58352578e+00
1.47718000e+00 -1.30609155e+00 -1.51038086e+00 2.35511327e+00
1.12220068e-02 6.77131116e-01 -1.57578528e+00 7.28903830e-01
1.53305069e-01 1.41794538e+00 8.46278593e-02 5.67265809e-01
8.36715698e-01 7.25901902e-01 -1.59344685e+00 -1.41044843e+00
-6.70181274e-01 -9.31744993e-01 -1.00910282e+00 1.94031477e-01
1.75198734e+00 4.23809551e-02 -1.33153701e+00 1.27069902e+00
5.32282472e-01 2.04189897e+00 -3.67238641e-01 1.58694279e+00
-2.48838592e+00 1.36547315e+00 1.36934888e+00 1.13085139e+00
2.84735656e+00 6.54410779e-01 8.09507370e-01 -9.17495012e-01
9.85128164e-01 1.52835238e+00 -2.95696944e-01 -6.94908798e-01
1.08731262e-01 -2.22732455e-01 9.58909690e-01 -3.17186117e+00
2.00879526e+00 -2.82169670e-01 9.70469773e-01 1.03761053e+00
-4.70901728e-01 3.67405891e-01 1.97300166e-01 1.42461681e+00
-5.67683399e-01 -5.72197512e-02 2.10058641e+00 6.16604149e-01
2.13886499e+00 1.14346838e+00 1.63408399e+00 -1.39311099e+00
1.39931142e+00 -1.42244124e+00 3.26153308e-01 -1.01225746e+00
1.27143419e+00 -1.47422886e+00 4.32568610e-01 -2.56865764e+00
1.09911728e+00 7.29744792e-01 -3.29833895e-01 2.10092306e+00
-9.02076438e-02 6.40789032e-01 -1.27699077e+00 -9.98630524e-01
-1.85138988e+00 -1.47061205e+00 -1.72385561e+00 -5.83995581e-01
1.87240863e+00 -1.34105369e-01 -1.11345994e+00 2.09584445e-01
1.68201923e+00 -3.93061433e-03 4.20599252e-01 1.14158893e+00]

类似的使用还有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#coding:utf-8
import gensim
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
model = gensim.models.Word2Vec.load('web_words.model')
# 计算两个词语相似度
y = model.similarity(u"男人", u"女人")
# 计算单词相关列表
list = model.most_similar(u"赌场", topn=20)
print y
for i in list:
print "word:%s | %g" % (i[0].decode('utf-8'), i[1])

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0.765876215668
word:贵宾厅 | 0.582149
word:赌博 | 0.576606
word:赌客 | 0.576345
word:娱乐场 | 0.560711
word:赌球 | 0.555879
word:澳门 | 0.549308
word:赌钱 | 0.543402
word:威尼斯人 | 0.53562
word:葡京 | 0.535545
word:拉斯维加斯 | 0.535134
word:赌厅 | 0.530768
word:轮盘 | 0.528454
word:荷官 | 0.52547
word:赌桌 | 0.525209
word:超濠 | 0.51781
word:博彩 | 0.514765
word:投注网 | 0.510482
word:大西洋城 | 0.509464
word:真钱 | 0.508942
word:赌局 | 0.506562

了解更多函数,可以查看gensim的word2vec model文档。

4.在线训练

当语料增加时,我们可以在现有模型基础上进行训练,而不需要重新开始训练。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
from gensim.models import Word2Vec
import logging,gensim,os
from gensim.models.word2vec import LineSentence
# 加载模型
model = Word2Vec.load('word2vector2.model')
# 导入新增语料
new_corpus = LineSentence('corpus/iphone6sreview-seg.txt')
# 在线训练模型
model.train(new_corpus)
# 保存新模型
model.save('word2vector3.model')

0x04.参考资料

自己动手写word2vec (一):主要概念和流程

Google word2vec算法 数学原理 - 下载频道 - CSDN.NET

Deep Learning实战之word2vec

使用 word2vec 训练wiki中英文语料库