TensorFlow 15——ch12-RNN、LSTM基本结构
目录
-
-
- RNNCell
- RNN 基本单元
- LSMT 基本单元
- MultiRNNCell
- BasicRNNCell 的 call
- BasicLSTMCell 的 call
- 展开时间维度
-
RNNCell
RNNCell 是 Tensorflow 中的 RNN 基本单元,是一个抽象类,没有办法实体化,要用的是两个子类,一个是 BasicRNNCell
,一个是 BasicLSTMCell
。
RNNCell 有一个 call
函数,是 RNN 的单步计算,调用:
(output, next_state) = call(input, state)
初始输入为 x1,初始的隐藏层为 h0,例如:
(output1, h1) = cell.call(x1, h0) # 得到h1
(output2, h2) = cell.call(x2, h1) # 得到h2
RNNCell 的类属性 :
- state_size 规定了隐藏层的大小
- output_size 规定了输出向量的大小
RNN 基本单元
import tensorflow as tf
rnn_cell = tf.nn.rnn_cell.BasicRNNCell(num_units=128)
print(rnn_cell.state_size)
LSMT 基本单元
import tensorflow as tf
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=128)
print(lstm_cell.state_size)
可以看到 BasicLSTMCell 的 state_size 由 c
和 h
两部分组成。
所以一般使用 BasicLSTMCell 的时候,分开这两部分:
import tensorflow as tf
import numpy as np
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=128)
inputs = tf.placeholder(np.float32, shape=(32, 100))
h0 = lstm_cell.zero_state(32, np.float32)
output, h1 = lstm_cell.call(inputs, h0)
print(h1.h)
print(h1.c)
MultiRNNCell
单层 RNN 的能力有限,所以需要多层的 RNN,也就是第一层的输出 h 作为第二层的输入。
可以使用tf.nn.rnn_cell.MultiRNNCell 函数对 RNN 进行堆叠。测试代码如下:
# 返回一个BasicLSTMCell
def get_a_cell():return tf.nn.rnn_cell.BasicLSTMCell(128)# 创建3层的RNN,state_size=(128,128,128),表示3个隐层大小都为128
cell = tf.nn.rnn_cell.MultiRNNCell([get_a_cell() for _ in range(3)])inputs = tf.placeholder(np.float32, shape=(32, 100))
h0 = cell.zero_state(32, np.float32)
output, h1 = cell.call(inputs, h0)
print(h1)
打印结果(换行是我加的):
LSTMStateTuple(
c=<tf.Tensor 'cell_0/cell_0/basic_lstm_cell/add_1:0' shape=(32, 128) dtype=float32>,
h=<tf.Tensor 'cell_0/cell_0/basic_lstm_cell/mul_2:0' shape=(32, 128) dtype=float32>
),
LSTMStateTuple(
c=<tf.Tensor 'cell_1/cell_1/basic_lstm_cell/add_1:0' shape=(32, 128) dtype=float32>,
h=<tf.Tensor 'cell_1/cell_1/basic_lstm_cell/mul_2:0' shape=(32, 128) dtype=float32>
),
LSTMStateTuple(
c=<tf.Tensor 'cell_2/cell_2/basic_lstm_cell/add_1:0' shape=(32, 128) dtype=float32>,
h=<tf.Tensor 'cell_2/cell_2/basic_lstm_cell/mul_2:0' shape=(32, 128) dtype=float32>
)
BasicRNNCell 的 call
BasicRNNCell 的 call 的 return :
def call(self, inputs, state):if self._linear is None:self._linear = _Linear([inputs, state], self._num_units, True)output = self._activation(self._linear([inputs, state]))return output, output
可以看出在 BasicRNNCell 中 output(输出) 和隐状态是一样的,因此需要额外对输出定义新的变换,才能得到图中真正的输出 y 。
而隐状态就是函数中的 output(函数),所以 BasicRNNCell 中 state_size 永远等于 output_size 。
BasicLSTMCell 的 call
BasicLSTMCell 的 call 的 return :
if self._state_is_tuple:new_state = LSTMStateTuple(new_c, new_h)
else:new_state = array_ops.concat([new_c, new_h], 1)
return new_h, new_state
其中 _state_is_tuple 是一直等于 Ture
的,所以返回的隐状态是 LSTMStateTuple(new_c, new_h)
,而output是 new_h
。
因此如果处理的是分类问题,还需要对 output 添加单独的 Softmax 层才能得到最后的分类概率输出。
展开时间维度
对单个的 RNNCell ,如果序列长,则需要调用n次call,所以 Tensorflow 提供了一个函数 tf.nn.dynamic_rnn
,这个函数相当于就调用了n次call。
def dynamic_rnn(cell, inputs, sequence_length=None, initial_state=None, dtype=None, parallel_iterations=None, swap_memory=False, time_major=False, scope=None):
输入的 inputs 数据格式:
inputs: shape = (batch_size, time_steps, input_size)
- batch_size:batch的大小
- time_steps:长度,也就是 time_steps 次call
- input_size:表示输入数据单个序列单个时间维度上固有的长度