Stanford CS224n 2020 Notes1

Posted by MakiNaruto on Thu, Feb 13, 2020

Stanford / Winter 2020 CS224n 课程学习笔记01,02

根据统计学, 比如一句话:“我想吃X”, 根据训练文本, 输出最有可能出现的X。比如训练文本出现最多的是"我想吃苹果", 那么"苹果"则应是最有可能被预测出来。 假设我们有一个列表D(字典), D = [‘problems’, ’turning’, ‘into’, ‘banking’, ‘crises’, ‘as’] 字典总长度为6, 我们假设窗口为3, 目前窗口位置在这里[‘problems’, ’turning’, ‘into’]。我们想预测’turning’的上下文(一个窗口内)最有可能出现的单词。按照从左到右进行预测, 我们理想状态下第一个词的预测输出为\$[1, 0, 0, 0, 0, 0]·D \$ = problems 。即直接对字典D进行索引。但现实是不可能的, 所以概率可能就变为了[0.92, 0.03, 0.01, 0.06, 0.07, 0.25]。

疑问一:那么这个概率矩阵\$\hat y\$是怎么得到的?

计算完毕后, 会得到一个和词汇表长度一样的概率输出,即:

\$\hat y\$ = [0.92, 0.03, 0.01, 0.06, 0.07, 0.25]。

疑问二:词向量是怎么训练出来的?

首先要用到交叉熵损失函数 (cross-entropy loss) :

$$ loss_{cross-entropy} = -\sum_{w \in Vocab} y_{w} \log \left(\hat{y}_{w}\right) $$

即预测值与真实值(目标值)的差异, 即

$$ y = [1, 0, 0, 0, 0, 0] $$ $$ \hat y = [0.92, 0.03, 0.01, 0.06, 0.07, 0.25] $$

差异性越大, 交叉熵损失越大。所以为了让预测更准确, 我们要降低交叉熵损失

tips:这里对交叉熵函数进行推导, 得到更简便的形式

$$ \begin{aligned} loss_{cross-entropy} = -\sum_{w \in Vocab} y_{w} \log \left(\hat{y}_{w}\right) & = - y_{o} \log \left(\hat{y}_{o}\right) - \sum_{w \in Vocab, w \neq o} y_{w} \log \left(\hat{y}_{w}\right)\\& = - \log \left(\hat{y}_{o}\right) \end{aligned} $$

当 \$loss_{cross-entropy}\$越来越小, 即我们的准确率越来越高时, 就达到我们想要的的效果了。降低loss的方法是利用梯度下降进行梯度更新。 这是当激活函数为softmax时, 我们的 \$loss_{cross-entropy}\$ 计算公式:

$$ \begin{aligned} J_{naive-softmax(v_{c},o,U)} & = -logP(O = o|C = c) \\& = -log( \frac{e^{u_{o}^{T} v_c}}{ \sum_{W \in Vocab} e^{u_{w}^{T} v_c}}) \\& = - u_{o}^{T} v_c + log(\sum_{W \in Vocab} e^{u_{w}^{T} v_c} ) \end{aligned} $$

进行梯度更新时, 我们要更新两个向量, 一个是中心词向量\$v_c\$, 一个是外部词向量\$u_w\$。

一、使用激活函数为Softmax, 进行梯度下降并更新词向量:

$$ \begin{aligned} \frac{\partial J\left(v_{c}, o, U\right)}{\partial v_{c}} & =-\frac{\partial\left(u_{o}^{T} v_{c}\right)}{\partial v_{c}}+\frac{\partial\left(\log \left(\sum_{w} \exp \left(u_{w}^{T} v_{c}\right)\right)\right)}{\partial v_{c}} \\ & =-u_{o}+\frac{1}{\sum_{w} \exp \left(u_{w}^{T} v_{c}\right)} \frac{\partial\left(\sum_{w} \exp \left(u_{w}^{T} v_{c}\right)\right)}{\partial v_{c}} \\ & =-u_{o}+\sum_{w} \frac{\exp \left(u_{w}^{T} v_{c}\right) u_{w}}{\sum_{w} \exp \left(u_{w}^{T} v_{c}\right)} \\ & =-u_{o}+\sum_{w} p(O=w | C=c) u_{w} \\ & =-u_{o}+\sum_{w} \hat{y}_{w} u_{w} \\ & = -u_{o} + u_{new} \\ & = U^T (\hat{y} - y) \end{aligned} $$

更新词向量\$u_w\$时, 分为两种情况, ①\$w=o\$时 ②\$w \neq o\$时 when \$w = o\$

$$ ①\begin{aligned} \frac{ \partial J\left(v_{c}, o, U\right)}{\partial u_{w} } & = -\frac{ \partial \left(u_{o}^{T} v_{c}\right)} { \partial u_{w} }+ \frac{ \partial \left( \log \left( \exp \left(u_{w}^{T} v_{c} \right) \right) \right)} { \partial u_{w} } \\ & = -v_c + \frac{1}{ \sum_{w} \exp \left(u_{w}^{T} v_{c} \right)} \frac{ \partial \left( \exp \left(u_{w}^{T} v_{c} \right) \right) } { \partial u_{w} } \\ & = -v_c + \frac{ \exp \left(u_{w}^{T} v_{c} \right) v_{c} } { \sum_{w} \exp \left( u_{w}^{T} v_{c} \right) } \\ & = -v_c + p(O=w | C=c) v_{c} \\ & = -v_c + \hat {y}_{w=o} v_{c} \end{aligned} $$

when \$w \neq o\$

$$ ② \begin{aligned} \frac{\partial J\left(v_{c}, o, U\right)}{ \partial u_{w} } & = 0+ \sum_{w-1} p(O = o | C = c) v_{c} \\ & = \sum_{w-1} \hat {y}_{w \neq o} v_{c} \end{aligned} $$

then

$$ \begin{aligned} \frac{ \partial J\left(v_{c}, o, U\right)}{ \partial u_{w} } & = ① + ② = -v_c + \hat{y}_{w=o} v_{c} + \sum_{w-1} \hat {y}_{w \neq o} v_{c} \\& = \hat{y}·v_c - y·v_c = (\hat{y} - y)^{T}v_c \end{aligned} $$

根据求导结果, 我们发现计算时仅需 \$U, y, \hat y\$ 即可。 注:在开始时生成两个同样大小的矩阵 \$U, V\$。\$U\$ 初始为0矩阵, \$V\$ 则进行随机初始化, 两个矩阵大小都是 Vocab * dim。但是, 进行更新时, \$\frac{\partial J }{\partial v_{c}}\$ 只在 \$V\$ 内更新, \$ \frac{\partial J }{\partial w_{u}}\$ 只在 \$U\$ 内更新。

为了更直观的理解, 下面是我做的动画演示。

softmax_vc的更新
softmax_vc的更新
Softmax_U的更新
Softmax_U的更新

二、使用激活函数为Sigmoid, 进行梯度下降并更新词向量:

在课程中, 又提出了一种对损失函数的优化算法, 其更新 \$v_c, u_w\$ 时, 取随机L个窗口外的词, 这样在 \$\frac{\partial J }{\partial u_{w}}\$ 在更新时, 不是更新几乎整个词汇表的词向量矩阵(Vocab * dim), 而是更新(L * dim)大小的矩阵。从计算成本上要节省了许多。下面是实现步骤。 我们将激活函数改为sigmoid

$$\sigma(x)= \frac{1}{1+e^{-x}}=\frac{e^x}{e^x + 1}$$

这是当激活函数为sigmoid时, 我们的 \$loss_{cross-entropy}\$ 计算公式:

$$ J_{\text {neg-sample }}\left(v_{c}, o, U\right)=-\log \left(\sigma\left(u_{o}^{\top} v_{c}\right)\right)-\sum_{k=1}^{K} \log \left(\sigma\left(-u_{k}^{\top} v_{c}\right)\right) $$

因为要计算梯度, 根据求导链式法则, 后面会对sigmoid进行求导, 所以先在这里求了。

$$\begin{aligned} \sigma(x)' & = (\frac{1}{1+e^{-x}})' \\& = \frac{e^{-x}}{(1 +e{^{-x}})^2} \\& = \frac{1+e^{-x} -1}{(1 +e{^{-x}})^2} \\& = \frac{1}{1+e^{x}}(1-\frac{1}{1+e^{x}})\\& = \sigma(x)(1-\sigma(x)) \end{aligned}$$

对中心词向量计算偏导 tips: \$1 - \sigma(-x) = \sigma(x)\$

$$ \begin{aligned} \frac{\partial J_{\text {neg-sample } }}{\partial v_{c}} & =\left(\sigma\left(u_{o}^{T} v_{c}\right)-1\right) u_{o}+\sum_{k=1}^{K}\left(1-\sigma\left(-u_{k}^{T} v_{c}\right)\right) u_{k} \\& =\left(\sigma\left(u_{o}^{T} v_{c}\right)-1\right) u_{o}+\sum_{k=1}^{K} \sigma\left(u_{k}^{T} v_{c}\right) u_{k} \end{aligned} $$

对负样本向量计算偏导 因为是负样本, 所以\$o \notin K \$。 \$ [w_1,w_2,…w_k] \in K\$,其向量表示为 \$[u_1,u_2,…u_k]\$

$$\begin{aligned} \frac{\partial J_{\text {neg-sample } }}{\partial u_{o} } & = (\sigma(u_{o}^{T}v_c) -1)v_c \end{aligned}$$ $$ \begin{aligned} \frac {\partial J_{ \text { neg-sample } } } { \partial u_{k} } & = \sum_{k=1}^{K} (1- \sigma (-u_{k}^{T} v_{c}) ) v_{c} \\& = \sum_{k=1}^{K} (\sigma (u_{k}^{T} v_{c}) ) v_{c} \end{aligned} $$

动画演示。

sigmoid_U的更新
sigmoid_U的更新
sigmoid_Vc的更新
sigmoid_Vc的更新

梯度更新:

\$\theta^{new} = \theta^{old} - \alpha \bigtriangledown_{\theta}J(\theta)\$。\$\alpha \$ 为学习率。 因为代码里 \$U,V\$ 矩阵是拼接后进行计算的, 所以 \$\bigtriangledown_{\theta}J(\theta)\$ 和 \$\theta\$ 同为 2Vocab * dim大小的矩阵。

最后, 训练出的词向量就是V矩阵。

参考资料

[1]Stanford / Winter 2020 CS224n [2]CS224n-2019 学习笔记(looperxx)