tensorflow之双向循环神经网络

时间:2021-09-18 10:45:00

一、定义

尽管从多层感知器(MLP)到循环神经网络(RNN)的扩展看起来微不足道,但是这对于序列的学习具有深远意义。循环神经网络(RNN)的使用是用来处理序列数据的。在传统的神经网络模型中,层与层之间是全连接的,每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题是无能为力的。比如,预测句子的下一个单词是什么,一般需要用到前面的单词,因为一个句子中前后单词并不是独立的。循环神经网络(RNN)指的是一个序列当前的输出与之前的输出也有关。具体的表现形式为网络会对前面的信息进行记忆,保存在网络的内部状态中,并应用于当前输出的计算中,即隐含层之间的节点不再无连接而是有链接的,并且隐含层的输入不仅包含输入层的输出还包含上一时刻隐含层的输出。理论上,循环神经网络能够对任何长度的序列数据进行处理,但是在实践中,为了减低复杂性往往假设当前的状态只与前面的几个状态相关。

在循环神经网络中(RNN),有一条单向流动的信息流是从输入单元到达隐含单元的,与此同时,另一条单向流动的信息流是从隐含单元到达输出单元。在某些情况下,循环神经网络(RNN)会打破后者的限制,引导信息从输出单元返回隐含单元,这些被称为“Back projections”,并且隐藏层的输入还包括上一层隐藏层的输出,即隐藏层内的节点是可以自连也可以互连的。

tensorflow之双向循环神经网络

对于上图网络的计算过程如下:

1)xt 表示第t(t=1, 2, 3, … )步的输入

2)st 为隐含层第t步的状态,它是神经的记忆单元。st 根据当前输入层和上一步隐含层的输出进行计算
st=f(Uxt+Wst−1) ,其中f一般为非线性的激活函数,如relu或tanh,在计算s0 时,即第一个的隐含状态,需要用到 st−1,但其并不存在,在现实中一般被设置为0向量。

3) ot 是第t步的输出,stot=softmax(Vst)

需要注意的是:

隐含层状态st 被认为是网络的记忆单元。st 包含了前面所有步的隐含层状态,而输出层的ot只与当前步的st有关。在实践中,为了降低网络的复杂度,往往st只包含前面若干步而不是所有步的隐含层输出。
循环神经网络(RNN)的关键之处在于隐含层,隐含层能够捕捉序列的信息。

Bidirectional Recurrent Neural Network(BRNN)
如果能像访问过去的上下文信息一样,访问未来的上下文,这样对于许多序列标注任务是非常有益的。例如,在最特殊分类的时候,如果能像知道这个字母之前的字母一样,知道将要来的字母,这将非常有帮助。
然而,由于标准的循环神经网络(RNN)在时序上处理序列,他们往往忽略了未来的上下文信息。一种很显而易见的解决办法是在输入和目标之间添加延迟,进而可以给网络一些时步来加入未来的上下文信息,也就是加入M时间帧的未来信息来一起预测输出。理论上,M可以非常大来捕获所有未来的可用信息,但事实上发现如果M过大,预测结果将会变差。这是因为网络把精力都集中记忆大量的输入信息,而导致将不同输入向量的预测只是联合的建模能力下降。因此,M的大小需要手动来调节。
双向循环神经网络(BRNN)的基本思想是提出每一个训练序列向前和向后分别是两个循环神经网络(RNN),而且这两个都连接着一个输出层。这个结构提供给输出层输入序列中每一个点的完整的过去和未来的上下文信息。下图展示的是一个沿着时间展开的双向循环神经网络。六个独特的权值在每一个时步被重复利用,六个权值分别对应:输入到向前和向后隐含层(w1,w3),隐含层到隐含层自己(w2,w5),向前和向后隐含层到输出层(w4,w6)。值得注意的是:向前和向后隐含层之间没有信息流,这保证了展开图是非循环的。tensorflow之双向循环神经网络

import tensorflow as tf
from tensorflow.contrib import rnn
import numpy as np

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(“/tmp/data/”, one_hot=True)

”’
To classify images using a bidirectional recurrent neural network, we consider
every image row as a sequence of pixels. Because MNIST image shape is 28*28px,
we will then handle 28 sequences of 28 steps for every sample.
”’

learning_rate = 0.001
training_iters = 100000
batch_size = 128
display_step = 10

n_input = 28 # MNIST data input (img shape: 28*28)
n_steps = 28 # timesteps
n_hidden = 128 # hidden layer num of features
n_classes = 10 # MNIST total classes (0-9 digits)

x = tf.placeholder(“float”, [None, n_steps, n_input])
y = tf.placeholder(“float”, [None, n_classes])

weights = {
# Hidden layer weights => 2*n_hidden because of forward + backward cells
‘out’: tf.Variable(tf.random_normal([2*n_hidden, n_classes]))
}
biases = {
‘out’: tf.Variable(tf.random_normal([n_classes]))
}

def BiRNN(x, weights, biases):

# Prepare data shape to match `bidirectional_rnn` function requirements
# Current data input shape: (batch_size, n_steps, n_input)
# Required shape: 'n_steps' tensors list of shape (batch_size, n_input)

# Unstack to get a list of 'n_steps' tensors of shape (batch_size, n_input)
x = tf.unstack(x, n_steps, 1)

# Define lstm cells with tensorflow
# Forward direction cell
lstm_fw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)
# Backward direction cell
lstm_bw_cell = rnn.BasicLSTMCell(n_hidden, forget_bias=1.0)

# Get lstm cell output
try:
outputs, _, _ = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x,
dtype=tf.float32)
except Exception: # Old TensorFlow version only returns outputs not states
outputs = rnn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x,
dtype=tf.float32)

# Linear activation, using rnn inner loop last output
return tf.matmul(outputs[-1], weights['out']) + biases['out']

pred = BiRNN(x, weights, biases)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

init = tf.global_variables_initializer()

with tf.Session() as sess:
sess.run(init)
step = 1
# Keep training until reach max iterations
while step * batch_size < training_iters:
batch_x, batch_y = mnist.train.next_batch(batch_size)
# Reshape data to get 28 seq of 28 elements
batch_x = batch_x.reshape((batch_size, n_steps, n_input))
# Run optimization op (backprop)
sess.run(optimizer, feed_dict={x: batch_x, y: batch_y})
if step % display_step == 0:
# Calculate batch accuracy
acc = sess.run(accuracy, feed_dict={x: batch_x, y: batch_y})
# Calculate batch loss
loss = sess.run(cost, feed_dict={x: batch_x, y: batch_y})
print(“Iter ” + str(step*batch_size) + “, Minibatch Loss= ” + \
“{:.6f}”.format(loss) + “, Training Accuracy= ” + \
“{:.5f}”.format(acc))
step += 1
print(“Optimization Finished!”)

# Calculate accuracy for 128 mnist test images
test_len = 128
test_data = mnist.test.images[:test_len].reshape((-1, n_steps, n_input))
test_label = mnist.test.labels[:test_len]
print("Testing Accuracy:", \
sess.run(accuracy, feed_dict={x: test_data, y: test_label}))