RNN基本公式
在普通神经网络中引入了隐藏状态
h
t
=
t
a
n
h
(
w
i
h
x
t
+
b
i
h
+
w
h
h
h
t
−
1
+
b
h
h
)
h_t=tanh(w_{ih}x_t+b_{ih}+w_{hh}h_{t-1}+b_{hh})
ht=tanh(wihxt+bih+whhht−1+bhh)
什么是Dynamic RNN
静态RNN:由于静态RNN需要预先指定seq_len,所以所有数据都必须使用同一个seq_len,即每一个batch的seq_len是一致的
动态RNN:由于利用循环次数实现了seq_len的作用,所以不同的batch可以有不同的seq_len,但是同一个batch内的seq_len还是保持一致的
注:左图和右图等价,但是右图等于把左图展开了,右图即static rnn,左图为更紧凑的dynamic rnn
为什么要用batch呢?
- 因为batch size可以加速训练
- 而且可以根据一个batch内的样本来更新参数,防止梯度下降的随机性,易于收敛
- 但是更大的batch会陷入局部最优值
为什么要用Dynamic RNN
dynamic RNN可以让不同迭代传入的batch可以是长度不同的数据,但同一次迭代一个batch内部的所有数据长度仍然是固定的。例如,第一时刻传入的数据shape=[batch_size, 10],第二时刻传入的数据shape=[batch_size, 12],第三时刻传入的数据shape=[batch_size, 8]等等;一般通过为batch内不同长度的sequence进行补零操作至相同长度后,与真实seq_len一同输入模型实现。
如何在pytorch中实现Dynamic RNN
- 输入数据处理
每次取数据时,要求先将不同长度的原始sequence补零至同一长度(max_seq_len),注意这里要返回seq_len
# 在dataset的定义中
def __getitem__(self, idx):
# data: seq_len * input_size
data, label, seq_len = self.train_data[idx]
# pad_data: max_seq_len * input_size
pad_data = np.zeros(shape=(self.max_seq_len, data.shape[1]))
pad_data[0:data.shape[0]] = data
# 这里可以在batch迭代时使用sample["data"]即可返回batch后的数据
sample = {'data': pad_data, 'label': label, 'seq_len': seq_len}
return sample
pack_padded_sequence & pad_packed_sequence
将补零后的数据重新打包整理,给出在RNN循环中每一步有几个数据同时进行运算
tensor([[[ 1.],
[ 2.],
[ 3.]],
[[ 1.],
[ 0.],
[ 0.]]])
————>
PackedSequence(data=tensor([[ 1.],
[ 1.],
[ 2.],
[ 3.]]), batch_sizes=tensor([ 2, 1, 1]))
该PackedSequence
对象可以直接输入RNN模型中,其中Pytorch在内部循环时如果发现是补的零,将不会添加进最终结果,对于h_n
我们可以直接使用,对于output
我们需要将其从PackedSequence
对象还原成原始补零的Tensor
PackedSequence(data=tensor([[ -0.3207, -0.4567],
[ 0.6665, 0.0530],
[ 0.4456, 0.1340],
[ 0.3373, -0.3268]]), batch_sizes=tensor([ 2, 1, 1]))
————>
tensor([[[ -0.3207, -0.4567],
[ 0.6665, 0.0530]],
[[ 0.4456, 0.1340],
[ 0.0000, 0.0000]],
[[ 0.3373, -0.3268],
[ 0.0000, 0.0000]]]) tensor([ 3, 1])
官网api
官网Example演示pack和pad操作;注意这里enforce_sorted=False
>>> from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
>>> seq = torch.tensor([[1,2,0], [3,0,0], [4,5,6]])
>>> lens = [2, 1, 3]
>>> packed = pack_padded_sequence(seq, lens, batch_first=True, enforce_sorted=False)
>>> packed
PackedSequence(data=tensor([4, 1, 3, 5, 2, 6]), batch_sizes=tensor([3, 2, 1]),
sorted_indices=tensor([2, 0, 1]), unsorted_indices=tensor([1, 2, 0]))
>>> seq_unpacked, lens_unpacked = pad_packed_sequence(packed, batch_first=True)
>>> seq_unpacked
tensor([[1, 2, 0],
[3, 0, 0],
[4, 5, 6]])
>>> lens_unpacked
tensor([2, 1, 3])
参考来源:
动态RNN和静态RNN区别
如何理解RNN中的Batch_size?
tensor flow dynamic_rnn 与rnn有啥区别?
[莫烦 PyTorch 系列教程] 5.1 – 为什么 Torch 是动态的
训练神经网络时如何确定batch size?
Pytorch实现RNN