资料来源:CS224n: Natural Language Processing with Deep Learning (Winter 2017) https://web.stanford.edu/class/archive/cs/cs224n/cs224n.1174/
声明:非原创,不用于商业用途,根据个人笔记习惯,进行整理改编自http://www.hankcs.com/?s=CS224n%E7%AC%94%E8%AE%B0
Lecture 2 -- Word Vector Representations: word2vec
1.词义 Word meaning (15 mins)
1.1 如何表示一个词语的意思 How do we represent the meaning of a word?
先来看看如何定义“meaning”的意思,英文中meaning代表人或文字想要表达的idea。这是个递归的定义,估计查询idea词典会用meaning去解释它。
中文中“意思”的意思更加有意思:
他说:“她这个人真有意思(funny)。”她说:“他这个人怪有意思的(funny)。”于是人们以为他们有了意思(wish),并让他向她意思意思(express)。他火了:“我根本没有那个意思(thought)!”她也生气了:“你们这么说是什么意思(intention)?”事后有人说:“真有意思(funny)。”也有人说:“真没意思(nonsense)”。(原文见《生活报》1994.11.13.第六版)[吴尉天,1999]
——《统计自然语言处理》
语言学中“meaning”近似于“指代、所指、符号”。
1.2 计算机如何处理词语的意思 How do we have usable meaning in a computer?
过去几个世纪里一直用的是分类 taxonomy 词典。计算语言学中常见的方式是 WordNet 那样的词库。比如NLTK中可以通过WordNet查询熊猫panda的上位词关系 hypernyms (is-a) relationships,得到“食肉动物”“动物”之类的上位词。也可以查询“good”的同义词——“honorable受人尊敬的”“ripe熟了”。
WordNet:A Lexical Database for English。是由Princeton 大学的心理学家,语言学家和计算机工程师联合设计的一种基于认知语言学的英语词典。它不是光把单词以字母顺序排列,而且按照单词的意义组成一个“单词的网络”。它是一个覆盖范围宽广的英语词汇语义网。名词,动词,形容词和副词各自被组织成一个同义词的网络,每个同义词集合都代表一个基本的语义概念,并且这些集合之间也由各种关系连接。WordNet包含描述概念含义,一义多词,一词多义,类别归属,近义,反义等问题。
1.3 离散表征的问题 Problems with this discrete representation
-
这种discrete representation虽然是种语言学资源,但丢失了细微区别 nuances。
- 比如这些同义词的意思实际上还是有微妙的差别:adept, expert, good, practiced, proficient, skillful
缺少新词
主观化
需要耗费大量人力去整理
-
无法计算准确的词语相似度 word similarity
- 比如 精通 比 好 更接近 专家
无论是规则学派,还是统计学派,绝大多数NLP学家都将词语作为最小单位。
事实上,词语只是词表长度的one-hot向量,这是一种localist representation(大概是借用localist“局部”的意项)。
在不同的语料中,词表大小不同。Google的1TB语料词汇量是1300万,这个向量的确太长了。
1.4 从符号表示到分布式表示 From symbolic to distributed representations
词语在符号表示上体现不出意义的相似性。

one-hot向量是正交 orthogonal 的,无法通过任何运算得到相似度。
1.5 基于分布相似性的表示 Distributional similarity based representations
You shall know a word by the company it keeps.——J. R. Firth
如果你能把单词放到正确的上下文中去,才说明你掌握了它的意义,这是现代统计自然语言处理 modern statistical NLP最成功的思想之一:

1.6 通过向量定义词语含义 Word meaning is defined in terms of vectors
给每一个单词构造一个向量,我们会选择一个密集向量 dense vector ,让它可以预测目标单词所在文本的其他词汇,所有其他词汇也是由一个个向量组成。
我们可以使用一些相似性方法,比如两个向量间的点积,以便于它可以预测两个单词的相似度。
通过某种聪明的算法实现递归 recursive,根据向量来调整向量,这样这些词汇就可以预测上下文的其他词汇。
分布式相似度 Distributional similarity,是一种关于词汇语义的理论,即你可以通过理解单词出现的上下文,来描述词汇的意思。
分布式表示 distributed representations (dense vector representation)与符号表示 symbolic representations(localist representation、one-hot representation)相对。
分布式表示和含义几乎总是通过分布相似性来构建的。
<u style="box-sizing: border-box;">[备注]这中间讲了一大段分布式和分布式表示的区别,但是没有听懂,也不是很重要。。。</u>
2.Word2vec的介绍 Word2vec introduction (20 mins)
2.1 学习神经网络单词嵌入的基本思想 Basic idea of learning neural network word embeddings
我们定义了一个模型,其目的是用词向量的方式来预测 中心词 wt 和 上下文 context 之间的关系(运用一些概率的方法,根据给定单词预测上下文单词出现的概率)
p(context|wt ) = …
损失函数 loss function定义如下:
J = 1 − p(w −t |w t )
理想状态下,我们可以准确预测 中心词 wt 周围的词,这里的w −t 表示围绕在 中心词wt 周围的其他词。如果我们可以根据 t 精准预测这些上下文,那么概率p 就为1,损失函数J 为0。
如果有25%的概率预测对,那就是说,我们有75%的概率的损失或失败,这就是一个损失函数的值。
我们将会在大型语料库 big language corpus的各个位置重复这样的操作,我们的目标是调整词汇向量表示 vector representations of words 从而最小化损失函数 minimize this loss**。
实际上,除了设置这样一个预测目标以外,也做不了其他的。只是让每个单词的向量都能预测其周围的词汇,反之亦然。通过深度学习训练后,这些词向量的输出 outputs 在表示单词意思方面如此强大, 在处理各种类似问题上也非常有用。
2.2 直接学习低维词向量 Directly learning low-dimensional word vectors
这其实并不是多么新潮的主意,很早就有一些研究了:
• Learning representations by 误差反向传播 back-propagating errors (Rumelhart et al., 1986)
• 神经概率语言模型 A neural probabilistic language model (Bengio et al., 2003) 中使用误差反向传播算法来做词汇表示
• NLP (almost) from Scratch (Collobert & Weston, 2008)
• A recent, even simpler and faster model: word2vec (Mikolov et al. 2013) 可以用来处理数十亿单词的文本,并生成非常棒的单词表示
只不过以前一直没有引起重视,直到Bengio展示了它的用处之大。后来研究才开始火热起来,并逐渐出现了更快更工业化的模型。
2.3 word2vec的主要思路 Main idea of word2vec
通过单词和上下文彼此预测 Predict between every word and its context words!
生成词汇向量两个算法:
Skip-grams (SG):预测上下文
Continuous Bag of Words (CBOW):预测目标单词
效率中等的两种训练方法:
Hierarchical softmax
Negative sampling 负采样
但在这门课里,只会讲Naïve softmax(效率低)。
两种算法和两种训练方法的四种组合原理推导和代码分析:http://www.hankcs.com/nlp/word2vec.html
2.4 Skip-gram预测 Skip-gram prediction

中心思想:在每一个估算步 estimation step都取一个词作为中心词汇 center word ,然后尝试去预测它一定范围内的上下文 context的词汇。
注意这里虽然有四条线,但这个模型中只定义一个概率分布 probability distribution,即给定一个中心词汇,某个单词在它上下文中出现的概率。(因为这只是个词袋模型而已,与位置无关)
Bag-of-words词袋模型:对于一篇文档来说,假定不考虑文档内的词的顺序关系和语法,只考虑该文档是否出现过这个单词。
我们会选取词汇的向量表示,学习就是要最大化这些概率分布值。这一个概率分布就是输出 output,也就是出现在中心词汇周围的上下文的一个输出。
个人理解:每个词,对于这个中心词而言,出现在周围,都有一个概率分布吧。那把所有的关于该中心词的周围概率分布拼在一块,形成的one-hot向量,再经过softmax就是输出。再把所有的中心词形成的向量拼在一起,就是一个矩阵。
2.5 word2vec细节 Details of word2vec
全部看完后,还是没看懂的话推荐一篇文章 https://www.jianshu.com/p/da235893e4a5
定义一个半径 m,窗口 就是围绕中心词的大小为 2m 距离的词汇(参照上图),中心词前后大小各为 m 。
在一篇文章中,遍历文本中的所有位置,对于每个位置,我们通过窗口大小内的上下文词汇得到了一个概率。
接下来就是设置模型的参数 parameter,让上下文中所有词汇出现的概率都尽可能的高。
公式里的 θ 就是模型的参数,也就是每个单词的向量表示的唯一参数。
每一个中心单词 t (1≤t≤T),目标函数定义为所有位置的预测结果的乘积:
个人理解,根据公式通俗地举个例子:m=2,T=100。假设第3个中心词为w3,在参数θ下,P(w1|w3)、P(w2|w3)、P(w4|w3)、P(w5|w3)这四个概率的乘积就是所要求的中心词w3关于上下文的概率,然后再将这100个中心词的概率累乘,就得到了目标函数。我们的目标呢,就是通过调整我们的参数θ要最大化这个目标函数。
注意:在这里m=2是超参数 hyper parameters,而不是参数,此处可以当做常量看待。

对如上的公式取对数,将所有的求积操作转换成求和操作,紧接着取每个位置上的平均值 1/T,相当于对每个词汇进行归一化处理 normalization 。
我们通常喜欢最小化问题,而不喜欢最大化问题。因此对已经取了对数的公式再加上负号,也就是得到了一个负对数似然 negative log likelihood,也就是模型的负对数分布。

2.6 目标函数细节 The objective function – details
这些术语都是一样的。损失函数 Loss function = 代价函数 cost function = 目标函数 objective function,不用担心用错了。
对于负对数似然来讲,常用的损失函数为交叉熵 Cross-entropy loss。
对于one-hot的wt+j目标,诀窍就是只预测一个当前的单词,在这个准则下,交叉熵损失函数里剩下的只有一项,那就是负对数似然。
2.7 Word2Vec细节 Details of Word2Vec
预测每个单词的窗口范围(半径为m)内的上下文单词。
对于p(wt+j|wt),最简单的公式就是Softmax函数。

o 是输出的上下文词语中的确切某一个的索引,c 是中心词汇的索引 indices,都是指针对整个词汇表空间。
uo 是对应的上下文词向量(输出向量 output vector),vc 是词向量(中心向量 center vector)。
2.8 点积 Dot products
复习一下课程开头所说的baby math:

首先由两个单词向量,求他们的点积,也就是取出相应的项,求他们的积,然后将所得的乘积求和。得到了两个向量的点积之后,将他们转换成Softmax形式。
这其实就是一种粗糙的相似度计算方法,两个向量的相似度越高,点积 dot products 就越大。
2.9 Softmax:从实数空间到概率分布的标准映射方法 Softmax function: Standard map from ℝV to a probability distribution

指数函数一定可以把实数映射成正数,为求解概率分布提供了一个很好的基础。然后归一化得到概率。
softmax之所叫softmax,是因为指数函数会导致较大的数变得更大,小数变得微不足道;这种选择作用类似于max函数。
提问:其实未必非得用softmax,只要能保证是正的才方便计算概率,如果正数很多,相加除以总数也是可以的。
提问:用两个向量而不用一个,因为者两个表示相互独立,当做优化的时候,不会相互耦合,在实际中也更好操作,所以每个单词都有两个向量。
提问:这个模型与中心词的位置无关,只有在词汇表中的ID有关。
2.10 Skipgram

别看这张图有点乱,但其实条理很清晰,基本一图流地说明了问题。
wt 是one-hot向量,代表了一个词。(事先对单词的编码,是ID不是位置)Vx1,V行1列,比如说该文本中有10000个不同的词,那么V=10000,其中只有一个位置为1,其余位置为0。
W 是由所有中心词汇的向量表示组成的矩阵 word embedding matrix,也就是隐藏层权重矩阵,也就是最终我们所需要的参数θ。矩阵大小为dxV,d行V列,d一般是自己取值,可以取300,可以理解为隐藏层神经元个数。那么该矩阵就是300x10000,300行,10000列,每一列代表每个单词的300维的中心词向量,每一行代表一个10000维的词向量和隐藏层单个神经元连接的权重向量。
vc=Wwt 是根据 wt 与 W相乘之后的结果,实际上不需要矩阵相乘操作,只需要在矩阵W中找到相对应的列即可。vc 就是我们所需要的中心词的向量表示。dx1,d行1列。
W' 是上下文单词矩阵context word matrix ,也就是输出层权重矩阵。矩阵大小Vxd,与W相反。
W'vc = [uxTvc] 相当于中心词vc 和词汇表中所有上下文单词都分别求内积,每个上下文单词是一个概率,把所有上下文单词概率拼在一起就组成一个向量。Vx1,V行1列,也就是一个10000维的向量,正好对应这个中心词和所有单词表内的词的相似度。
p(x|c)=softmax(uxTvc),求出来的概率可能包括负值,因此选择softmax,得到一个满足概率分布的矩阵。
最后与与真实值进行对比,计算损失即可。
[备注]该图与PPT上的图不一致。
Word2Vec的整个建模过程实际上与自编码器(auto-encoder)的思想很相似,即先基于训练数据构建一个神经网络,当这个模型训练好以后,我们并不会用这个训练好的模型处理新的任务,我们真正需要的是这个模型通过训练数据所学得的参数,例如隐层的权重矩阵。训练完成后,我们会将输出层“砍掉”,仅保留隐层。
隐藏层没有使用任何激活函数,但是输出层使用了sotfmax。
官方笔记里有非手写版,一样的意思:

这两个矩阵都含有V个词向量,也就是说同一个词有两个词向量,哪个作为最终的、提供给其他应用使用的embeddings呢?有两种策略,要么加起来,要么拼接起来。在CS224n的编程练习中,采取的是拼接起来的策略:
# concatenate the input and output word vectors
wordVectors = np.concatenate((wordVectors[:nWords,:], wordVectors[nWords:,:]),axis=0)
# wordVectors = wordVectors[:nWords,:] + wordVectors[nWords:,:]
他们管W中的向量叫input vector,W'中的向量叫output vector。
2.11 训练模型:计算参数向量的梯度 To train the model: Compute all vector gradients!
把所有参数写进向量θ,对d维的词向量和大小V的词表来讲,有:

由于上述两个矩阵的原因,所以θ的维度中有个2。模型的学习当然是梯度法了。
3.研究展示 Research highlight (Danqi) (5 mins)
Paper:A Simple but Tough-to-beat Baseline for Sentence Embeddings (ICLR 2017)
在自然语言处理中,核心问题就是我们怎样才能定义可以编码句子含义的向量表示 Sentence embedding。
有了句子表示后,我们可以通过求解两个向量的内积 inner product来计算句子相似性sentence similarity。
我们还可以将句子表示作为特征去处理一些句子分类任务 sentence classification,例如情感分析。
构造句子向量表示,最简单的方法就是使用词袋 Bag-of-words (BoW)。就是求出词的向量表示,然后取平均值。
这篇论文本质上给出的是一个非常简单的无监督模型:加权词袋weighted Bag-of-words 句子表示 + 移除一些特定方向 remove some special direction。
第一步:就是计算向量表示的平均值,不过每个单词都有一个独立的权重。这里a是一个常数,p(w)表示该单词出现的频率。这意味着平均表示除以单词频率的权重。

第二步:我们算出所有这些句子的向量表示后,我们计算第一主成分,然后减去第一主成分上的投影。

他们计算上下文的句子相似性,证明这个简单的方法远远好于其他所有方法。还有对于一些监督学习任务,比如句子分类,方法依然优秀。
4.目标梯度函数 Word2vec objective function gradients (25 mins)
有用的基础知识:(后面白板上有解释这个公式)

链式规则:如果y = f(u) 并且 u = g(x),那么等价于 y = f ( g( x ) )

推导过程:最大化目标函数也就是最小化负对数似然。
每个单词有两个向量表示,一个是中心词center word向量,记为v,一个是上下文词context word向量,记为u。

这里是对向量求偏导,而不是对变量求偏导。



视频里的提问挺有意思的,这里没有做记录。
更清晰的公式参考:http://www.hankcs.com/nlp/word2vec.html#h3-5
5.损失/目标函数 Cost/Objective functions
梯度有了,参数减去梯度就能朝着最小值走了。
5.1梯度下降 Gradient Descent
公式只是一个位置更新公式。θnew是新的位置,θold是旧的位置,α是步长,也叫学习率。朝哪个方向走呢,就是J(θ)关于θold的偏导数,也就是损失函数。
因此根据梯度下降公式,已知当前的参数值,计算出这个位置的梯度值,从中选取一定的步长,可以得到新的参数值,这个新的参数值可以让目标函数的值变小一点,我们就是朝着最小值的方向移动。

5.2 普通梯度下降代码 Vanilla Gradient Descent Code
while True:
theta_grad = evaluate_gradient(J , corpus , theta) # theta_grad 是J(θ)关于θ的偏导
theta = theta - alpha * theta_grad # theta 是新的参数
5.3 直观讲解 Intuition
对于两个参数上的简单凸函数。等高线表示目标函数的级别。

5.4 随机梯度下降法 Stochastic Gradient Descent(SGD)
当数据特别大时,做一次简单的梯度更新都需要非常久的时间。
因此我们采取随机梯度下降法。只选取文本的一个位置,有了一个中心词汇,以及它周围的词,我们每移动一个位置,对所有的参数求解梯度,然后使用这个位置的梯度估计值,这样就前进了一小步。如果你想想用它来处理单词向量学习之类的事情,这种梯度估计是及其及其粗糙的,因为,我们只在一个位置这么做,这个位置周围只有几个单词,我们就看不到大部分参数,这就是一种非常粗糙的梯度估计。
while True:
window = sample_window(corpus) # 选取部分点
theta_grad = evaluate_gradient(J , window , theta) # theta_grad 是J(θ)关于θ的偏导
theta = theta - alpha * theta_grad # theta 是新的参数
神经网络算法喜欢噪音。
但是这种方法有两个优点,一个是计算速度快。比如说可以从百万级的数据中提取100个数据点进行计算,算一个不是那么准确的数,时间节约了近10000倍,就算多走了几倍的路也值得。还有一个优点是能够自动逃离比较差的局部最优点,实际情况下比GD效果更好。