文本表示模型
文本表示模型可分为以下几种:
- 基于one-hot, tf-idf, textrank等的bag-of-words;
- 基于计数的,主题模型,如LSA, pLSA, LDA
- 基于预测的,静态词嵌入,如Word2Vec, FastText, Glove
- 基于大规模预训练的,动态词嵌入,如BERT, ELMo, GPT, T5
本文讲解第三种“静态词嵌入”。
静态词嵌入
词嵌入是一类将词向量化的模型的统称,核心思想是将每个词都映射成低维空间上的一个稠密向量(Dense Vector)。
Word2Vec
Word2Vec是一个浅层神经网络,包含Skip-gram和CBOW两种模型结构。
首先设置一个固定大小的上下文窗口C。(用一个固定的上下文窗口意味着可以处理任意长度的句子)
- 如果是用一个词作为输入,来预测它上下文中的任意词,那这个模型叫做『Skip-gram 模型』
- 如果是用一个词语的上下文中的各个词作为输入,来预测这个词语本身,则是『CBOW 模型』
注:在CBOW中,需要将各个输入词所计算出的隐层单元求和。(如下右图)
CBOW的优势是训练时间短,每个词只参与一次预测,反向传播时梯度经由累加后的隐层单元传递到上下文窗口中的每一个词;
Skip-gram的优势是词嵌入效果好,尤其是低频词,反向传播时每预测一个上下文窗口中的词就把梯度传递到中心词,而不是等所有词处理完后再传递到中心词。
Skip-gram | CBOW |
---|---|
![]() | ![]() |
如何训练Word2Vec并计算词嵌入?
Word2Vec模型以词的 one-hot 向量为输入,采用 softmax 输出多分类预测结果(判断目标词),以目标词的最大对数似然估计为学习目标,如下以Skip-gram公式为例。
![]() | ![]() |
构造上述的监督学习模型,并不是解决这个监督问题本身,而是以这个监督问题为模型的学习目标,学得更好的词嵌入。
词嵌入是Word2Vec网络模型训练的副产物,是网络模型的输入层到隐藏层(或隐藏层到输出层)的权重矩阵,可看作一个大小为𝑑 × |𝑉|的词表,其中|𝑉|表示词表大小,𝑑表示词嵌入维度。随着Word2Vec模型的训练,模型逐渐优化,权重矩阵(词嵌入)也逐渐变好。训练结束后,用权重矩阵和词的one-hot向量相乘,就能查找到这个词的词嵌入。
注:Word2Vec网络模型的输入层单元数为|𝑉|,隐藏层单元数为𝑑,输出层单元数为|𝑉|。
以查找“阿”这个字的词嵌入为例,假设它是词表中第三个字,它的 one-hot 向量是
[
0
,
0
,
1
,
0
,
…
,
0
]
T
[0,0,1,0,…,0]^T
[0,0,1,0,…,0]T,将上述的参数矩阵与这个one-hot向量相乘,就得到了“阿”这个字的词嵌入。细节见下图。此外,如果待查找的词不在字典中,就用“<unk>
”代替,查找“<unk>
”的词嵌入。
注:具体实现时并不是用矩阵相乘来查找词向量,矩阵相乘效率很低,而是使用一个专门的函数来查找矩阵的某列。例如在Keras中就有一个嵌入层,用这个嵌入层更有效地从嵌入矩阵中提取出需要的列。
如何优化Word2Vec的性能?
上文提到,Word2Vec有两个地方需要大量的计算
- 隐藏层神经元和输出层权重矩阵W_out的乘积
- Softmax 分母项需要对词汇表中所有词求和,词表越大,softmax 计算越慢。
针对Word2Vec的性能优化,通常采用层次化 softmax(Hierarchical Softmax)和 负采样(Negative Sampling)两种优化方案,后者效果更好。
Hierarchical Softmax
在层次化 softmax 中,根据单词的频次构建哈夫曼树,把原先的一个|𝑉|分类问题优化为log (|𝑉|)个二分类问题,如下图所示。在计算目标单词(某个叶子节点)的概率时,只需计算从根节点到该节点的路径上的节点的条件概率乘积,并且在反向传播时,只更新这条路径上的节点。
由于哈夫曼树的特性,高频词非常接近树根,使得越常见的单词,检索路径越短。进一步缩短了计算耗时。
![]() | ![]() |
![]() | ![]() |
Negative sampling
在负采样中,给定一个中心词,从上下文窗口中选取一个词与它组成正样本,标签为1;再在词表中随机选取 𝑘 个词与它组成负样例,标签为0。从而将原来的多分类问题转换为一组二分类问题,同时将预测空间从原先的|𝑉|缩小至𝑘 + 1。
注:Mikolov等人推荐小数据集的 𝑘 值设置为5到20比较好,数据集越小 𝑘 就越大。对于较大的数据集,𝑘 值就等于2到5。
注:如果从词表中负采样随机选到的词,正好属于上下文窗口内,比如说是上下文词orange前后10个词之一,也是没关系的。
负采样时单词被选取的概率和它的频次成正比,但并不是直接使用频次,而是取0.75次幂,公式如下:
公式中的f(w)
表示词w在语料中的频次,相比于直接使用频次,取0.75幂的好处可以减弱不同频次差异过大带来的影响,使得小频次的单词被采样的概率变大。
反向传播时,要记得是将多份梯度累加起来。
负采样的底层实现:新建一个包含M个元素的数组,M远大于词表大小|V|,从前往后遍历词表中的词 w i w_i wi,用 w i w_i wi在词表中的索引号i,填充新数组的第 r o u n d ( p ( w i − 1 ) ∗ M , 0 ) + 1 round(p(w_{i-1}) * M, 0)+1 round(p(wi−1)∗M,0)+1到第 r o u n d ( p ( w i ) ∗ M , 0 ) round(p(w_i) * M, 0) round(p(wi)∗M,0)个数组元素。有了这个数组以后,每次进行负采样时,只需要在0-M范围内生成一个随机数,然后选择数组中索引号为这个随机数的那个单词作为negative word即可。
FastText
FastText是Facebook提出的,有着和Word2Vec相同的训练目标:利用局部上下文信息,学习词的向量表示。
FastText的粒度更细,它采用char为训练对象,而不是词,它把一个词表示为多个char的组合。比如,把“体育”按2-gram拆分为“<体”,“体育”,“育>”。因此,它可以学到词的char级形态,它比Word2Vec更擅长词法类比,比如“体育运动”和“体育”,却更不擅长语义类比,比如“长跑”和“马拉松”。
还有一个优点,可以推测未见过的词的意思,缓解OOV问题。比如通过已见过的“体育”这个词,推断未见过的“体育运动”的含义。而Word2Vec无法推测未见过的词。
GloVe
GloVe是斯坦福提出的,把语料的全局统计特征(词共现)和局部统计特征(词的上下文)结合在一起。前者是LSA中所做的,后者是word2vec所做的。
为了学习词嵌入,GloVe首先构建一个非常大的word-word共现矩阵 X
(词w_j出现在词w_i的上下文窗口中的次数)),并以两个GloVe词向量的内积近似等于这两个词的共现频次的对数值为学习目标,例如Vector(cat).Vector(dog) = log(10),采用Adagrad对最小平方损失进行优化,公式如下。
J = ∑ i , j = 1 V f ( X i j ) ( w i T w ~ j + b i + b ~ j − l o g X i j ) 2 J=\sum_{i,j=1}^{V}{f(X_{ij})(w_i^T \tilde{w}_j+b_i+\tilde{b}_j-logX_{ij})^2} J=i,j=1∑Vf(Xij)(wiTw~j+bi+b~j−logXij)2
其中 w w w是中心词向量, w ~ \tilde{w} w~是上下文词向量,二者只是随机初始化值不同,Glove词嵌入是把这两个向量的相加,这样做的优点是缓解过拟合;b是一个常数向量;f(·)函数是一个加权函数,和Word2Vec中一样,用来减弱不同频次的差异,f(·)函数公式如下:
f ( x ) = { 0 if x=0 x ( 3 / 4 ) x m a x ( 3 / 4 ) 0 < x < x m a x 1 otherwise f(x)= \begin{cases} 0& \text{if x=0}\\ \frac{x^{(3/4)}}{{x_{max}}^{(3/4)}}& 0<x<x_{max}\\ 1& \text{otherwise} \end{cases} f(x)=⎩⎪⎨⎪⎧0xmax(3/4)x(3/4)1if x=00<x<xmaxotherwise
其中,原论文中取 x m a x 的 值 x_{max}的值 xmax的值为100。
Reference: