放牧代码和思想
专注自然语言处理、机器学习算法
    愛しさ 優しさ すべて投げ出してもいい

CS224n Assignment 2

目录

3 RNN和语言模型

给定一个词语的序列:

$$\boldsymbol{x}^{(1)}, \boldsymbol{x}^{(2)}, \dots, \boldsymbol{x}^{(t)}$$

语言模型的任务就是通过建模

$$\begin{align}
P(\boldsymbol{x}^{(t+1)} &= \boldsymbol{v}_{j} \vert \boldsymbol{x}^{(t)}, \dots, \boldsymbol{x}^{(1)})
\end{align}$$

来预测下一个单词$\boldsymbol{x}^{(t+1)}$,其中$\boldsymbol{v}_{j}$是词表中的一个单词。

对RNN来讲,有:

$$\begin{align}
\boldsymbol{e}^{(t)} &= \boldsymbol{x}^{(t)}\boldsymbol{L} \\
%
\boldsymbol{h}^{(t)} &= \text{sigmoid} \left( \boldsymbol{h}^{(t-1)}\boldsymbol{H} + \boldsymbol{e}^{(t)}\boldsymbol{I} + \boldsymbol{b}_{1} \right)\\
%
\hat{\boldsymbol{y}}^{(t)} &= \text{softmax}  \left( \boldsymbol{h}^{(t)}\boldsymbol{U} + \boldsymbol{b}_{2} \right) \\
%
\bar{P}\left( \boldsymbol{x}^{(t+1)} = \boldsymbol{v}_{j} \vert \boldsymbol{x}^{(t)}, \dots, \boldsymbol{x}^{(1)} \right) &= \hat{\boldsymbol{y}}_{j}^{(t)}
\end{align}$$

其中$\boldsymbol{h}^{(0)} = \boldsymbol{h}_{0} \in \mathbb{R}^{D_{k}}$ 是隐藏层的初始状态, $\boldsymbol{L}$ 是lookup-table。参数维度如下:

$$\begin{align}
&\boldsymbol{L} \in \mathbb{R}^{\vert V \vert \times d}
%
&\boldsymbol{H} \in \mathbb{R}^{D_{h} \times D_{h}} \\
%
%
&\boldsymbol{I} \in \mathbb{R}^{d \times D_{h}}
%
&\boldsymbol{b}_{1} \in \mathbb{R}^{D_{h}} \\
%
%
& \boldsymbol{U} \in \mathbb{R}^{D_{h} \times \vert V \vert}
%
& \boldsymbol{b}_{2} \in \mathbb{R}^{\vert V \vert}
\end{align}$$

$d$是嵌入维度,$\vert V \vert$是词表大小,$D_{h}$是隐藏单元数。

输出向量$\hat{\boldsymbol{y}}^{(t)} \in \mathbb{R}^{\vert V \vert}$是词表上的概率分布。模型通过最小化交叉熵损失训练:

$$\begin{align}
J^{(t)}(\theta) = CE(\boldsymbol{y}^{(t)} , \hat{\boldsymbol{y}}^{(t)}) &= - \sum_{j=1}^{\vert V \vert} y_{j}^{(t)} log(\hat{y}_{j}^{(t)})
\end{align}$$

$\boldsymbol{y}^{(t)}$是正确答案的one-hot向量(等于$\boldsymbol{x}^{(t+1)}$)。对单个序列来讲,在所有实例(单词)上取平均。

a 困惑度和交叉熵损失

按惯例,语言模型的效果用困惑度衡量,其定义如下:

$$\begin{align}
\label{eq:perplexity}
PP^{(t)} \left( \boldsymbol{y}^{(t)}, \hat{\boldsymbol{y}}^{(t)} \right) &= \frac{ 1 }{ \bar{P} \left( \boldsymbol{x}_{pred}^{(t+1)} = \boldsymbol{x}^{(t+1)} \vert \boldsymbol{x}^{(t)}, \dots, \boldsymbol{x }^{(1)} \right)} = \frac{ 1 }{ \sum_{j=1}^{\vert V \vert} y_{j}^{(t)} \cdot \hat{y}_{j}^{(t)} }
\end{align}$$

也就是模型分布$\bar{P}$下输出正确答案的概率的倒数。

请从交叉熵推导困惑度。

由于正确答案是one-hot的,所以交叉熵中只有一项不为0:

$$CE(y^{(t)},\hat{y}^{t})=-\log \hat{y}_i^{(t)}=\log \frac{1}{\hat{y}^{t}}$$

$$PP^{(t)}(y^{(t)},\hat{y}^{(t)})=\frac{1}{\hat{y}^{(t)}}$$

$$CE(y^{(t)},\hat{y}^{t})=\log PP^{(t)}(y^{(t)},\hat{y}^{(t)})$$

可见最小化交叉熵的算术平均就相当于最小化困惑度的几何平均。如果模型随机预测,则期望$E[\hat{y}^{(t)}]=\frac{1}{|V|}$,困惑度为其倒数$|V|$,交叉熵为困惑度的对数$\log |V|$。

b 推导梯度

推导损失函数关于下列参数的梯度:

$$\begin{align}
\frac{ \partial J^{(t)} }{ \partial \boldsymbol{b}_{2} }
\quad
\frac{ \partial J^{(t)} }{ \partial \boldsymbol{L}_{x^{(t)}} }
\quad
\frac{ \partial J^{(t)} }{ \partial \boldsymbol{I} } \bigg\rvert_{(t)}
\quad
\frac{ \partial J^{(t)} }{ \partial \boldsymbol{H} } \bigg\rvert_{(t)}
\quad
\frac{\partial J^{(t)}}{\partial \boldsymbol{h}^{(t-1)}}
\end{align}$$

其中, $\boldsymbol{L}_{x^{(t)}}$ 是矩阵 $\boldsymbol{L}$ 对应当前词语 $\boldsymbol{x}^{(t)}$ 的那一行词向量。$\bigg\rvert_{(t)}$代表某个参数在$t$时的取值,而将之前的时刻视作固定值。

把big picture放在眼前

rnn.png

记$\boldsymbol{f}_{1}^{(t)} = \boldsymbol{h}^{(t-1)}\boldsymbol{H} + e^{(t)}\boldsymbol{I} + \boldsymbol{b}_{1}$ 、 $\boldsymbol{f}_{2}^{(t)} = \boldsymbol{h}^{(t)}\boldsymbol{U} + \boldsymbol{b}_{2}$,计算残差:

$$
\begin{align}
\boldsymbol{\delta}_{2}^{(t)} &= \frac{\partial J^{(t)}}{\partial \boldsymbol{f}_{2}^{(t)}} %
    = \hat{\boldsymbol{y}}^{(t)} - \boldsymbol{y}^{(t)} \nonumber \\
%
%
\boldsymbol{\delta}_{1}^{(t)}
&= \frac{\partial J^{(t)}}{\partial \boldsymbol{f}_{1}^{(t)}} \\
&=\frac{\partial \boldsymbol{J}^{(t)}}{\partial \boldsymbol{f}_2^{(t)}} \cdot \frac{\partial \boldsymbol{f}_{2}^{(t)}}{\partial \boldsymbol{h}^{(t)}} \cdot \frac{\partial \boldsymbol{h}^{(t)}}{\partial \boldsymbol{f}_{1}^{(t)}} \\
    &= \boldsymbol{\delta}_{2}^{(t)} \cdot \frac{\partial \boldsymbol{f}_{2}^{(t)}}{\partial \boldsymbol{h}^{(t)}} \cdot \frac{\partial \boldsymbol{h}^{(t)}}{\partial \boldsymbol{f}_{1}^{(t)}} \nonumber \\
   &= \boldsymbol{\delta}_{2}^{(t)} \cdot \boldsymbol{U}^{T} \circ \boldsymbol{h}^{(t)} \circ (1 - \boldsymbol{h}^{(t)}) \nonumber
\end{align}
$$

利用这些残差迅速得到结果:

$$\begin{align}
\frac{\partial J^{(t)}}{\partial \boldsymbol{b}_{2}} &= %
\frac{\partial J^{(t)}}{\partial \boldsymbol{f}_{2}^{(t)}} \frac{\partial \boldsymbol{f}_{2}^{(t)}}{\partial \boldsymbol{b}_{2}} \nonumber \\
%
%
  &= \boldsymbol{\delta}_{2}^{(t)} = \hat{\boldsymbol{y}}^{(t)} - \boldsymbol{y}^{(t)} \nonumber
\end{align}$$

$$\begin{align}
\frac{\partial J^{(t)}}{\partial \boldsymbol{L}_{x^{(t)}}} &= %
\frac{\partial J^{(t)}}{\partial \boldsymbol{f}_{1}^{(t)}} \frac{\partial \boldsymbol{f}_{1}^{(t)}}{\partial \boldsymbol{e}^{(t)}} %
\frac{\partial \boldsymbol{e}^{(t)}}{\partial \boldsymbol{L}_{x^{(t)}}} \nonumber \\
%
%
&= \boldsymbol{\delta}_{1}^{(t)} \boldsymbol{I}^{T} \nonumber
\end{align}$$

这里利用到了one-hot向量时$\boldsymbol{e}^{(t)}= \boldsymbol{L}_{x^{(t)}}$。

$$\begin{align}
\frac{\partial J^{(t)}}{\partial \boldsymbol{I}} &= %
\frac{\partial J^{(t)}}{\partial \boldsymbol{f}_{1}^{(t)}} \frac{\partial \boldsymbol{f}_{1}^{(t)}}{\partial \boldsymbol{I}} \nonumber \\
%
%
  &=  (\boldsymbol{e}^{(t)})^{T} \boldsymbol{\delta}_{1}^{(t)}  \nonumber
\end{align}$$

$$\begin{align}
\frac{\partial J^{(t)}}{\partial \boldsymbol{H}} &= %
\frac{\partial J^{(t)}}{\partial \boldsymbol{f}_{1}^{(t)}} \frac{\partial \boldsymbol{f}_{1}^{(t)}}{\partial \boldsymbol{H}} \nonumber \\
%
%
  &= (\boldsymbol{h}^{(t-1)})^{T} \boldsymbol{\delta}_{1}^{(t)} \nonumber
\end{align}$$

$$\begin{align}
\frac{\partial J}{\partial \boldsymbol{h}^{(t-1)}} &= %
\frac{\partial J^{(t)}}{\partial \boldsymbol{f}_{1}^{(t)}} \frac{\partial \boldsymbol{f}_{1}^{(t)}}{\partial \boldsymbol{h}^{(t-1)}} \nonumber \\
%
%
  &=  \boldsymbol{\delta}_{1}^{(t)} \boldsymbol{H}^{T} \nonumber
\end{align}$$

c Backpropagation Through Time

unroll反向传播并计算下列梯度:

$$
\begin{align}
\frac{ \partial J^{(t)} }{ \partial \boldsymbol{L}_{x^{(t-1)}} }
\quad
\frac{ \partial J^{(t)} }{ \partial \boldsymbol{I} } \bigg\rvert_{(t-1)}
\quad
\frac{ \partial J^{(t)} }{ \partial \boldsymbol{H} } \bigg\rvert_{(t-1)}
\end{align}
$$

利用$\boldsymbol{\delta}^{(t-1)} = \frac{\partial J^{(t)}}{\partial \boldsymbol{h}^{(t-1)}}$来简化结果。

真实梯度需要运用反向传播到$t=0$,而实践中经常只运行固定的$\tau \approx 5-10$步。

01-unrolled_rnn.png

引入新记号$\sigma'(\boldsymbol{f}_{1}^{(t-1)}) = \frac{\partial \boldsymbol{h}^{(t-1)}}{\partial \boldsymbol{f}_{1}^{(t-1)}} = diag\left(\boldsymbol{h}^{(t-1)} \circ (1 - \boldsymbol{h}^{(t-1)}) \right)$

对$\frac{\partial J ^{(t)}}{\partial \boldsymbol{L}_{x^{(t-1)}}}$有

$$\begin{align}
\frac{\partial J ^{(t)}}{\partial \boldsymbol{L}_{x^{(t-1)}}} &= %
\frac{\partial J ^{(t)}}{\partial \boldsymbol{h}^{(t-1)}} %
\frac{\partial \boldsymbol{h}^{(t-1)}}{\partial \boldsymbol{f}_{1}^{(t-1)}} %
\frac{\partial \boldsymbol{f}_{1}^{(t-1)}}{\partial \boldsymbol{L}_{x^{(t-1)}}} \nonumber \\
%
&= \boldsymbol{\delta}^{(t-1)} \sigma'(\boldsymbol{f}_{1}^{(t-1)}) \boldsymbol{I}^{T} \nonumber
\end{align}$$

对$\boldsymbol{I}$有

$$
\begin{align}
\frac{\partial J ^{(t)}}{\partial \boldsymbol{I}} &= %
\frac{\partial J ^{(t)}}{\partial \boldsymbol{h}^{(t-1)}} %
\frac{\partial \boldsymbol{h}^{(t-1)}}{\partial \boldsymbol{v}^{(t-1)}} %
\frac{\partial \boldsymbol{v}^{(t-1)}}{\partial \boldsymbol{I}} \nonumber \\
%
&=  (\boldsymbol{e}^{(t-1)})^{T} \boldsymbol{\delta}_{1}^{(t-1)} \sigma'(\boldsymbol{v}^{(t-1)}) \nonumber
\end{align}
$$

对$H$有

$$
\begin{align}
\frac{\partial J ^{(t)}}{\partial \boldsymbol{H}} &= %
\frac{\partial J ^{(t)}}{\partial \boldsymbol{h}^{(t-1)}} %
\frac{\partial \boldsymbol{h}^{(t-1)}}{\partial \boldsymbol{v}^{(t-1)}} %
\frac{\partial \boldsymbol{v}^{(t-1)}}{\partial \boldsymbol{H}} \nonumber \\
%
&= (\boldsymbol{h}^{(t-2)})^{T} \boldsymbol{\delta}^{(t-1)} \sigma'(\boldsymbol{v}^{(t-1)}) \nonumber
\end{align}
$$

d 计算复杂度

给定$\boldsymbol{h}^{(t-1)}$,前向传播计算$J^{(t)}(\theta)$的复杂度是多少?反向传播$\tau$步呢?

把矩阵运算中涉及到元素数量(维度)估计一下即可。

前向传播:

$$\begin{align}
O\left( (d \times D_{h}) + D_{h}^{2} + (D_{h} \times |V|) \right) \nonumber
\end{align}$$

从左到右3项分别来自输入词向量到隐藏层的权值矩阵、隐藏层到隐藏层的权值矩阵、隐藏层到输出的权值矩阵。

反向传播:

如果对所有单词计算梯度:

$$\begin{align} O\left( \tau \left((d \times D_{h}) + D_{h}^{2} +  D_{h} \times |V| \right)\right) \nonumber
\end{align}$$

这是因为反向传播$\tau$次的路径上,残差来自$\tau$个输出单词。

如果对单个单词计算梯度:

$$\begin{align} O\left( \tau \left((d \times D_{h}) + D_{h}^{2}  \right) +  D_{h} \times |V| \right) \nonumber \end{align}$$

此时反向传播$\tau$次的路径上,来自$\tau$个输出单词的残差中对该单词之外的导数为0。

由于词表远远大于隐藏层节点数$|V|>>D_h$,所以softmax层的$O(D_{h} \times |V|)$是瓶颈。一个解决办法是用NCE代替昂贵的交叉熵损失函数

References

https://github.com/hankcs/CS224n

https://github.com/rymc9384/DeepNLP_CS224N.git

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » CS224n Assignment 2

评论 15

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #10

    我搞到90.5分了,哈哈哈哈哈。

    ALEX5年前 (2019-05-21)回复
  2. #9

    2g(i)中:(以下来自斯坦福官网给出的Solution):Adam使用动量,可以防止梯度更新过快。一是为了在陷入局部最优的时候梯度不为零, 仍然可以逃离局部最优。二是可以让每一次的梯度估计都更加接近数据集整体的梯度。

    dudu5年前 (2018-12-12)回复
  3. #8

    我想问下,为什么笔记note里面的困惑度perplexity = 2^J,和作业里面定义的困惑度不一样??

    zavid6年前 (2018-08-02)回复
  4. #7

    dropout那边,如果p是drop的概率的话,gamma应该等于\frac{1}{1-p}

    r3dir3ct6年前 (2018-05-11)回复
  5. #6

    2.b, 每个词进去再出来,应该是2n步,可以数数上面5个词是10步。

    Albert_J6年前 (2018-03-01)回复
  6. #5

    xavier初始化是均匀分布,博文里面应该是笔误写成了正态分布

    HYN6年前 (2017-12-29)回复
  7. #4

    谢谢博主分享!
    每次代码都得参考您的= =~~很感谢!

    zy7年前 (2017-08-05)回复
  8. #3

    请问在求偏导的时候如何确定一个参数矩阵是否需要转置?
    是在求导完式子之后通过观察求导式的各个参数的形状来确定是否需要添加转置操作嘛?
    求导求得好乱。。博主能否推荐一下矩阵求导的资料。。感觉和高数学的知识之间有空白。。

    MONK7年前 (2017-08-03)回复
    • stanford视频里面Richard是对各个元素求导,然后把结果写出矩阵形式。
      博主这里可能是直接用的线性时的结论,就是左乘右乘分别有公式的。

      zy7年前 (2017-08-05)回复
    • 关于资料,你可以去http://web.stanford.edu/class/cs224n/syllabus.html
      有一个补充阅读是讲神经网络梯度计算的,我还没看不知道情况。
      我之前是看的一个网友总结的,= =反正我看完,做出来的结果就不对,还觉得正确的解法不符合定理。
      就不推荐了。

      zy7年前 (2017-08-05)回复
  9. #2

    不是很明白 3.d 关于反向传播计算复杂度的计算,是dJ/dL, dJ/dI, dJ/dH三个计算复杂度的和么? 对于softmax只在最后做一次来讲,传播 t 次是否应该是前两项乘上 t,而最后一项只算一次呢?

    nylqd7年前 (2017-07-30)回复
    • 1. 是的
      2. 有两种interpretation,见上文补充。

      hankcs7年前 (2017-07-31)回复
  10. #1

    d Minibatch Parsing部分,
    代码27行minibatch = [parse for parse in partial_parses if len(parse.stack) > 1 or len(parse.buffer) > 0]
    其中partial_parses应为minibatch

    matianjun17年前 (2017-07-12)回复

我的作品

HanLP自然语言处理包《自然语言处理入门》