白话RNN系列(七)

本文,探讨下LSTM的一些高级应用,比如双向LSTM。

前面的探讨过程中, 我们使用到的RNN或者LSTM都是单向的,即按照时间顺序排列的一维序列;而在实际应用中,双向的RNN由于考虑到更充足的上下文,往往能起到更好的效果:

Bi-RNN又叫双向RNN,是采用了两个方向的RNN网络。
RNN网络擅长的是对于连续数据的处理,既然是连续的数据规律,我们不仅可以学习它的正向规律,还可以学习它的反向规律。这样将正向和反向结合的网络,会比单向的循环网络有更高的拟合度。例如,预测一个语句中缺失的词语,则需要根据上下文来进行预测。
双向RNN的处理过程与单向的RNN非常类似,就是在正向传播的基础上再进行一次反向传播,而且这两个都连接着一个输出层。这个结构提供给输出层输入序列中,每一个点完整的过去和未来的上下文信息。下图所示为一个沿着时间展开的双向循环神经网络:

在按照时间序列正向运算完之后,网络又从时间的最后一项反向地运算一遍,假如上图中我们定义T=3,即把t3时刻的输入与默认值0一起生成反向的out3,把反向out3当成t2时刻的输入与原来的t2时刻输入一起生成反向out2;依此类推,直到第一个时序数据。

双向循环网络的输出是2个,正向一个,反向一个。最终会把输出结果通过concat并联在一起,然后交给后面的层来处理。例如,数据输入[batch,nhidden],输出就会变成[batch,nhidden×2]。

结构看起来很简单,我们还是找个具体的例子,来对双向RNN进行一个透彻的分析;这里,我们使用单层动态双向RNN网络对MNIST数据集进行分类:

n_input = 28  # MNIST data 输入 (img shape: 28*28)
n_steps = 28  # timesteps
n_hidden = 128  # hidden layer num of features
n_classes = 10  # MNIST 列别 (0-9 ,一共10类)

初始化的一些变量,与原先的LSTM并无很大区别:

区别主要在如下地方:

x1 = tf.unstack(x, n_steps, 1)
# 我们定义了前向的LSTMCell, 其隐藏层包含了128个节点
lstm_fw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
# 同时,定义了反向的LSTMCell,其隐藏层同样包含了128个节点
lstm_bw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)

在定义LSTMCell的时候,我们需要定义两遍LSTMCell,其隐藏层都拥有128个节点:

outputs, output_states = tf.nn.bidirectional_dynamic_rnn(lstm_fw_cell, lstm_bw_cell, x,
                                                         dtype=tf.float32)

训练方式稍有不同,可以看到,我们这里使用了bidirectional_dynamic_rnn的训练方式;同时输入的x是128 * 28 * 28 的数据,并未按照时间序列进行切分:

        out = sess.run(outputs_first, feed_dict={x: batch_x, y: batch_y})
        print('out-0:'+str(out[0].shape))
        print('out-1:'+str(out[1].shape))

我们在训练过程中,输出outputs两个元素的形状,发现:

out-0:(100, 28, 128)
out-1:(100, 28, 128)

前面定义的100位batch_size即每个批次数据量的大小;中间的28即循环次数;128 即隐藏层神经元的数目:

outputs = tf.concat(outputs_first, 2)
outputs = tf.transpose(outputs, [1, 0, 2])

通常,我们会把两次输出进行拼接,生成最后的输入。

余下,都和单向的LSTM完全一致了:

总结下,输出的outputs是前向和后向分开的。这种方法最原始也最灵活,但要注意,一定要把两个输出结果进行融合,我们可以采用拼接的形式,自然也可以采用其他的向量相加的方式等来实现自己的目的。

下面,我们再使用静态双向RNN把上述操作实现一遍,看看其与静态双向RNN有什么本质的区别:

lstm_fw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
# 反向cell
lstm_bw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
outputs, _, _ = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x1,
                                              dtype=tf.float32)
print(outputs[0].shape,len(outputs))
pred = tf.contrib.layers.fully_connected(outputs[-1],n_classes,activation_fn = None)

很明显两个区别:

1.输出的形式,静态需要对输入数据进行unstack操作,分割成按照时间顺序排列的数据(而动态执行不需要此种方式,更加简洁)

2.其输出outputs[-1]直接是拼接好的数据,我们在执行过程中可以看到:

(?, 256) 28

毫无疑问,这种方式没有动态执行方式灵活。

当然,我们在实现过程中,还可以采用多层RNN的方式来实现自己的目的,在此处不赘述了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值