说在前面:
例如词汇表大小 ∣ V ∣ = N |V|=N ∣V∣=N,则用一个 N N N维的one-hot向量来表示一个词,每个词的one-hot中 1 1 1的位置就对应了该词在词汇表的索引。
Word2Vec包括Skip-Gram(SG)和CBOW。SG模型需要根据target来预测上下文的词(即target左右的词,称为context);而CBOW相反,需要根据context来预测target,准确来说,是使用规定窗口范围内的context的平均(或求和)来预测target。
SG的训练过程:
当window size为2时,表示取center的左右各两个context。那么从Coupus中的一个句子构造训练样本的过程可以图示为如下(只演示到target为fox就停止了)。
这里需要特别注意SG和CBOW的区别,其实他们的本质区别不在于用context来预测target还是用target来预测context。例如,如果以同样的句子来构造CBOW的样本,假设我们不对context取平均(这并不是真正的CBOW!),而是每个context都构造一个
其实这一现象在StackOverflow上也有讨论:CBOW v.s. skip-gram: why invert context and target words?。
那么SG的CBOW的区别在哪?没错,就是在对context取平均。不信?来看看FastText的官方文档是怎么介绍这两种word2vec模型的。(P.S. FastText由Tomas Mikolov团队开发,这一团队正是在2013年提出word2vec的团队。)
引用链接: skipgram versus cbow
FastText provides two models for computing word representations: skipgram and cbow (‘continuous-bag-of-words’).
The skipgram model learns to predict a target word thanks to a nearby word. On the other hand, the cbow model predicts the target word according to its context. The context is represented as a bag of the words contained in a fixed size window around the target word.
Let us illustrate this difference with an example: given the sentence ‘Poets have been mysteriously silent on the subject of cheese’ and the target word ‘silent’, a skipgram model tries to predict the target using a random close-by word, like ‘subject’ or ‘mysteriously’. The cbow model takes all the words in a surrounding window, like {been, mysteriously, on, the}, and uses the sum of their vectors to predict the target. The figure below summarizes this difference with another example.
可以看到,FastText文档在介绍SG和CBOW时,都介绍为“根据nearby word/context来预测target word”,看下面的图,也可以发现这里讲的SG是用context来预测target。是不是作者搞错了?肯定不是,作者所在团队就是提出这些模型的团队,现在在Facebook的AI research团队,怎么可能搞错?实际上,我在前面已经解释了,用context来预测target和用target来预测context得到的训练集几乎没有区别,所以这篇文章在介绍两个模型时都直接使用了“用context来预测target”这一语言模型任务。但是SG和CBOW是有区别的,那就是对context取平均这一操作。这一操作会带来什么影响呢?
Skip-gram: works well with small amount of the training data, represents well even rare words or phrases.
CBOW: several times faster to train than the skip-gram, slightly better accuracy for the frequent words
原始Word2Vec的问题以及解决办法:
超参数的选择对Word2Vec的效果影响 [ 1 , , 7 , 8 ] ^{[1,,7,8]} [1,,7,8]:
负采样原理见本小节(后面省略,默认指对应小节的)学习资料1,分层softmax(后称h-softmax)原理见学习资料5,代码实现见学习资料6。
要点:
下面讨论时,以CBOW为背景,即我们要根据多个context的embedding的平均作为输入,来预测center。
负采样和分层softmax都是将原来的多分类问题(设类别数为 V V V)转化为了 k k k个二分类问题,其中 k k k远远小于 V V V。(在负采样中, k k k是固定的,一般在2~20中取;在h-softmax中, k k k是变化的,平均情况下, k = l o g V k=logV k=logV)。
我们仍然有两个参数矩阵, W W W和 W ′ W^\prime W′。 W W W就是context的lookup table,即 W W W的每一行/列就对应了一个单词的embedding。而 W ′ W^\prime W′在两种方法中作用不一样:在负采样中 W ′ W^\prime W′是center的lookup table;在h-softmax中, W ′ W^\prime W′存放的是 V − 1 V-1 V−1个二分类器的参数。
负采样时,对于1个正确的 < c o n t e x t , c e n t e r >
h-softmax中,我们对于 V V V个词汇(类别)根据其在corpus中出现的频率进行哈夫曼编码,那么对应的哈夫曼树有 V V V个叶子结点,分别对应了每一个词汇,并有 V − 1 V-1 V−1个内部结点,对应了 V − 1 V-1 V−1个二分类器(每个分类器的参数就是 W ′ W^\prime W′的每行/每列,i.e., h-softmax中的 W ′ W^\prime W′含义不再是center的lookup table)。
例如上图中的center词汇 y 2 y_2 y2对应的哈夫曼编码为 110 110 110(假设规定是向左为1),那么我们从 W ′ W^\prime W′中得到该条路径上的3个非叶子结点对应的向量 W k 1 ′ , W k 2 ′ , W k 3 ′ W^\prime_{k1},W^\prime_{k2},W^\prime_{k3} Wk1′,Wk2′,Wk3′,那么我们就可以得到3个二分类问题: ( W i T W k 1 ′ , 1 ) , ( W i T W k 2 ′ , 1 ) , ( W i T W k 3 ′ , 0 ) (W_i^TW^\prime_{k1}, 1),(W_i^TW^\prime_{k2}, 1),(W_i^TW^\prime_{k3}, 0) (WiTWk1′,1),(WiTWk2′,1),(WiTWk3′,0) 。
代码分析:
'''
下面的代码以CBOW为基础,提供negative sampling和h-softmax两种训练方法
代码中的syn0与syn1分别指的是前文所提到的W和W`
'''
# 计算neu1,即多个context词汇的embedding取平均
neu1 = np.mean(np.array([syn0[c] for c in context]), axis=0)
assert len(neu1) == dim, 'neu1 and dim do not agree'
# Init neu1e with zeros
neu1e = np.zeros(dim)
# Compute neu1e and update syn1
if neg > 0: # neg训练时传入的参数,指的是负样本个数,大于0代表使用负采样
# token是真实的center,target是采样得到的center,一共有(neg+1)个二分类器
classifiers = [(token, 1)] + [(target, 0) for target in table.sample(neg)]
else: # 使用h-softmax
# path是由路径上的内部结点的编号所组成的list
# 例如给前面例子中y2对应的内部结点编号1、2、4,则path=[1,2,4],后面将作为W`的索引使用
# code是token(即真实center)的哈夫曼编码,对应了每个内部结点应该向左还是向右
# 例如前面例子中y2的哈夫曼编码为110,则code=[1,1,0],后面将作为二分类的label使用
classifiers = zip(vocab[token].path, vocab[token].code)
for target, label in classifiers: # 对每个二分类器计算损失并更新syn1(即W`)的参数
# 逻辑斯谛回归的前向和反向传播
z = np.dot(neu1, syn1[target])
p = sigmoid(z)
g = alpha * (label - p)
neu1e += g * syn1[target] # Error to backpropagate to syn0
syn1[target] += g * neu1 # Update syn1
# 更新syn0(即W)的参数
for context_word in context:
syn0[context_word] += neu1e
h-softmax和负采样的(实验)效果对比 [ 7 , 8 ] ^{[7,8]} [7,8]:
对于 “hierarchical softmax对生僻词很不友好” 的观点,我专门写了一篇博客来反对:hierarchical softmax对生僻词很不友好?扯淡!
在word2vec中,我们需要计算target与context(分别记为 w w w和 c c c)的相似度score,一般使用内积,即 s ( w , c ) = u w T v c s(w,c)=u_{w}^Tv_{c} s(w,c)=uwTvc( u 、 v u、v u、v分别来自 W 、 W ′ W、W^\prime W、W′两个lookup table)。
在FastText中,每个词w都是由字符级的n-gram加上
假设我们由corpus得到的character n-gram加上
另外,FastText除了训练词向量,更重要的一个功能是可以文本分类。
FastText特点小结如下:
1、对于一个词,将其字符级 n-gram 的Embedding与该词的Embedding求和作为原词的最终Embedding,作用:
2、为了节省内存,对哈希到同一个位置的字符n-gram使用相同的Embedding,哈希函数使用的是FNV函数(具体来说是衍生版本Fowler–Noll–Vo 1a).
3、在分类任务中,使用词语级别的n-gram,与Text-CNN很类似,都是基于n-gram理论,可以捕捉到词序信息
在统计共现矩阵 X i , j X_{i,j} Xi,j之后,对其进行奇异值分解(SVD),得到的左奇异矩阵就是我们想要的Embedding矩阵。但是对一个巨大的共现矩阵进行SVD的代价很大,所以我们想办法利用神经网络的方式将共现矩阵“分解”(用神经网络来拟合共现矩阵中的信息)。
Glove先从corpus统计共现矩阵,然后使用神经网络来拟合共现矩阵,目标函数如下:
最后我们对于一个词 w w w的target embedding和context embedding,就是它对应的 v i v_i vi和 v j v_j vj求和( v i v_i vi和 v j v_j vj来自不同的lookup table)。
权重函数的设计:
最后 f ( x ) f(x) f(x)的设计如下,论文中的 x m a x x_{max} xmax取100, α \alpha α取0.75:
不难发现其实所有的Embedding方式都是Transfer Learning的模式,先在预训练(pre-training)阶段利用语言模型(LM)或其他任务训练得到词的representation,然后在下游任务中,只需要将之前训练好的Embedding Matrix(lookup table)加载到下游任务的Embedding Layer,然后,既可以fix(frozen)Embedding层的参数,也可以将这些参数与下游任务的其他参数一起训练,即fine-tune的过程。注意,pre-training阶段一般是利用LM,可以利用大量无标签的corpus,即unsupervised的方式;而下游任务一般是supervised的,即一般使用有标签的corpus。
只不过,之前介绍的Embedding方法有个最大的缺陷就是,它们都只能为每个词学习到单个固定的,因此也是上下文无关(context independent)的representation。然而一个单词,可能有多种词性或者多种词义,甚至每种词性都有多种含义。我们希望得到embedding可以很好地对一词多义现象进行建模,包括语法( syntax)和语义(semantics)两方面,即多种词性、多种语义。
第一个做到这点(并为大家所熟知)的是ELMo模型,后面相继出了GPT、BERT、XLNet等模型。这些模型都需要面对两个难题:
预训练阶段应该使用哪些tasks或optimization objectives?
目前尚不清楚使用何种task来学习文本的representation在迁移到下游任务时最有效,为了预训练模型的通用性,目前大家一般用language model作为预训练阶段的task
ELMo的task是标准的LM,只不过ELMo训练了两个LM—— 一个forward LM
,一个backward LM(在BERT的论文中被称为left-to-right LM和right-to-left的LM)。
(图片来源:A Step-by-Step NLP Guide to Learn ELMo for Extracting Features from Text)
给定一个N个token的序列,forword LM就是给定前 k k k个token,预测序列中的后一个token,用公式来表达它对句子分布的建模如下:
而backward LM类似,只是方向相反,即给定后 k k k个token,模型需要预测序列中的前一个token,用公式来表达它对句子分布的建模如下:
特别需要指出的是,ELMo中,每个LM在预测下一个token(后一个或前一个)时,都没有利用到句子的另一半段/另一个方向/另一个LM的信息,这一点与BERT很容易混淆,需要注意区分。
GPT的task也是标准的LM,只不过与ELMo的区别有二:
细节见学习资料。
BERT的task有两个:
一是Masked LM(MLM),随机选择输入序列中15%的WordPiece(设为 T i T_i Ti),然后使用这15%的WordPiece的输出用于预测输入 T i T_i Ti。另外,为了缓和pre-training与fine-tuning阶段的mismatch(pre-training阶段有[MASK]字符,而fine-tuning阶段没有),对于选中的15%,按80%概率将其替换为特殊字符[MASK],按10%概率将其替换为一个随机的token,按10%概率不做任何替换。
二是 Next Sentence Prediction (NSP),在将两个序列用特殊字符[SEP]拼接起来,然后在开头加上特殊字符[CLS]表示分类(classification),然后使用[CLS]对应的输出用于预测这两个序列是否是相邻的。
如何将得到的representation运用/迁移到下游任务中?目前尚无公认最好的方式,其中流行的有两种策略,分别是feature-based和fine-tuning.
feature-based: 固定预训练模型的参数,抽取预训练模型的hidden states作为下游任务的额外/辅助的特征(additional / auxiliary features),与下游任务的embedding拼接起来。优点在于非常灵活,几乎不必改动下游任务的网络架构,所以适用于所有下游任务。
fine-tuning: 在预训练模型的适当位置加上分类层(FFNN+Softmax),直接将其作为下游任务的网络架构,并将下游任务的数据集做适当的转换使其符合预训练模型的输入方式。优点在于需要train from scratch的参数非常少,所以需要的训练时间更少,也往往效果更好,特别是下游任务数据集较小时。
feature-based示意图(ELMo):
(图片来源(右边部分经修改):Improving a Sentiment Analyzer using ELMo)
fine-tuning示意图(GPT与BERT):
下面表格是一个小结。
特征提取器 | 预训练阶段的任务 | 将得到的representation运用到下游任务的策略 | |
---|---|---|---|
ELMo | 2个单向双层 LSTM | biLM: forward LM + backward LM | feature-based |
GPT | Transformer Decoder | unidirectional LM | fine-tuning |
BERT | Transformer Encoder | Masked LM + Next Sentence Prediction | fine-tuning |
注意:
Vector Space Model(VSM),即向量空间模型,使用一个向量表示一个文本。每一维都代表一个词。如果某个词出现在了文档中,那它在向量中的值就非零。有几种常见方法计算这个值:
Bag-of-Words(BoW)又叫词袋模型,用于文档特征表示。BoW 忽略文本的语法和语序,用一组无序的单词来表示这段文本。即 BoW 不会保留单词在句子里的顺序/位置信息。
TF-IDF(term frequency–inverse document frequency)是一种统计方法,用以评估一个字词对于其所在的文档的重要程度。字词的重要性随着它在文档中出现的次数成正比,但同时会随着它在语料库(所有文档)中出现的频率成反比。为了反映这种思想,TF-IDF由TF与IDF相乘得到,TF表示词频,IDF表示逆文档频率。
常用的VSM文本表示模型中有两个主要的缺陷:
LSI的基本思想是文本中的词与词之间不是孤立的,存在着某种潜在的语义关系,通过对样本数据的统计分析,让机器自动挖掘出这些潜在的语义关系,并把这些关系表示成计算机可以”理解”的模型。它可以消除词匹配过程中的同义和多义现象。它可以将传统的VSM降秩到一个低维的语义空间中,在该语义空间中计算文档的相似度等。总的说来,LSI就是利用词的语义关系对VSM模型进行降维,并提高分类的效果。