输入 x1 单词对应一个embedding,Ex1
输出 y' = [y1', y2', ...] (任务不同,使用的y不同:词性标注,每个y'都要;情感分类,仅需要最后一个yt')
隐层 h2 = tanh(w_h1*h1 + w_xh2 * Ex2 + b_l1)
y2' = g(w_hy2 * h2 + b_l2)
y2' 是 (0.01, 0.02, 0.001, ....) R|V|, 词库里每个单词的概率
groundtruth y2 = (0,0,1,...) R|V|, 表示x3这个词的独热编码
损失函数 l_t2 = loss(y2, y2')
需要学习的参数:{ W_xh, W_h, W_hy, b_ln, Ex}
语言模型回顾:P_lm(S) = P_lm(w1,w2,...,wn)
为什么RNN会出现梯度爆炸或者消失的问题?
1.RNN是深度学习模型,在时间维度上的叠加,和普通的NN堆叠不一样。当然也可以在空间上堆叠多层
LSTM
- 参考https://towardsdatascience.com/illustrated-guide-to-lstms-and-gru-s-a-step-by-step-explanation-44e9eb85bf21
LSTM是用来解决RNN的梯度问题,起到了有效缓解作用
LSTM比RNN多出了几个门,用来控制信息的流动
LSTM的效率要比RNN低,毕竟计算上多出了更多步骤
LSTM能比RNN捕获更长的依赖关系
FAQ
1.为什么门控(遗忘门、输入门、输出门)用sigmoid做激活函数,而求值(ct~, ht)是用tanh做激活函数?
答:该问题分为3个子问题回答。
1)为什么因为门控和求值用不同的激活函数?因为门是控制开闭的,全开时值为0,全闭值为0。有开有闭时,值在0到1之间。
对于求值时的激活函数,选取时我认为与深层网络中激活函数选取是一样的,没有行与不行,只有好与不好。
2)为什么使用sigmoid函数与tanh函数这种“饱和函数”?LSTM应该需要饱和的门来记住或忘记信息,不饱和的门会使得过去和现在的记忆一直在叠加,造成记忆错乱。饱和门也就是说在输入达到一定值的情况下,输出就不会发生明显变化了。如果是用非饱和的激活图数,例如ReLU,那么将难以实现门控的效果。
3)为什么使用sigmoid函数与tanh函数?
关键点:归一化,0中心;收敛快
使用tanh函数生成候选记忆,是因为其输出在-1-1之间,这与大多数场景下特征分布是0中心的吻合。此外,tanh函数在输入为0近相比 sigmoid函数有更大的梯度,通常使模型收敛更快。使用sigmoid函数,一是由于门控需要0-1的值域,二是经验习得。
"""
LSTM伪代码
"""
def LSTMCell(x, h, c):
data = concat(h,x)
# 遗忘门
forget_gate = sigmoid(W_f · data + b_f)
# 输入门
input_gate = sigmoid(W_i · data + b_i)
# 输出门
output_gate = sigmoid(W_o · data + b_o)
# 生成候选记忆
c_t~ = tanh(W_c · data + b_c)
# 生成当前记忆
c_t = forget_gate · c_t-1 + input_gate · c_t~
# 输出
ht = output_gate · tanh(c_t)
return ct, ht
情感分类代码
depth(num_layers) 指的是多层LSTM,一般只有一层。
关于h0 c0初始化:
一般翻译的话用 output,分类用hn
self.decoder 是一个二分类,单向LSTM的话,需要*2,是因为二分类
from_pretrained(), 用一个已经训练好的weight
require_grad = False, weight不需要梯度下降来更新
permute ,维度调换
初始状态和最终状态concate 是因为是双向LSTM
扩展API