RNN

RNN组织架构的精髓在于:在输入输出之外,引入了隐状态来表达当前输入输出所额外包含的序列信息。RNN的Hidden_size是超参数,这种自由度为隐状态的物理含义披上了一层神秘面纱(当然本来ML就是一个难以解释的迷hh)。给出RNN的基本计算公式。由这个公式可以拓展出,RNN包括下面的所有东西都可以是双向的bi-direction、多层的multi_layer。

Ht=tanh(XtWxh+Ht1Whh+bh)Ot=O(HtWhq+bq)H_t = \tanh(X_t*W_{xh}+H_{t-1}*W_{hh}+b_h)\\ O_t = O(H_t*W_{hq}+b_q)

GRU和LSTM

每次RNN对输入进来的东西进行处理后,会不由分说/平等/一视同仁地更新隐状态。仿佛对上一隐状态只留存极其短期的记忆,但有时候可能特别在意序列特别早期输入进来的内容,或者此次的输入根本不重要等,希望希望对这个短期记忆能够长一点。因此就叫做长的短期记忆神经网络(Long Short-Term Memory)。GRU和LSTM都是这个作用,GRU虽然提出的晚但是结构更简单计算更快,但是LSTM似乎被用的更加广泛一些。

GRU

简单给出公式和图解,这里得到新的隐状态之后,再用上面RNN的最后一条公式得到输出。在这里,重置门直接把输入和上一隐状态结合得到候选隐状态,因此可以把候选隐状态当作是普通RNN直接处理输入信息的流程。因此重置门肯定依赖的是序列中的短期信息。而更新门把根据当前输入,决定上一隐状态有多少程度被保留下来,有多少程度被候选隐状态更新,因此可以捕获较为长期的信息。

Rt=σ(XtWxr+Ht1Whr+br)Zt=σ(XtWxz+Ht1Whz+bz)\begin{aligned} \mathbf{R}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xr} + \mathbf{H}_{t-1} \mathbf{W}_{hr} + \mathbf{b}_r)\\ \mathbf{Z}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xz} + \mathbf{H}_{t-1} \mathbf{W}_{hz} + \mathbf{b}_z) \end{aligned}

H~t=tanh(XtWxh+(RtHt1)Whh+bh)Ht=ZtHt1+(1Zt)H~t.\tilde{\mathbf{H}}_t = \tanh(\mathbf{X}_t \mathbf{W}_{xh} + \left(\mathbf{R}_t \odot \mathbf{H}_{t-1}\right) \mathbf{W}_{hh} + \mathbf{b}_h)\\ \mathbf{H}_t = \mathbf{Z}_t \odot \mathbf{H}_{t-1} + (1 - \mathbf{Z}_t) \odot \tilde{\mathbf{H}}_t.

GRU

LSTM

这里李沐和李宏毅作的图都不太完美。可以发现比较特别的地方在于,除了隐状态随时间变化之外,还有一个记忆元随时间变化。似乎很难直接理解lstm的设计思路。这里的逻辑可以理解成:

  1. 基于Ht1H_{t-1}生成输入门、遗忘门、输出门的值;
  2. 根据输入生成一个候选记忆元C~t\tilde{\mathbf{C}}_t,用输入门和遗忘门分别对候选记忆元和旧记忆元做加权决定新记忆元CtC_t
  3. 用新记忆元CtC_t和输出门来决定HtH_t

It=σ(XtWxi+Ht1Whi+bi)Ft=σ(XtWxf+Ht1Whf+bf)Ot=σ(XtWxo+Ht1Who+bo)\begin{aligned} \mathbf{I}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xi} + \mathbf{H}_{t-1} \mathbf{W}_{hi} + \mathbf{b}_i)\\ \mathbf{F}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xf} + \mathbf{H}_{t-1} \mathbf{W}_{hf} + \mathbf{b}_f)\\ \mathbf{O}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xo} + \mathbf{H}_{t-1} \mathbf{W}_{ho} + \mathbf{b}_o) \end{aligned}

C~t=tanh(XtWxc+Ht1Whc+bc)Ct=FtCt1+ItC~tHt=Ottanh(Ct)\tilde{\mathbf{C}}_t = \text{tanh}(\mathbf{X}_t \mathbf{W}_{xc} + \mathbf{H}_{t-1} \mathbf{W}_{hc} + \mathbf{b}_c)\\ \mathbf{C}_t = \mathbf{F}_t \odot \mathbf{C}_{t-1} + \mathbf{I}_t \odot \tilde{\mathbf{C}}_t\\ \mathbf{H}_t = \mathbf{O}_t \odot \tanh(\mathbf{C}_t)

LSTM-LM
LSTM-LHY

Seq2seq

对于一个rnn/gru/lstm来说,假设输入x的形状为(num_steps,input_size),则输出y的形状为(num_steps,output_size),也就是序列长度num_steps是不变的。Seq2seq和以往所有网络不同的地方在于输入序列和输出序列都是长度可变的。

Embedding

1
2
3
4
x =  torch.zeros((m,n))
embd = nn.Embedding(vocab_size, embed_size)
# 则y.shape: (m,n,embed_size)
y = embd(x)

pred & train

s2s_eval
s2s_train

这是一个’后处理’的问题。以文本生成为例,考虑在s2s的解码过程中,每次得到y_hat,(似乎不会直接把这个y_hat直接作为下一步的输入),会做softmax之后从词表中找到概率最大对应的字,以这个字对应的独热编码作为下一步的输入。
这么做的本质就已经是一种贪心算法的体现了,如果把softmax的结果视作概率,每次都取最大概率得到的序列,其对应的总概率可能并不是一个全局最大的结果。因此比起把可能的所有序列的穷举出来,不如每次softmax之后选概率比较大的k个字作为下一步的输入,等下一步得到k2k^2个待选序列之后再取k个概率更大的序列再次放入下一步,以此不断循环。

Attention Mechanism

用数学语言描述,假设有一个查询qRq\mathbf{q} \in \mathbb{R}^qmm个“键-值”对(k1,v1),,(km,vm)(\mathbf{k}_1, \mathbf{v}_1), \ldots, (\mathbf{k}_m, \mathbf{v}_m),其中kiRk\mathbf{k}_i \in \mathbb{R}^kviRv\mathbf{v}_i \in \mathbb{R}^v。注意力汇聚函数ff就被表示成值的加权和:

f(q,(k1,v1),,(km,vm))=i=1mα(q,ki)viRv,f(\mathbf{q}, (\mathbf{k}_1, \mathbf{v}_1), \ldots, (\mathbf{k}_m, \mathbf{v}_m)) = \sum_{i=1}^m \alpha(\mathbf{q}, \mathbf{k}_i) \mathbf{v}_i \in \mathbb{R}^v,

其中查询q\mathbf{q}和键ki\mathbf{k}_i的注意力权重(标量)是通过注意力评分函数aa 将两个向量映射成标量,再经过softmax运算得到的:

α(q,ki)=softmax(a(q,ki))=exp(a(q,ki))j=1mexp(a(q,kj))R.\alpha(\mathbf{q}, \mathbf{k}_i) = \mathrm{softmax}(a(\mathbf{q}, \mathbf{k}_i)) = \frac{\exp(a(\mathbf{q}, \mathbf{k}_i))}{\sum_{j=1}^m \exp(a(\mathbf{q}, \mathbf{k}_j))} \in \mathbb{R}.

因此,注意力汇聚函数在很大程度上依赖于注意力评分函数aa 。

此外,给出各个参数的形状来帮助理解:
queries: (batch, num_steps,len(vocab))
keys: (batch, num_KV, key_size)
values: (batch, num_KV, values_size)
attention: (batch, num_steps, value_size)

加性评分函数

当查询和键是不同长度的矢量时,我们可以使用加性注意力作为评分函数。给定查询qRq\mathbf{q} \in \mathbb{R}^q和键kRk\mathbf{k} \in \mathbb{R}^k加性注意力(additive attention)的评分函数为

a(q,k)=wvtanh(Wqq+Wkk)R,a(\mathbf q, \mathbf k) = \mathbf w_v^\top \text{tanh}(\mathbf W_q\mathbf q + \mathbf W_k \mathbf k) \in \mathbb{R},

其中可学习的参数是WqRh×q\mathbf W_q\in\mathbb R^{h\times q}WkRh×k\mathbf W_k\in\mathbb R^{h\times k}wvRh\mathbf w_v\in\mathbb R^{h}。将查询和键连结起来后输入到一个多层感知机(MLP)中,感知机包含一个隐藏层,其隐藏单元数是一个超参数hh。通过使用tanh\tanh作为激活函数,并且禁用偏置项。

点积评分函数(transformer用)

使用点积可以得到计算效率更高的评分函数,但是点积操作要求查询和键具有相同的长度dd。假设查询和键的所有元素都是独立的随机变量,并且都满足零均值和单位方差,那么两个向量的点积的均值为00,方差为dd。为确保无论向量长度如何,点积的方差在不考虑向量长度的情况下仍然是11,我们将点积除以d\sqrt{d},则缩放点积注意力(scaled dot-product attention)评分函数为:

a(q,k)=qk/d.a(\mathbf q, \mathbf k) = \mathbf{q}^\top \mathbf{k} /\sqrt{d}.

在实践中,我们通常从小批量的角度来考虑提高效率,例如基于nn个查询和mm个键-值对计算注意力,其中查询和键的长度为dd,值的长度为vv。查询QRn×d\mathbf Q\in\mathbb R^{n\times d}、键KRm×d\mathbf K\in\mathbb R^{m\times d}和值VRm×v\mathbf V\in\mathbb R^{m\times v}的缩放点积注意力是:

softmax(QKd)VRn×v.\mathrm{softmax}\left(\frac{\mathbf Q \mathbf K^\top }{\sqrt{d}}\right) \mathbf V \in \mathbb{R}^{n\times v}.

Bahdanau注意力模型

下面描述的Bahdanau注意力模型中,上下文变量c\mathbf{c}在任何解码时间步tt'都会被ct\mathbf{c}_{t'}替换。假设输入序列中有TT个词元,解码时间步tt'的上下文变量是注意力集中的输出:

ct=t=1Tα(st1,ht)ht,\mathbf{c}_{t'} = \sum_{t=1}^T \alpha(\mathbf{s}_{t' - 1}, \mathbf{h}_t) \mathbf{h}_t,

其中,时间步t1t' - 1时的解码器隐状态st1\mathbf{s}_{t' - 1}是查询,编码器隐状态ht\mathbf{h}_t既是键,也是值。
一个带有Bahdanau注意力的循环神经网络编码器-解码器模型

多头注意力

多头注意力:多个头连结然后线性变换

自注意力

下图来自李宏毅的讲义,用矩阵乘法非常形象地说明了自注意力的精髓(使用的是点积评分函数)。不同之处在于,前面介绍的两个评分函数权重参数都放在QKV前面,但此处都放在Input前面(其实本质一样)。
从矩阵乘法理解自注意力

我的理解

从自注意力出发,观察表达式可知,注意力汇聚函数的输出是对Value的加权组合。而这个加权来自于评分函数,即Query与Key之间的关系。以点积评分函数为例,因为除以了根号d,因此点积越大说明向量的方向越接近。为了让损失函数降低,多加一层注意力汇聚层,神经网络会自动地学习并选择性地关注输入中的重要信息,(让方向该接近的更接近),因此可以提高模型的性能和泛化能力。