首先MultiRNNCell函数第一个参数是RNN实例形成的列表,第二个参数就是让状态成为一个元组。
#tf.nn.rnn_cell.MultiRNNCell([list RNNcell], state_is_tuple=True).这个函数里面主要这两个参数,
# 第一个参数就是输入的RNN实例形成的列表,第二个参数就是让状态是一个元祖,官方推荐就是用True。
方法一:
stacked_rnn = []
for iiLyr in range(layer_num):
stacked_rnn.append(tf.nn.rnn_cell.LSTMCell(num_units=hidden_size, state_is_tuple=True))
mlstm_cell = tf.nn.rnn_cell.MultiRNNCell(cells=stacked_rnn, state_is_tuple=True)
方法二:
def lstm_cell():
cell = rnn.LSTMCell(hidden_size)#, reuse=tf.get_variable_scope().reuse)###考虑中 明天问一下 问过了 没用####
return rnn.DropoutWrapper(cell, output_keep_prob=keep_prob)
mlstm_cell = tf.contrib.rnn.MultiRNNCell([lstm_cell() for _ in range(layer_num)], state_is_tuple=True)
其实方法2和方法1效果一样 只不过加了一个dropout层而已 (一般dropout层只设置output_keep_prob即可)
下面再来说一下关于双向LSTM的建立:
在上篇博客里我们说明了static和dynamic建立lstm的区别,在此我们在此再次看一下建立双向lstm的过程中static和dynamic的区别:
1.首先关于tf.contrib.rnn.static_bidirectional_rnn建立双向lstm,代码如下:(总之大体意思就是,这个已经是级联好了的,所以说output【-1】就代表我们的最终输出,他的size是【time,batch_size,hidden_size*2】*2是因为把双向的lstm进行级联)
def BiRNN(x, weights, biases):
x = tf.transpose(x, [1, 0, 2])
x = tf.reshape(x, [-1, n_input])
x = tf.split(x, n_steps)
lstm_fw_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
lstm_bw_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
outputs, _, _ = tf.contrib.rnn.static_bidirectional_rnn(
lstm_fw_cell,
lstm_bw_cell,
x,
dtype=tf.float32)
#outputs = tf.concat(outputs,2)
return tf.matmul(outputs[-1], weights) + biases
#这个地方是因为static已经对正向和反向的输出进行级联了,所以这里不需要concat 而dynamic需要进行拼接
Aliases:
tf.contrib.rnn.static_bidirectional_rnn
tf.nn.static_bidirectional_rnn
创建双向循环神经网络。
与单向循环神经网络类似,只不过双向循环神经网络同时接受前向和反向的RNN神经元,最终对前向和反向的输出进行深度级联,输出的格式如: [time][batch][cell_fw.output_size + cell_bw.output_size].前向和反向神经元的input_size必须匹配。默认情况下,前向和反向的初始状态为0,并且不反回任何中间状态。如果给定序列的长度,网络完全展开,如果没有给定长度,则不展开。
#########################################################
格式:
tf.nn.static_bidirectional_rnn(
cell_fw,
cell_bw,
inputs,
initial_state_fw=None,
initial_state_bw=None,
dtype=None,
sequence_length=None,
scope=None
)
################################################
参数说明:
cell_fw:前向神经元,如BasicRNNCell.
cell_bw:反向神经元
input:网络输入,一个长度为T的list,list中的每个Tensor元素shape为[batch_size,input_size]
initial_state_fw:可选参数。前向RNN的初始RNN,必须是合适类型的Tensor以及shape为[batch_size,cell_fw.state_size]。
Initial_state_bw:可选参数。同initial_state_fw。
dtype:初始状态的数据类型。
sequence_length:一个int32/int64的向量,长度为[batch_size],包含每个序列的实际长度。
scope:默认为”bidirectional_rnn
#################################################
返回:
一个(outputs,output_state_fw,output_state_bw)的元组,其中,outputs是一个长度为T的list,list中的每个元素对应每个时间步的输出,它们是深度级联的前向和反向输出。output_state_fw是前向RNN的最终状态,output_state_bw是反向RNN的最终状态。
2.然后我们看一下动态函数tf.nn.bidirectional_dynamic_rnn建立(在此说明,本身两个lstm是反向的,通过reverse逆向保证了可以用相同的数字去索引 比如正向的第一个和逆向的最后一个都可以通过0去索引到,这里要注意的是,因为这里得到的返回值是【outputs_fw,outputs_bw】的格式,所以我们需要通过tf.concat(outputs,2)在最后一维进行连接才能保证最后输出是outputs【-1】):
def bidirectional_dynamic_rnn(
cell_fw, # 前向RNN
cell_bw, # 后向RNN
inputs, # 输入
sequence_length=None,# 输入序列的实际长度(可选,默认为输入序列的最大长度)
initial_state_fw=None, # 前向的初始化状态(可选)
initial_state_bw=None, # 后向的初始化状态(可选)
dtype=None, # 初始化和输出的数据类型(可选)
parallel_iterations=None,
swap_memory=False,
time_major=False,
# 决定了输入输出tensor的格式:如果为true, 向量的形状必须为 `[max_time, batch_size, depth]`.
# 如果为false, tensor的形状必须为`[batch_size, max_time, depth]`.
scope=None
)
返回值:
元组:(outputs, output_states)
1.outputs为(output_fw, output_bw),是一个包含前向cell输出tensor和后向cell输出tensor组成的元组。假设 time_major=false,tensor的shape为[batch_size, max_time, depth]。实验中使用tf.concat(outputs, 2)将其拼接。
2.output_states为(output_state_fw, output_state_bw),包含了前向和后向最后的隐藏状态的组成的元组。
output_state_fw和output_state_bw的类型为LSTMStateTuple。
LSTMStateTuple由(c,h)组成,分别代表memory cell和hidden state。
#################################
而cell_fw和cell_bw的定义是完全一样的。如果这两个cell选LSTM cell整个结构就是双向LSTM了。
# lstm模型 正方向传播的RNN
lstm_fw_cell = tf.nn.rnn_cell.BasicLSTMCell(embedding_size, forget_bias=1.0)
# 反方向传播的RNN
lstm_bw_cell = tf.nn.rnn_cell.BasicLSTMCell(embedding_size, forget_bias=1.0)
12345
##################################
其实在bidirectional_dynamic_rnn函数的内部,会把反向传播的cell使用array_ops.reverse_sequence的函数将输入的序列逆序排列,使其可以达到反向传播的效果。
在实现的时候,我们是需要传入两个cell作为参数就可以了: