TensorFlow中实现RNN,彻底弄懂time_step

  这篇博客不是一篇讲解原理的博客,这篇博客主要讲解tnesorlfow的RNN代码结构,通过代码来学习RNN,以及讲解time_steps,如果这篇博客没有让你明白time_steps,欢迎博客下面评论交流。

  我曾翻阅各大网站,各大博客,他们的对RNN中time_steps的讲解,都没有一个让人醍醐灌顶的答案,甚至让人越看模糊。有的博主在博客中讲的看似他懂了,一问他自己他答不上来。在这里,我向全中国还迷糊在time_step的学者答疑,立此博文。

学习RNNCell要重点关注三个地方:

  • 类方法call
  • 类属性state_size
  • 类属性output_size

RNNCell

想要看懂tensorflow RNN代码,我们必须要先了解RNNCell,RNNcell 是 tensorlfow中实现RNN的基本单元。我们平时在代码中用的是RNNcell的子类,BasicRNNCell(RNN的基础类)和BasicLSTMCell(LSTM的基础类)。为了方便,我用cell对这两个类进行统称。

使用方式是:(output, next_state) = call(input, state)

理解例子:输入序列是:$x_1、x_2、x_3$,RNN的初始状态为$h_0$

t=1时刻,$(output_1, h_1) = cell(x_1,h_0)$

t=2时刻,$(output_2, h_2) = cell(x_2,h_1)$

t=3时刻,$(output_3, h_3) = cell(x_3,h_2)$

每调用一次RNNCell的call方法,就相当于在时间上推进了一步

RNNCell中还有两个输出比较重要,state_size(隐层的大小)output_size(输出的大小)

设输入数据的形状为(batch_size, input_size),那么计算时得到的隐层状态就是(batch_size, state_size),输出就是(batch_size, output_size)。

import tensorflow as tf

cell = tf.nn.rnn_cell.BasicRNNCell(num_units=128)  # state_size = 128
# cell = tf.keras.layers.SimpleRNNCell(units=128)
print(cell.state_size)  # 128

# 32 是 batch_size
inputs = tf.placeholder(tf.float32, shape=(32, 100))

# 通过zero_state得到一个全0的初始状态,形状为(batch_size, state_size)
h0 = cell.zero_state(32, tf.float32)        # (32, 128)

# 调用call函数
output, h1 = cell.__call__(inputs, h0)

print(h1.shape)  # (32, 128)

对于BasicLSTMCell,因为LSTM可以看做有两个隐状态h和c,对应的隐层就是一个Tuple,每个都是(batch_size, state_size)的形状:

import tensorflow as tf

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=128)
inputs = tf.placeholder(tf.float32, shape=(32, 100)) # 32 是 batch_size
h0 = lstm_cell.zero_state(32, tf.float32) # (32,128)
output, h1 = lstm_cell.__call__(inputs, h0)

print(h1.h.shape)  # shape=(32, 128)
print(h1.c.shape)  # shape=(32, 128)

tf.nn.static_rnn

  tf.nn.static_rnn——随时间静态展开。static_rnn() 返回两个对象,第一个是每一时刻time_steps RNN输出的列表,另一个是RNN网络的最终状态state。下面代码举例time_steps=2的输入。

X0 = tf.placeholder(tf.float32, [None, n_inputs])
X1 = tf.placeholder(tf.float32, [None, n_inputs])
 
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
output_seqs, states = tf.contrib.rnn.static_rnn(basic_cell, [X0, X1], dtype=tf.float32)
 
Y0, Y1 = output_seqs

如果有50个tiime_steps时刻,操作50个输入占位符实在太繁琐了,假如输入shape=(None, time_steps, imput_size),可以用如下方法一并输入

X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
X = tf.transpose(X, perm=[1, 0, 2])     # shape=(n_steps, batchs ,n_inputs)
X_seqs = tf.unstack(X)      # time_steps个(batchs, n_inputs)的列表

basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
output_seqs, states = tf.contrib.rnn.static_rnn(basic_cell, X_seqs, dtype=tf.float32)
 
outputs = tf.transpose(tf.stack(output_seqs), perm=[1, 0, 2])

最终的outputs是一个包含所有实例、任一时刻、所有神经元的输出的张量。幸运的是,还有更好的解决方案,那就是dynamic_rnn()函数。

tf.nn.dynamic_rnn

  tf.nn.dynamic_rnn——随时间动态展开。基础的RNNCell有一个很明显的问题:对于单个的RNNCell,我们使用它的call函数进行运算时,只是在序列时间上前进了一步。如果我们的序列长度为10,就要调用10次call函数,比较麻烦。对此,TensorFlow提供了一个tf.nn.dynamic_rnn函数,该函数就相当于调用了n(输入数据的格式为(batch_size, time_steps, input_size),其中time_steps表示序列本身的长度,如在Char RNN中,长度为10的句子对应的time_steps就等于10。最后的input_size就表示输入数据单个序列单个时间维度上固有的长度。另外我们已经定义好了一个RNNCell,调用该RNNCell的call函数time_steps次,对应的代码就是:)次call函数。即通过${h_0,x_1, x_2, …., x_n}$直接得${h_1,h_2…,h_n}$。

举个例子:假设输入数据的格式为(batch_size, time_steps, input_size),其中time_steps表示序列本身的长度,如在NLP中,一句话有25个字,每个字的向量维度为300,那么time_steps就是句子的长度=25,input_size=300。另外我们已经定义好了一个RNNCell,调用该RNNCell的call函数time_steps次,对应的代码就是:

outputs, state = tf.nn.dynamic_rnn(cell, inputs, initial_state=initial_state)

参数:

  • inputs: 输入序列 shape = (batch_size, time_steps, input_size)
  • cell: RNNCell
  • initial_state: 初始状态。一般可以取零矩阵shape = (batch_size, cell.state_size)。

返回:

  • outputs:time_steps步里所有输出,shape=(batch_size, time_steps, cell.output_size)
  • state:最后一步的隐状态,它的形状为(batch_size, cell.state_size)
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])   # (batch_size, time_steps,input_size)
 
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)

变长输入序列

  前面我们处理的输入shape=(batch_size, time_step, input_size),输入序列是定长的,拿我们做自然一样处理为例子,如果数据有1000段时序的句子,每句话有25个字,对每个字进行向量化,每个字的向量维度为300,那么batch_size=1000,time_steps=25,input_size=300。但是每句话的句子长度都是不一样的,这时候我们就需要在调用dynamic_rnn()(或者static_rnn)时使用sequence_length参数。指明了每一实例输入序列的长度。例如:

X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])   # (batch_size, time_steps,input_size)
seq_length = tf.placeholder(tf.int32, [None])

basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, sequence_length=seq_length, dtype=tf.float32)

假设我们输入的第二个实例只有一个时刻的输入,表示该实例张量的第二维需要补零,如下所示:

X_batch = np.array([
    # step 0     step 1
    [[0, 1, 2], [9, 8, 7]], # instance 0
    [[3, 4, 5], [0, 0, 0]], # instance 1 (padded with a zero vector)
    [[6, 7, 8], [6, 5, 4]], # instance 2
    [[9, 0, 1], [3, 2, 1]], # instance 3
])
seq_length_batch = np.array([2, 1, 2, 2])
 
with tf.Session() as sess:
    init.run()
    outputs_val, states_val = sess.run([outputs, states], feed_dict={X: X_batch, seq_length: seq_length_batch})

tf.nn.rnn_cell.MultiRNNCell

单层RNN能力有限,我们需要多层的RNN。将x输入第一层RNN的后得到隐层状态h,这个隐层状态就相当于第二层RNN的输入,第二层RNN的隐层状态又相当于第三层RNN的输入,以此类推。在TensorFlow中,可以使用tf.nn.rnn_cell.MultiRNNCell函数对RNNCell进行堆叠

import tensorflow as tf

# 每调用一次这个函数就返回一个BasicRNNCell
def get_a_cell():
    return tf.nn.rnn_cell.BasicRNNCell(num_units=128)
# 用tf.nn.rnn_cell MultiRNNCell创建3层RNN
cell = tf.nn.rnn_cell.MultiRNNCell([get_a_cell() for _ in range(3)]) # 3层RNN
# 得到的cell实际也是RNNCell的子类
# 它的state_size是(128, 128, 128)
# (128, 128, 128)并不是128x128x128的意思
# 而是表示共有3个隐层状态,每个隐层状态的大小为128
print(cell.state_size) # (128, 128, 128)
# 使用对应的call函数
inputs = tf.placeholder(tf.float32, shape=(32, 100)) # 32 是 batch_size
h0 = cell.zero_state(32, tf.float32) # 通过zero_state得到一个全0的初始状态
output, h1 = cell.__call__(inputs, h0)

print(h1) # tuple中含有3个32x128的向量
# (<tf.Tensor 'multi_rnn_cell/cell_0/basic_rnn_cell/Tanh:0' shape=(32, 128) dtype=float32>, 
# <tf.Tensor 'multi_rnn_cell/cell_1/basic_rnn_cell/Tanh:0' shape=(32, 128) dtype=float32>, 
# <tf.Tensor 'multi_rnn_cell/cell_2/basic_rnn_cell/Tanh:0' shape=(32, 128) dtype=float32>)

RNN的其他变种

### ------------ LSTM ------------- ###
lstm_cell = tf.contrib.rnn.BasicLSTMCell(num_units=n_neurons)
# peephole connections
# 让长期记忆也参与控制门的管理可能会更好
lstm_cell = tf.contrib.rnn.LSTMCell(num_units=n_neurons, use_peepholes=True)

### ------------ GRU ------------- ###
gru_cell = tf.contrib.rnn.GRUCell(num_units=n_neurons)

time_steps专栏

有的人学习到RNN的时候,死活都弄不清batch、input_size、time_steps。在这篇博文中,我做一个专栏。

文字数据

  如果数据有1000段时序的句子,每句话有25个字,对每个字进行向量化,每个字的向量维度为300,那么batch_size=1000,time_steps=25,input_size=300。

解析:time_steps一般情况下就是等于句子的长度,input_size等于字量化后向量的长度。

图片数据

  拿MNIST手写数字集来说,训练数据有6000个手写数字图像,每个数字图像大小为28*28,batch_size=6000没的说,time_steps=28,input_size=28,我们可以理解为把图片图片分成28份,每份shape=(1, 28)。

音频数据

  假如训练数据有225段语音,每段语音对其进行分帧处理,每段语音有480帧,每一帧数据shape=(8910,),则输入shape=(225, 480 8910),batch_size=225,time_steps就是,每段语音的帧数,time_steps=480,input_size=8910

我们使用RNN永远只需要记住,输入数据一定要是时序的,不能用这一段语音的语音帧,接下一段语音的语音帧,因为这两段语音之间没有时域连续性。

  RNN数据一定要是三维的,第一维是batch_size,第二维是time_steps,第三位是数据input_size。

参考文献

知乎-何之源:TensorFlow中RNN实现的正确打开方式

学习RNN练手项目 Char-RNN

royhoo的博客园——循环神经网络

转载于:https://www.cnblogs.com/LXP-Never/p/9678423.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值