Meaning的定义有很多种,其中有:
那么在计算机中是如何获取一个word的meaning的呢?常见的解决办法是使用像WordNet之类的数据集,它包含了同义词(synonym)组和上位词(hypernyms)组。这种表示方法属于Discrete representation
上位词(hypernym),指概念上外延更广的主题词。 例如:”花”是”鲜花”的上位词,”植物”是”花”的上位词,”音乐”是”mp3”的上位词。上位词是相对某主题词的,也有它自己的等同词、上位词、下位词、同类词。
但是类似于WordNet的数据集存在如下缺点:
如何将单词量化成计算机能读懂的数据呢?常见的一种方法是one-hot编码。
例如假设我们的数据集一共有6个单词,其中有motel(汽车旅馆) 和 hotel。
那么我们可以作如下表示:
但是这有一个很明显的缺点就是词与词之间没有关联性,也就是说我们无法从one-hot编码中得知不同词之间的相似性,因为各个词编码后得到的向量是正交的。
所以我们还需要找到一种能够计算单词相似性,相关性的编码方式。
一个很自然,很直观的方法就是根据某个单词的上下文对该单词的含义进行编码。该方法的核心思想是:A word’s meaning is given by the words that frequently appear close-by,由J.R.Firth在1957年提出。
下图给出示例,假如我们需要对banking的含义进行编码,那么我们可以根据预先设定的固定大小的窗口对banking前后出现的单词进行统计,banking的含义可以有这些单词表示。
我们先定义这样一个模型,该模型是在word vectors编码的基础上能够预测出中心词\(w_t\)的上下文: \[p(context|w_t)\]
该模型的损失函数为 \[J=1-p(w_{-t}|w_t)\] \(w_{-t}\)表示出了t以外的其他词,即\(w_t\)的上下文。
word2vec的核心思想是predict between every word and its context words!
两个算法:
两个稍微高效一点的训练方法:
该课程主要集中讲解Naive softmax,上面的两个算法和训练方法可以参考word2vec原理推导与代码分析。
下图给出了skip-gram示意图。中心词banking位置为\(t\),用\(w_t\)表示。窗口大小为\(m\)
需要注意的是虽然banking的上下文可以分为前面和后面,但是概率分布只有一种,即假设banking左边第三个单词是as,右边第二个是as,这两个as概率分布是要合并计算的,不存在说分前后两种概率分布。
介绍完模型后下面需要介绍一下目标函数,函数形式如下: \[ \begin{align} max\,\,J'(\theta)=\prod_{t=1}^T\,\,\,\prod_{-m≤j≤m,\,\,j≠0}p(w_{t+j}|w_t;\theta) \end{align}\]
通常为了方便计算会将上式化为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} \]
那么如何计算\(p(w_{t+j}|w_t)\)呢?为方便说明先做如下定义:
所以\(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} \]
公式(3)中的分子的点积运算,他可以用于计算中心词和上下文词的相似性,值越大表示越相似。
上图虽然乍一看很凌乱,但是很清晰地展示了skipgram的计算过程。
从左往右看:
需要注意的是 \(W'\)并不是\(W\)的转置 ,他们是两个完全不同的矩阵,只不过维度恰好是对方的转置矩阵维度而已,一般将\(W∈R^{d×V}\)称为input vector,\(W'∈R^{V×d}\)称为output vector。所以每个单词由两个词向量表示,那么那个作为最终的表示呢?有两种策略,一种是将两个词向量加起来,另一种是将两个词向量拼接起来,即得到\(R^{2d×1}\)词向量。
这里有个不明白的地方是为什么单词矩阵\(W∈R^{d×V}\)不止一个?希望明白的朋友能在评论区或者发邮件(marsggbo@foxmail.com)解释一下,谢谢!
下图是官方给的整理后的示意图,意思与上图一样不再赘述。
如果上面的解释还不能让你明白,可以参考Word2Vec介绍:直观理解skip-gram模型。
期间老师让一个中国学生做了一个关于一篇论文的报告,具体内容不作赘述,可参考CS224n研究热点1 一个简单但很难超越的Sentence Embedding基线方法。
目前为止,目标函数和流程图都已经清楚了,那么接下来我们需要计算出模型的参数\(\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项求微分,具体推导过程如下:
由上面的步骤可以得到 \[ \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