文章目录
摘要
This week, I read a paper on recurrent neural networks, it is mentioned in the paper that although Dropout has achieved good results in regularizing neural networks, it does not perform well in recurrent neural networks, and Overfitting will occur. Therefore, the paper proposes solutions, Dropout is not used in the structure of circular connection in the network, this method solves the above problems and improves the performance of the program. The paper shows its results in three areas, and also proves that the idea is feasible. Finally, I try to implement a simple recurrent neural network with code.
本周,我阅读了一篇关于循环神经网络的论文,论文中提到虽然Dropout对神经网络进行正则化取得不错效果,但是在循环神经网络中表现不佳,并会出现过拟合现象。因此论文中提出了解决方法,对网络中没有在循环连接的结构中使用Dropout,这个方法解决上面的问题,并且提高程序的性能。论文在3个领域中展示了自己的结果,并且也证实了想法是切实可行的。最后,我尝试用代码去实现简单的循环神经网络。
论文阅读
1.题目
文献链接:RECURRENT NEURAL NETWORK REGULARIZATION
2.摘要
We present a simple regularization technique for Recurrent Neural Networks (RNNs) with Long Short-Term Memory (LSTM) units. Dropout, the most successful technique for regularizing neural networks, does not work well with RNNs and LSTMs. In this paper, we show how to correctly apply dropout to LSTMs, and show that it substantially reduces overfitting on a variety of tasks. These tasks include language modeling, speech recognition, image caption generation, and machine translation.
3.网络结构
3.1 网络示意图
RNN是一类用于处理序列数据的神经网络,其中包括输入层、隐藏层、输出层,通过激活函数控制输出,层与层之间通过权值连接。如下图所示,一个RNN的结构图,从图中可以发现此结构的循环体现在隐藏层,并且箭头连接上都带有权值。
其中:x(t):表示t时刻的输入,o(t):表示t时刻的输出,s(t):表示t时刻的记忆。
RNN的基础公式:s(t) = f(U * x(t) + w * s(t-1))
RNN通过当前时刻的记忆s(t)去预测下一个词出现的概率,因此运用softmax去预测每个词出现的概率,但预测不能直接用一个矩阵来预测,需要带一个权重矩阵V,用公式表示为:o(t) = softmax(V * s(t))
3.2 网络特点
1)权值共享,图中的W、U和V全部相同。
2)前面的输出会影响到后面的输出,因此适合处理序列数据。
3)损失是随着序列的推进而不断积累的。
4.问题的提出
问题:Dropout是使神经网络正则化最成功的技术,但在RNN中表现不佳,并且在比较大的RNN网络中,常常出现过拟合现象。
解决方案:Dropout在RNN中表现不佳的原因是循环增大了噪声,而该噪声会对学习效果产生不利影响的问题。于是论文中提出了一个使用dropout的技巧,通过在RNN的一些子集连接中使用dropout来解决这个问题。
5.正则化带有LSTM单元的RNNs
5.1 LSTM单元
RNN动态地描述了之前状态到当前状态的隐藏转变,可以用以下函数形式表示:
在经典的RNNs网络中,该函数表达如下形式;
LSTM具有复杂的动态特性,可以记住一定数量步长的的记忆信息,同时使得上述过程复杂化。长期的记忆信息在一个存储单元矢量中,表示为CtL∈Rn,因此LSTM的结构可以由以下方程表示:
5.2 具有 Dropout 的正则化
如下图所示,LSTM的结构表示:
论文提出解决方案的突破点是没有在循环连接的结构中使用Dropout,因为有循环连接结构中不适用Dropout,因此方程被修改为以下形式:
从下图中,我可以知道虚线表示使用了Dropout,实线表示没有使用Dropout:
论文中采用的解决方式使得RNN之间的信息传递,数据计算更加具有鲁棒性,同时也保证了数据之间的相关性和完整性。从下图中可以看到,信息从 t-2 步流向到 t+2 步中,实线表示信息数据的流向。
6.实验
论文中提出在三个领域展示自己的实验成果:语言建模、语音识别、机器翻译和图像字幕生成。
6.1 语音建模
在Penn Tree Bank数据集Marcus等人的基础上进行了单词级预测实验。将以前的结果与论文中LSTM进行了比较,结果如下所示:
6.2 语音识别
声学建模是将声学信号映射到单词序列的关键组件,因为它对p(st|X)进行建模,其中st是时间t的语音状态,而X是声学观测。通过研究表明,Dropout提高了LSTM的帧精度,结果如下所示:
6.3 机器翻译
论文中将机器翻译问题表述为一个语言建模任务,其中训练了LSTM为源句子的正确翻译分配高概率。通过研究表明,Dropout提高了LSTM的翻译性能,结果如下所示:
6.4 图像字幕生成
将Dropout变体应用于Vinyals等人的图像字幕生成模型,结果表明相对于不使用Dropout,Dropout是有帮助的,但是使用集成消除了Dropout所获得的增益。结果如下所示:
7.结论
论文中提出一种将Dropout应用于LSTM简单的方法,该方法可以使在不同领域的几个问题的性能得到大幅提高。这个方法对于RNNs很有用,实验结果表明Dropout可以提高各种应用程序的性能。
深度学习
Pytorch实现简单的RNN
参考链接:https://blog.csdn.net/qq_41775769/article/details/121707309
- 通过 PyTorch 内置的 torch.nn.RNNCell 方法实现一个简单的单隐藏循环神经网络:
1 ) 初始化参数
import torch
from torch import nn
# 初始化参数
input_size = 4 # 输入层特征维度
hidden_size = 2 # 隐藏层特征维度
batch_size = 5 # 批量大小
seq_len = 3 # 序列长度
2)创建网络模型
class RNNCell(nn.Module):
def __init__(self, input_size, hidden_size):
super(RNNCell, self).__init__()
self.rnn = nn.RNNCell(
input_size=input_size,
hidden_size=hidden_size,
)
def forward(self, x, y):
out = self.rnn(x, y)
return out
net = RNNCell(input_size, hidden_size)
3)我们需要通过循环的方式将多个 RNNCell 连接起来,构成一个序列长度为 3 的单隐藏循环神经网络。
其中:初始化第一个隐藏层单元全部设置为0
dataset = torch.randn(seq_len, batch_size, input_size)
hidden = torch.zeros(batch_size, hidden_size)
for index, input in enumerate(dataset):
print("=" * 25, index, "=" * 25)
print("Input Size:", input.shape)
print(input)
hidden = net(input, hidden)
print("outputs size:", hidden.shape)
print(hidden)
4)输出结果如下:
========================= 0 =========================
Input Size: torch.Size([5, 4])
tensor([[-0.4355, -0.8889, -0.7113, -1.7701],
[ 0.7077, -0.5906, -0.7883, -0.6755],
[ 0.2374, 0.9325, -2.8647, -0.4067],
[-1.9673, -1.0559, -1.4416, 0.2632],
[-0.9249, -1.2202, -1.2100, 0.6101]])
outputs size: torch.Size([5, 2])
tensor([[-0.9052, -0.2139],
[ 0.0105, -0.6293],
[ 0.6009, 0.2832],
[-0.8218, 0.4057],
[-0.4313, -0.2500]], grad_fn=<TanhBackward0>)
========================= 1 =========================
Input Size: torch.Size([5, 4])
tensor([[-0.9352, -0.6076, -0.1785, -0.9232],
[ 1.1922, 2.1866, 0.7483, 1.9199],
[-0.1916, -0.7159, 1.5545, -1.1090],
[-0.2615, 0.5862, 0.3292, -2.8022],
[ 0.2399, 0.6184, -1.4309, -0.4018]])
outputs size: torch.Size([5, 2])
tensor([[-0.8108, 0.0502],
[ 0.9996, 0.6874],
[-0.4933, -0.1337],
[-0.8944, 0.1895],
[ 0.5667, 0.1658]], grad_fn=<TanhBackward0>)
========================= 2 =========================
Input Size: torch.Size([5, 4])
tensor([[ 9.1031e-01, 3.0937e-01, -5.6861e-01, 1.6473e+00],
[ 2.2319e-01, 2.4434e-01, 1.7937e+00, -3.8823e-02],
[-5.2079e-01, -1.6724e+00, 7.8306e-01, 1.5158e+00],
[ 7.9093e-01, 3.8346e-01, 5.7846e-01, 1.3453e+00],
[ 6.4528e-01, -1.0996e+00, 1.7943e+00, 1.2756e-03]])
outputs size: torch.Size([5, 2])
tensor([[ 0.9637, -0.5961],
[ 0.7423, 0.0756],
[ 0.1905, -0.6469],
[ 0.9485, -0.5378],
[ 0.4275, -0.6734]], grad_fn=<TanhBackward0>)
- 通过 PyTorch 内置的 torch.nn.RNN 方法实现一个多层循环神经网络:
1 ) 初始化参数
import torch
from torch import nn
# 初始化参数
input_size = 4 # 输入层特征维度
hidden_size = 2 # 隐藏层特征维度
batch_size = 5 # 批量大小
seq_len = 6 # 序列长度
num_layers = 3 # 隐藏层的数目
2)创建网络模型
class RNN(nn.Module):
def __init__(self, input_size, hidden_size, num_layers):
super(RNN, self).__init__()
self.rnn = nn.RNN(
input_size=input_size,
hidden_size=hidden_size,
num_layers=num_layers
)
def forward(self, x, y):
r_out, h_n = self.rnn(x, y)
return r_out, h_n
net = RNN(input_size, hidden_size, num_layers)
3)实现多层循环神经网络模型
inputs = torch.randn(seq_len, batch_size, input_size)
h0 = torch.zeros(num_layers, batch_size, hidden_size)
out, hidden = net(inputs, h0)
print("Output size:", out.shape)
print("Output:", out)
print("Hidden size:", hidden.shape)
print("Hidden:", hidden)
4)输出结果如下:
Output size: torch.Size([6, 5, 2])
Output: tensor([[[-0.2231, -0.6859],
[-0.2436, -0.6917],
[-0.1823, -0.6649],
[-0.1846, -0.6672],
[-0.2742, -0.7015]],
[[ 0.2740, -0.7958],
[ 0.2787, -0.7967],
[ 0.2588, -0.7934],
[ 0.2983, -0.7813],
[ 0.2941, -0.7951]],
[[-0.0353, -0.8764],
[-0.0508, -0.8788],
[ 0.0950, -0.8508],
[-0.0125, -0.8695],
[-0.0321, -0.8745]],
[[ 0.3087, -0.8336],
[ 0.2564, -0.8476],
[ 0.1196, -0.8666],
[ 0.1984, -0.8579],
[ 0.2961, -0.8363]],
[[-0.0143, -0.8798],
[ 0.1162, -0.8589],
[ 0.1314, -0.8662],
[ 0.1592, -0.8547],
[ 0.0259, -0.8739]],
[[ 0.3072, -0.8344],
[ 0.1133, -0.8684],
[ 0.2111, -0.8481],
[ 0.0733, -0.8739],
[ 0.2018, -0.8586]]], grad_fn=<StackBackward0>)
Hidden size: torch.Size([3, 5, 2])
Hidden: tensor([[[-0.8294, 0.9059],
[-0.3150, -0.5678],
[-0.8449, 0.9023],
[-0.1080, -0.9393],
[ 0.3210, -0.9623]],
[[ 0.7452, 0.5128],
[ 0.7111, 0.7616],
[ 0.7437, 0.5205],
[ 0.7163, 0.7915],
[ 0.7727, 0.7361]],
[[ 0.3072, -0.8344],
[ 0.1133, -0.8684],
[ 0.2111, -0.8481],
[ 0.0733, -0.8739],
[ 0.2018, -0.8586]]], grad_fn=<StackBackward0>)
总结
RNN的特点是给模型一个记忆的功能,储存上一次节点的输出结果,让之后每一步的输出对于前面的输入有关,因此就算是同样的输入集合,只要改变其输入序列,输出结果就会完全不一样。对于随着时间变化的数据,会常使用到RNN,但RNN也存在一些问题,随着时序长度变长,RNN的深度也会变深,这就会导致出现梯度爆炸和梯度消失的问题。本周只是简单地学习RNN,下周会进一步深入学习RNN循环神经网络。