【NLP CS224N笔记】Lecture 2 - Word Vector Representations: word2vec

I. Word meaning

Meaning的定义有很多种,其中有:

  • the idea that is represented by a word,phrase,etc.
  • the idea that a person wants to express by using words, signs, etc.

1.Discrete representation

那么在计算机中是如何获取一个word的meaning的呢?常见的解决办法是使用像WordNet之类的数据集,它包含了同义词(synonym)组和上位词(hypernyms)组。这种表示方法属于Discrete representation

上位词(hypernym),指概念上外延更广的主题词。 例如:”花”是”鲜花”的上位词,”植物”是”花”的上位词,”音乐”是”mp3”的上位词。上位词是相对某主题词的,也有它自己的等同词、上位词、下位词、同类词。

但是类似于WordNet的数据集存在如下缺点:

  • 尽管存储的词条较为丰富,但是词与词之间缺少细微的差别。例如proficient只是good的同义词,但是二者却存在一些差别。
  • 缺少新的词汇,例如Dama(大妈)这种非常fashion的词汇很难及时地更新。
  • 对词的定义较为主观,因为都需要人工提前设定。因此也需要大量的人力去维护这个数据集。
  • 很难计算词之间的相似性。

2.将words表示为离散符号(discrete symbols)

如何将单词量化成计算机能读懂的数据呢?常见的一种方法是one-hot编码。

例如假设我们的数据集一共有6个单词,其中有motel(汽车旅馆)hotel

那么我们可以作如下表示:

  • motel=[0 0 0 0 1 0]
  • hotel=[0 0 0 0 0 1]

但是这有一个很明显的缺点就是词与词之间没有关联性,也就是说我们无法从one-hot编码中得知不同词之间的相似性,因为各个词编码后得到的向量是正交的。

所以我们还需要找到一种能够计算单词相似性,相关性的编码方式。

3. Distributed similarity based representation

一个很自然,很直观的方法就是根据某个单词的上下文对该单词的含义进行编码。该方法的核心思想是:A word’s meaning is given by the words that frequently appear close-by,由J.R.Firth在1957年提出。

下图给出示例,假如我们需要对banking的含义进行编码,那么我们可以根据预先设定的固定大小的窗口对banking前后出现的单词进行统计,banking的含义可以有这些单词表示。

II. Word2vec Indtroduction

1. 学习神经网络word embeddings的基本思路

我们先定义这样一个模型,该模型是在word vectors编码的基础上能够预测出中心词\(w_t\)的上下文:

\[p(context|w_t) \]

该模型的损失函数为

\[J=1-p(w_{-t}|w_t) \]

\(w_{-t}\)表示出了t以外的其他词,即\(w_t\)的上下文。

2. word2vec的核心思想

word2vec的核心思想是predict between every word and its context words!

两个算法:

  • Skip-grams (SG):给定目标词汇去预测它的上下文。简单地说就是预测上下文
  • Continuous Bag of Words (CBOW):从bag-ofwords上下文去预测目标词汇

两个稍微高效一点的训练方法:

  • Hierarchical softmax
  • Negative sampling

该课程主要集中讲解Naive softmax,上面的两个算法和训练方法可以参考word2vec原理推导与代码分析

3. Skip-gram prediction

下图给出了skip-gram示意图。中心词banking位置为\(t\),用\(w_t\)表示。窗口大小为\(m\)

需要注意的是虽然banking的上下文可以分为前面和后面,但是概率分布只有一种,即假设banking左边第三个单词是as,右边第二个是as,这两个as概率分布是要合并计算的,不存在说分前后两种概率分布。

image.png

4. Word2vec细节

1)目标函数

介绍完模型后下面需要介绍一下目标函数,函数形式如下:

\[\begin{align} max\,\,J'(\theta)=\prod_{t=1}^T\,\,\,\prod_{-m≤j≤m,\,\,j≠0}p(w_{t+j}|w_t;\theta) \end{align}\]

  • \(J'\)表示最优值
  • \(t\)表示当前中心词所在位置,\(T\)表示词库的总长度
  • \(m\)表示窗口的大小,\(j\)表示从窗口最左边移动到最右边,不等于0是因为不用计算中心词本身
  • \(\theta\)表示word representation模型本身
    上式表示尽可能地预测出每个中心词的上下文,即最大化所有概率的乘积。

通常为了方便计算会将上式化为log的形式,即

\[\begin{align} min \,\,\,J(\theta)=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,p(w_{t+j}|w_t;\theta) \end{align} \]

2)引入softmax

那么如何计算\(p(w_{t+j}|w_t)\)呢?为方便说明先做如下定义:

  • \(v_w\)表示w是一个中心词
  • \(u_w\)表示w是一个上下文词

所以\(p(w_{t+j}|w_t)\)简写成\(p(o|c)\),且有

\[\begin{align} p(o|c)=\frac{exp(u_o^Tv_c)}{\sum_{w=1}^Vexp(u_w^Tv_c)} \end{align} \]

  • \(o\)表示output或outside之意,即上下文。\(u_o\)用来表示某个需要计算上下文词汇
  • \(c\)表示center,\(v_c\)就表示中心词
  • \(V\)表示词库的长度,\(w\)从1开始遍历到\(V\)

公式(3)中的分子的点积运算,他可以用于计算中心词和上下文词的相似性,值越大表示越相似。

3)流程示意图

image.png

上图虽然乍一看很凌乱,但是很清晰地展示了skipgram的计算过程。

从左往右看:

  • 1.首先是维度为\(V×1\)的向量\(w_t\),用来表示此时中心词所在的位置,用one-hot形式进行编码,其中\(V\)表示需要遍历计算的中心词的个数,即总共的词数量。
  • 2.之后是维度为\(d×V\)的单词矩阵\(W\),该矩阵存储了所有中心词(center word)的向量表达,\(d\)表示用于表示词的向量的长度。
  • 3.\(w_t\)\(W\)做矩阵运算后便可得到当前的中心词的representation,即\(v_c=W·w_t∈R^{d×1}\)
  • 4.下一步就是中心词向量\(v_c\)与上下文矩阵\(W'\)相乘(\(u_o^Tv_c\))求得各个词之间的相似性。
  • 5.接下来根据上一步求出的相似性值,使用softmax将相似性大小转化为概率,选择概率最大的index作为输出。

需要注意的是 \(W'\)并不是\(W\)的转置 ,他们是两个完全不同的矩阵,只不过维度恰好是对方的转置矩阵维度而已,一般将\(W∈R^{d×V}\)称为input vector,\(W'∈R^{V×d}\)称为output vector。所以每个单词由两个词向量表示,那么那个作为最终的表示呢?有两种策略,一种是将两个词向量加起来,另一种是将两个词向量拼接起来,即得到\(R^{2d×1}\)词向量。

这里有个不明白的地方是为什么单词矩阵\(W∈R^{d×V}\)不止一个?希望明白的朋友能在评论区或者发邮件(marsggbo@foxmail.com)解释一下,谢谢!

下图是官方给的整理后的示意图,意思与上图一样不再赘述。

image.png

如果上面的解释还不能让你明白,可以参考Word2Vec介绍:直观理解skip-gram模型

III. Research Highlight

期间老师让一个中国学生做了一个关于一篇论文的报告,具体内容不作赘述,可参考CS224n研究热点1 一个简单但很难超越的Sentence Embedding基线方法

IV. Word2vec objective function gradients

目前为止,目标函数和流程图都已经清楚了,那么接下来我们需要计算出模型的参数\(\theta\)了。在上面内容中已经介绍了每个单词由两个维度为\(d×1\)的向量表示,常见的办法是将二者拼接,这样我们就可以得到一个非常庞大的向量参数,即

\[\begin{align} \theta&=\left[ \begin{matrix} v_a \\ v_{abandon}\\ \vdots \\ v_{zoo}\\ i_a \\ u_{abandon}\\ \vdots \\ u_{zoo}\\ \end{matrix} \right] ∈R^{2dV} \end{align} \]

那么如何优化这个参数呢?很自然地是梯度下降啦。

首先回顾一下目标函数:

\[\begin{align} min \,\,\,J(\theta)&=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,p(w_{t+j}|w_t;\theta) \\ &=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,p(u_o|v_c;\theta) \\ &=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,\frac{exp(u_o^Tv_c)}{\sum_{w=1}^Vexp(u_w^Tv_c)} \end{align} \]

要想计算\(J(\theta)\)最小值,首先需要计算\(\frac{\partial{J(\theta)}}{\partial{v_c}}\)。仔细观察公式(7)知道我可以先对右边的log项求微分,具体推导过程如下:

  • 首先将log项中的除法拆分成减法,得到两项:①和②,接着分别对这两项进行偏微分求导
  • ①和②都包含log和指数,所以为了求导方便令\(log=ln\),所以①很容易就求出来了。需要注意的是\(\frac{\partial{u_o^Tv_c}}{\partial{v_c}}=u_o\)而不是\(u_o^T\)
  • ②的偏微分稍微复杂一点,需要使用链式法则进行求解。
    • 首先我们想log右边的求和项看成一个整体,即为\(z=g(v_c)\),那么log整体可以看成是\(f(z)=f(g(v_c))\)注意不能把log和∑换位!!!看视频的时候我就是这么想的。。。
    • 同样为了计算方便令\(log=ln\),那么\(\frac{f(g(v_c)}{\partial{v_c}}=\frac{1}{g(v_c)}\)
    • 最后只需要再求\(\frac{\partial{g_c}}{\partial{v_c}}\)即可,需要用到指数求导公式,\(d{e^x}/dx={e^x}\),后面的步骤很简单了,不再赘述。

由上面的步骤可以得到

\[\begin{align} \frac{\partial{J(\theta)}}{\partial{v_c}}=-\frac{1}{T}\sum_{t=1}^T\sum_{-m≤j≤m,\,\,j≠0}(u_o-\sum_{x=1}^Vp(u_x|v_c)·u_x) \end{align} \]

计算出梯度后,就能够开始优化参数了:

\[\begin{align} \theta^{new} &=\theta^{old}-\alpha\frac{\partial{J(\theta)}}{\partial{\theta^{old}}} \notag\\ &=\theta^{old}-\alpha \nabla_\theta J(\theta) \end{align} \]

上面的公式在实际应用中还存在一些问题,因为通常一个词库是包含非常多单词的,如果对整个词库进行梯度更新的话会非常缓慢。所以可以引入SGD算法,即每次只对窗口大小的数据进行梯度更新。代码流程如下:

while True:
	window = sample_window(corpus)
	theta_grad = evaluate_gradient(J, window, theta)
	theta = theta - alpha * theta_grad



MARSGGBO原创





2018-12-27



posted @ 2018-12-27 10:13  marsggbo  阅读(644)  评论(0编辑  收藏  举报