word2vec学习笔记
在自然语言处理中,传统统计特征包含的信息量过少,这也一直限制着深度学习在自然语言处理中的应用。词向量由于包含了更丰富的信息,使得深度学习能够处理绝大多数自然语言处理应用。词向量技术是将词转化成为稠密向量,并且对于相似的词,其对应的词向量也相近。
目前生成词向量的方法有很多,这些方法都依照一个思想:任一词的含义可以用它的周边词来表示。下面针对word2vec的两个模型进行介绍。
word2vec介绍
Mikolov等人提出的word2vec模型及其应用最近两年吸引了很多关注,通过word2vec模型学习的单词的矢量表示在各种NLP任务中很有用。其中实现word2vec有两种模型,分别是:CBOW(continuous bag-of-word)模型和SG(skip-gram)模型,下面分别对其进行介绍。
CBOW模型
One-word Context
这是CBOW模型的最简单版本,即预测目标的上下文只有一个单词,用一个单词来预测另一个单词的共现概率,下图即为该定义下的神经网络模型。
其中V表示文本词汇量的大小;N表示隐藏层的大小;W表示计算隐含层的权重矩阵;W’表示从隐含层计算输出层的权重矩阵。
- 首先,从输入层到隐藏层的计算公式为:
h = W T x = W ( k , . ) T : = V w I T h = W^Tx=W^T_{(k,.)}:=V^T_{wI} h=WTx=W(k,.)T:=VwIT
由于x是one-hot编码(即对应该向量只有词id位置的值为1,其余值均为0),隐含层相当于取出W(维度为V×N)中单词id对应的行向量,这里定义为输入词的向量 V w I V_{wI} VwI - 其次,从隐藏层到输出层定义了另一个权重矩阵W’,维度为N×V,可理解为单词id位置对应的列向量为输出词的词向量。通过隐藏层与输出词词向量作内积,可以认为是隐藏层与实际输出的余弦相似度。从而得到每个词与当前位置的匹配度为:
u j = v w j ′ T h u_j = {v'_{w_j}}^Th uj=vwj′Th
其中,下标j即为输出单词的id, v w j ′ {v'_{w_j}} vwj′即第j个输出词对应的词向量。 - 再利用softmax函数将匹配度转化为对应词出现的概率分布y,从而将其映射到0-1之间。模型的目标是使对应真实输出词的输出概率最高,从而得出目标函数,同时其相反数作为损失函数:
m a x ( p ( w O ∣ w I ) ) = m a x ( y j ∗ ) = m a x ( l o g ( y j ∗ ) ) = u j ∗ − l o g ( ∑ j ′ = 1 V e x p ( u j ′ ) ) : = − E max(p(w_O|w_I))=max(y^*_j)=max(log(y^*_j))=u^*_j-log(\sum_{j'=1}^{V}exp(u_{j'})):=-E max(p(wO∣wI))=max(yj∗)=max(log(yj∗))=uj∗−log(j′=1∑Vexp(uj′)):=−E
其中上标*表示实际对应的情况, u j ∗ {u^*_j} uj∗表示实际输出词对应到模型中的匹配度。 - 之后对损失函数进行求偏导,从而得出训练时的更新方程。
(1)首先计算损失函数对于匹配度的偏导(预测误差):
ð E ð u j = y j − t j : = e j \frac {\eth E}{\eth u_j}=y_j-t_j:=e_j ðujðE=yj−tj:=ej
其中, y j y_j yj对应 l o g ( ∑ j ′ = 1 V e x p ( u j ′ ) ) log(\sum_{j'=1}^{V}exp(u_{j'})) log(∑j′=1Vexp(uj′))部分,求导后为预测的概率分布;当且仅当下标为 j ∗ j^* j∗时, t j t_j tj对应 u j ∗ u^*_j uj∗的定义为1,否则为0。
之后通过链式法则,即可计算出代价函数关于W’的偏导,进一步得出:
W ′ n e w = W ′ o l d − η ⋅ e ⋅ h T W'^{new}=W'^{old}-\eta \cdot e \cdot h^T W′new=W′old−η⋅e⋅hT
(2)其次计算代价函数对于W的偏导。首先计算代价函数关于隐含层的偏导:
ð E ð h i = ∑ i = 1 V ð E ð u j ⋅ ð u j ð h i = ∑ i = 1 V e j ⋅ w i j ′ : = E H i \frac {\eth E}{\eth h_i}=\sum_{i=1}^{V} \frac {\eth E}{\eth u_j} \cdot \frac {\eth u_j}{\eth h_i}=\sum_{i=1}^{V}e_j \cdot w'_{ij}:=EH_i ðhiðE=i=1∑VðujðE⋅ðhiðuj=i=1∑Vej⋅wij′:=EHi
这里定义EH为W’所有词向量关于预测误差 e j e_j ej为权重的和,即:
E H = W ′ e EH=W'e EH=W′e
同样通过链式法则计算代价函数关于W的偏导,最终得到更新函数为:
W n e w = W o l d − η ⋅ x ⋅ E H T W^{new}=W^{old}-\eta \cdot x \cdot {EH}^T Wnew=Wold−η⋅x⋅EHT
Multi-word Context
基于multi-word context的CBOW模型就是利用多个上下文单词来推测中心单词target word的一种模型,利用词上下文的k个词来预测该位置词的信息,预测模型的框架图如下所示:
- 从输入层到隐藏层的计算公式为:
h = 1 C W T ( x 1 + x 2 + … + x C ) = 1 C ( v w 1 + v w 2 + … + v w C ) h=\frac{1}{C}W^T(x_1+x_2+…+x_C)=\frac{1}{C}(v_{w1}+v_{w2}+…+v_{wC}) h=C1WT(x1+x2+…+xC)=C1(vw1+vw2+…+vwC)
在该过程中,可以对于C个输入词分别按照one-word过程进行计算后取平均值,因而可以得出输入层到隐含层权重的更新函数为:
W n e w = W o l d − η C ⋅ ∑ i = 1 C x i ⋅ E H T W^{new}=W^{old}-\frac{\eta}{C} \cdot \sum_{i=1}^{C}x_i \cdot EH^T Wnew=Wold−Cη⋅i=1∑Cxi⋅EHT - 从隐含层到输出层的过程与上述过程相同,对于W’的更新函数也一致:
W ′ n e w = W ′ o l d − η ⋅ e ⋅ h T W'^{new}=W'^{old}-\eta \cdot e \cdot h^T W′new=W′old−η⋅e⋅hT
Skip-Gram模型
Skip-Gram方法是用中心词预测输出上下文的概率,预测模型框架图如下所示:
Skip-Gram模型与one-word情形的最大区别在于输出词由1个变为C个,因而从损失函数函数开始就要发生变化:
E = − l o g ( p ( w O , 1 , w O , 2 , … , w O , C ∣ w I ) ) = − l o g ∏ c = 1 C e x p ( u c , j c ∗ ) ∑ j ′ = 1 V e x p ( u j ′ ) = − ∑ c = 1 C u c , j c ∗ + C ⋅ l o g ( ∑ j ′ = 1 V e x p ( u j ′ ) ) E=-log(p(w_{O,1},w_{O,2},…,w_{O,C}|w_I))=-log \prod \limits_{c=1}^C \frac{exp(u_{c,j^*_c})}{\sum_{j'=1}^{V}exp(u'_j)}=-\sum_{c=1}^{C}u_{c,j^*_c}+C\cdot log(\sum_{j'=1}^{V}exp(u'_j)) E=−log(p(wO,1,wO,2,…,wO,C∣wI))=−logc=1∏C∑j′=1Vexp(uj′)exp(uc,jc∗)=−c=1∑Cuc,jc∗+C⋅log(j′=1∑Vexp(uj′))
从而可看出其等于C次one-word损失函数加和,由此,根据one-word情形损失函数对于单一匹配度的偏导,定义对于上下文输出匹配度的偏导为:
E
I
=
∑
c
=
1
C
e
c
EI=\sum_{c=1}^{C}e_c
EI=c=1∑Cec
根据链式法则,损失函数从隐藏层到输出层权重的偏导为:
从而得出隐藏层到输出层权重的更新函数为:
同时更新定义EH为W’所有词向量关于预测误差
E
I
EI
EI为权重的和,即:
E
H
=
W
′
E
I
EH=W'EI
EH=W′EI
从而输入层到隐含层的更新函数仍然适用:
W
n
e
w
=
W
o
l
d
−
η
⋅
x
⋅
E
H
T
W^{new}=W^{old}-\eta \cdot x \cdot {EH}^T
Wnew=Wold−η⋅x⋅EHT
虽然skip-gram算法输出层的权重是相同的,但也可重新定义y为C个上下文词的独热编码等权平均数,从而完全套用one-word情形中的公式:
h
=
1
C
(
y
1
+
y
2
+
…
+
y
C
)
h=\frac{1}{C}(y_{1}+y_{2}+…+y_{C})
h=C1(y1+y2+…+yC)
word2vec词向量训练
在学习了word2vec的原理后,利用维基百科的数据集进行了词向量训练,下面是实验的过程。
数据来源
训练使用的语料库来自维基百科的中文语料库,下载地址为:https://dumps.wikimedia.org/zhwiki/latest/zhwiki-latest-pages-articles.xml.bz2
数据预处理
繁体转换成简体
由于原始语料中有繁体文字,因此需要将其先统一转换为简体字,方便后面进行处理,使用python的zhconv库对每一行调用convert函数进行操作。
import zhconv
lines = input_file.readlines()
for line in lines:
output_file.write(zhconv.convert(line, 'zh-hans'))
转换后的语料截图为:
分词
利用jieba库对语料进行分词,采用的是cut方法。
import jieba
lines = input_file.readlines()
for line in lines:
output_file.write(' '.join(jieba.cut(line.split('\n')[0].replace(' ', ''))) + '\n')
分词后的语料截图为:
去除非中文词汇
经过上述两个步骤后,语料中还存在一些非中文的单词,在该步中去除这些单词,通过匹配正则表达式来判断单词是否属于中文。
import re
lines = input_file.readlines()
chinese = '^[\u4e00-\u9fa5]+$'
for line in lines:
line_list = line.split('\n')[0].split(' ')
line_list_new = []
for word in line_list:
if re.search(chinese, word):
line_list_new.append(word)
output_file.write(' '.join(line_list_new) + '\n')
处理后的语料截图为:
词向量训练
使用python的gensim库对预处理好的语料进行训练,训练参数设置为:
size:词向量维度为200;
window:当前词与预测词在一个句子中的最大距离是5;
min_count:词频少于5次的单词被丢弃
sg: 采用skip-gram算法;
workers:训练过程中的并行数
训练结束后保存模型。
import multiprocessing
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
#模型的训练与保存
model = Word2Vec(LineSentence(input_file_name), size=200, window=5, min_count=5, sg=1, workers=multiprocessing.cpu_count())
model.save(model_file_name)
保存的词向量模型截图为:
通过工具Embedding projector对其进行可视化,工具的网址为:https://projector.tensorflow.org/。
选取其中100个词向量生成的三维图片如下所示:
总结
除了word2vec方法外,近年来利用BERT(Bidirectional Encoder Representation from Transformers)的预处理模型,能够得到更充分联系上下文的信息,从而提高在NLP中的处理效率。后面会对BERT模型的内容进行研究,并在此基础上进行实践。