语言模型
自然语言处理的重要技术。自然语言文本是一段离散的时间序列,长度为T的文本各词w_t的出现可认为在时间步t的输出。语言模型会计算词序列的概率,判断哪种组合出现概率最大,作为最终输出。
通过全概率公式计算一个文本序列出现的概率。此时需要计算词的概率,以及各词在给定前词情况下的条件概率。
- 词的概率=该词在训练数据集中的相对词频
- 各词在给定前词情况下的条件概率=训练数据集中的相对词频
- P(w2|w1)=w1w2两词相邻的频率除以w1的词频
n元语法
当文本序列长度增加时,计算和存储多个词的概率的复杂度会指数增加。
马尔科夫假设是指一个词的出现只与前面n个词相关,即马尔科夫链。
n元语法基于n-1阶马尔科夫链,其中n衡量了计算复杂度和模型准确性。
当$n$分别为1、2和3时,我们将其分别称作一元语法(unigram)、二元语法(bigram)和三元语法(trigram)。例如,长度为4的序列$w_1, w_2, w_3,w_4$在一元语法、二元语法和三元语法中的概率分别为
$$
\begin{aligned}
P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2) P(w_3) P(w_4) ,\
P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_2) P(w_4 \mid w_3) ,\
P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_2, w_3) .
\end{aligned}
$$
当n较小时,n元语法往往不准确,但n较大时,存储量大。
循环神经网络
n元语法只考虑前n-1个词,考虑t-(n-1)前的词,模型参数变大。循环神经网络通过隐藏状态来存储之前时间步的信息。
多层感知机添加隐藏状态变成循环神经网络。
不含隐藏状态的神经网络
$O=HW_{hq}+b_q=\Phi(XW_{xh}+b_h)W_{hq}+b_q$
若是分类问题使用softmax($O$)计算输出类别的概率分布。
含隐含状态的循环神经网络
$O_t=H_tW_{hq}+b_q=\Phi(X_tW_{xh}+H_{t-1}W_{hh}+b_h)W_{hq}+b_q$
想比与不含隐含状态的多层感知机,RNN多了$H_{t-1}W_{hh}$这一项,可以存储前一时间步$t-1$的历史信息,RNN的模型参数固定,不随时间增长。
隐含状态中的$X_tW_{xh}+H_{t-1}W_{hh}$等价于$X_t$与$H_{t-1}$在列维度连接的矩阵乘以$W_{xh}$与$W_{hh}$在行维度连接的矩阵。
语言模型数据集
语出里数据集,并转化为字符级RNN所需要的输入格式。
- 将换行符换位空格
- 建立字符索引:不同词不同索引–>词典。
- 数据采样:批量样本数+时间步
- 随机采样:采样的两个随机小批量在原始序列上位置不一定相邻
- 相邻采样:采样的两个随机小批量在原始序列上位置相邻
循环神经网路从0实现
one-hot变量:一维向量,在某维度为1,其余为0。
输入为(批量大小,时间步数),输出为(批量大小,词典大小,时间步数)。
scatter_(dim, index, src) → Tensor
Writes all values from the tensor src into self at the indices specified in the index tensor. For each value in src, its output index is specified by its index in src for dimension != dim and by the corresponding value in index for dimension = dim.
For a 3-D tensor, self is updated as:
self[index[i][j][k]][j][k] = src[i][j][k] # if dim == 0
self[i][index[i][j][k]][k] = src[i][j][k] # if dim == 1
self[i][j][index[i][j][k]] = src[i][j][k] # if dim == 2
裁剪梯度
应对梯度爆炸,将所有模型参数梯度的元素拼接成向量g,并设定裁剪阈值为$\theta$。g取min{$\theta$,g}裁剪后的梯度的$L_2$范数不超过$\theta$。
困惑度
困惑度评价语言模型的好坏,对交叉熵损失函数做指数运算。
$$
2^{-\sum_x{p(x)log_2(p(x))}}
其中p(x)=p(w_{i+1}|w_{i-k},…,w_i)
$$
困惑度的值(1,+inf)。
定义模型训练函数
- 困惑度评价
- 迭代参数前裁剪梯度
- 采样方法的不同导致隐藏状态初始化的不同
1 | # 本函数已保存在d2lzh_pytorch包中方便以后使用 |
循环神经网络简易实现
- 定义rnn层
- 输入数据one-hot表示
- rnn层进行处理
- 全连接层输出
模型定义
1 | # 本类已保存在d2lzh_pytorch包中方便以后使用 |
模型训练
通过时间反向传播
正向传播在rnn比较直观,通过时间反向传播是反向传播在rnn中的具体应用。
时间步t的损失为$l(o_t,y_t)$`
时间步数为T的损失函数为
$$
L=\frac{1}{T}\sum_{t=1}^Tl(o_t,y_t)
$$
误差项通过反向链式求导,得到与时间相关。
梯度爆炸,可直接从NaN得知,通过设阈值解决。
梯度衰减,有三种方法:
- 合理初始化权重值,躲开梯度消失区域。
- 使用ReLu代替sigmoid和tanh作为激活函数。
- 使用其他结构的rnn,例如lstm和gru。
门控循环单元 gru
能够很好的捕捉时间序列中时间步距离较大的依赖关系,较早时刻的隐藏状态通过时间保存并传递到当前时间步t,抑制梯度衰减问题。
重置门
$R_t=\sigma(X_tW_{xr}+H_{t-1}W_{hr}+b_r)$
候选隐藏状态
$\bar{H_t}=tanh(X_tW_{xh}+R_t·H_{t-1}W_{hh}+b_h)$
重置门丢弃与预测无关的历史信息。
更新门
$Z_t=\sigma(X_tW_{xz}+H_{t-1}W_{hz}+b_z)$
隐藏状态
$H_t=Z_t·H_{t-1}+(1-Z_t)·\bar{H_t}$
更新们可以控制隐藏状态如何被包含当前时间步信息的候选隐藏状态所更新。
- 重置门:捕捉时间序列里短期的依赖关系。
- 更新们:捕捉时间序列里长期的依赖关系。
长短期记忆 LSTM
隐藏层输出包括隐藏状态和记忆细胞,仅隐藏状态会传到输出层。
输入门、遗忘门、输出门
控制信息的流动。与gru的重置门和更新门一样的输出公式,激活函数为sigmoid。
$$
I_t=\sigma(X_tW_{xi}+H_{t-1}W_{hi}+b_i)
F_t=\sigma(X_tW_{xf}+H_{t-1}W_{hf}+b_f)
O_t=\sigma(X_tW_{xo}+H_{t-1}W_{ho}+b_o)
$$
候选记忆细胞
输出公式与上类似,激活函数为tanh。
$$
\bar{C_t}=tanh(X_tW_{xc}+H_{t-1}W_{hc}+b_c)
$$
记忆细胞
当前时间步记忆细胞组合上一时间步记忆细胞和当前时间步候选细胞的信息,通过遗忘门和输入门来控制信息流动。
$$
C_t=F_t·C_{T-1}+I_t·\bar{C_t}
$$
若过去的记忆细胞一直通过时间保存病传递到当前时间步,则可以应对梯度衰减问题,更好地捕捉时间序列中时间步距离较大的依赖关系。
隐藏状态
输出门控制记忆细胞到隐藏状态。
$$
H_t=O_t·tanh(C_t)
$$
深度循环神经网络
含多个隐含层的rnn。每个隐含状态不断传递到当前层的下一时间步和当前时间步的下一层。
双向循环神经网络
当前时间步由较早时间步的序列决定的,信息丛前往后传递。
信息从后往前传递的隐藏层–>双向循环神经网络。