本打算详细梳理下RNN这块的内容,但是看都下面这篇博客写的很好,就拿来细细阅读,并且多不理解的地方做出补充,同时加上代码进一步深入理解。
首先感谢博主:
https://blog.csdn.net/zhaojc1995/article/details/80572098
一.补充
1.RNN
- 大项目中可能RNN和CNN共同存在。
2.LSTM
-
细胞状态的改变只做线性运算。加分(增加信息)和乘法(去除信息)。
-
更新之前细胞状态( 遗忘门 ):选择性通过信息。 softmax函数输出范围再0-1之间,不为0或者1。
-
更新当前特征量(输入门): sigmoid函数 取值范围在0-1之间,tanh激活层与RNN一样,只有唯一的激活。将sigmoid函数得到的结果(0-1)与tanh激活层得到的结果(所有当前得到的特征量)相乘,控制tanh激活层输入细胞状态多少。
-
输出门:将细胞状态使用tanh激活,得到当前状态,再使用sigmoid函数激活,表示当前输出多少。结果做当前输出和传给t+1时刻两种。
- 乘法:忘掉细胞状态。
- 加法:输入/更新细胞状态。
- sigmoid函数选择更新内容,tanh函数创建更新候选。
3.变种
(1)GRU
- 没有细胞状态,参数比普通LSTM网络少1/3(筛选越多,参数越少),计算更快。效果与LSTM差不多。广泛使用。
- 只筛选了两次。
(2)SRU
- 细胞状态没有激活,输出有激活。
(3)双向LSTM
二.LSTM代码理解
使用RNN识别MNIST数据集。
- 图片大小:28x28。
- CNN结构:NCHW
- 全连接:NV。
- RNN结构:NSV。
- 将28x28的图片分为28步(如下图),所以向量为28,步长为1,需要28步。通道放到批次上,将N变大。
-
从第一部往后走。后边一步代表前面所有步。
-
输入大小28x28,通过权重变成128或者256个特征。
-
NSV:100,28,28。
-
(28,28)x(28,128)(权重)–>(28x128)。即,(28个步长,第一竖列28个像素)x(28x128)的权重,得到(28x128),这里的128代表第一竖列28个像素值。最后特征还是为128维度。并且将128变为10进行计算。神经网络简易图如下所示:
-
LSTM原始API:
class RNNBase(Module):
__constants__ = ['mode', 'input_size', 'hidden_size', 'num_layers', 'bias',
'batch_first', 'dropout', 'bidirectional']
def __init__(self, mode, input_size, hidden_size,
num_layers=1, bias=True, batch_first=False,
dropout=0., bidirectional=False):
super(RNNBase, self).__init__()
- LSTM使用
def __init__(self):
super(Net, self).__init__()
# nn.GRU()
# nn.LSTMCell()
self.lstm = nn.LSTM(
input_size=28,#一个步长,28个特征点。
hidden_size=128,#隐藏层128。
num_layers=2,#LSTM做几次,层数越多,权重越多,更新起来越慢。一般双向用两层。单项用一层和两层没多大区别。
batch_first=True,#是否将批次加到第一维。一般的,如果不把批次放到第一维,会跑到第二维,变成(S,N,V)。通常为True
bidirectional=True#是否为双向。双向时,特征值增加一倍,变为128x2.
)
# self.out = nn.Linear(128, 10)
self.out = nn.Linear(128*2, 10)
- LSTM前向传播
def forward(self, x):
r_out, (h_t,c_t) = self.lstm(x, None)#r_out:(28,128);h_t:隐藏层的输出;c_t:细胞状态。每一步,h_t,c_t形状一样,一般不用,但是要输出,给个占位符。
out = self.out(r_out[:, -1, :])#切片:N和V维度不变,S取最后一步,即,取第28步,拿到(100,128)。再乘以(128,10)(即self.out())变成(100,10)。r_out形状为(100,28,128),输出最后一步形状变为(100,128).其中,100为批次。(100,128)x(128,10)-->(100,10)
return out