[TensorFlow深度學習入門]實戰十一·用雙向BiRNN(LSTM)做手寫數字識別準確率99%+
[TensorFlow深度學習入門]實戰十一·用雙向BiRNN(LSTM)做手寫數字識別準確率99%+
此博文是我們在完成實戰五·用RNN(LSTM)做手寫數字識別的基礎上使用BiRNN(LSTM)結構,進一步提升模型的準確率,1000steps準確率達到99%。
- 首先我們先熟悉BiRNN
tf.nn.static_bidirectional_rnn 函式原型
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:用於前向傳播的RNNCell.
cell_bw: 用於反向傳播的RNNCell.
inputs: A length T list of inputs, each a tensor of shape [batch_size, input_size], or a nested tuple of such elements.(輸入 資料為list型別,list中元素為Tensor,每個Tensor的shape為[batch_size, input_size],如有batch_size個文件,每個文件的單詞數量為1000,每個單詞的詞向量維度為100,則該inputs為list(1000tensor(batch_size
initial_state_fw: (optional)前向RNN的初始狀態。 This must be a tensor of appropriate type and shape [batch_size, cell_fw.state_size]. If cell_fw.state_size is a tuple, this should be a tuple of tensors having shapes [batch_size, s] for s in cell_fw.state_size.。
initial_state_bw: (optional) Same as for initial_state_fw, but using the corresponding properties of cell_bw.
dtype: (optional) The data type for the initial state. Required if either of the initial states are not provided.
sequence_length: (optional) An int32/int64 vector, size [batch_size], containing the actual lengths for each of the sequences.
scope: VariableScope for the created subgraph; defaults to “bidirectional_rnn”
輸出值介紹:
A tuple (outputs, output_state_fw, output_state_bw) where: outputs is a length T list of outputs (one for each input), which are depth-concatenated forward and backward outputs. output_state_fw is the final state of the forward rnn. output_state_bw is the final state of the backward rnn.
- 程式碼部分
主體結構仿照實戰五·用RNN(LSTM)做手寫數字識別,修改的地方加了備註資訊,推薦對比實戰五來看。
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import time
import tensorflow as tf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cv2
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets("./2RNN/data",one_hot=True)
train_rate=0.002
train_step=1001
batch_size=1000
display_step=10
frame_size=28
sequence_length=28
hidden_num=128
n_classes=10
"""其中:
train_rate是學習速率,這是一個超引數,目前由經驗設定的,當然也可以自適應。
batch_size:每批樣本數,rnn也可以使用隨機梯度下降進行訓練,一批批的灌資料進去,而不是每一次把整個資料集都灌進去。
sequence_size:每個樣本序列的長度。因為我們希望把一個28x28的圖片當做一個序列輸入到rnn進行訓練,所以我們需要對圖片進行序列化。一種最方便的方法就是我們認為行與行之間存在某些關係,於是把圖片的每一行取出來當做序列的一個維度。所以這裡sequence_size就是設定為28。
反映到圖1裡面就是左邊迴圈圖展開後右圖從左往右xi的數目。 rnn cell number
frame_size:序列裡面每一個分量的大小。因為每個分量都是一行畫素,而一行畫素有28個畫素點。所以frame_size為28。
反映到圖1裡面就是最下變的輸入中每個xi都是一個長度為frame_size的向量或矩陣。input cell number
hidden_num:隱層個數,經驗設定為5
反映到圖1裡面就是從下往上數的有hidden_num個隱層單元。
n_classes:類別數,10個數字就是設定為10咯"""
x=tf.placeholder(dtype=tf.float32,shape=[None,sequence_length*frame_size],name="inputx")
y=tf.placeholder(dtype=tf.float32,shape=[None,n_classes],name="expected_y")
weights=tf.Variable(tf.random_normal(shape=[2*hidden_num,n_classes]))#因為是雙向,輸出形狀為(-1,2*hidden_num)
bias=tf.Variable(tf.fill([n_classes],0.1))
"""注意:weights是整個網路的最後一層,它的形狀為hidden_numXn_class,至於為什麼是這個形狀,我們下面來說。
bias最後一層的偏置"""
#定義RNN網路
def RNN(x,weights,bias):
x = tf.reshape(x,shape=[-1,sequence_length,frame_size])
#把輸入轉換為static_bidirectional_rnn接受的形狀:輸入 資料為list型別,list中元素為Tensor,每個Tensor的shape為[batch_size, input_size]
#先把資料x 的第一維度與第二維度互換
x = tf.transpose(x,[1,0,2])
#變形為(-1,frame_size)形狀
x = tf.reshape(x,shape=[-1,frame_size])
#拆分為list,list中元素為Tensor,每個Tensor的shape為[batch_size, input_size]
x = tf.split(x,sequence_length)
lstm_fw_cell = tf.nn.rnn_cell.BasicLSTMCell(hidden_num) # 正向RNN,輸出神經元數量為128
lstm_bw_cell = tf.nn.rnn_cell.BasicLSTMCell(hidden_num) # 反向RNN,輸出神經元數量為128
output, fw_state, bw_state = tf.nn.static_bidirectional_rnn(lstm_fw_cell, lstm_bw_cell, x, dtype=tf.float32)
print(len(output))
#生成hidden_num個隱層的RNN網路
#這是一個深度RNN網路,對於每一個長度為sequence_length的序列[x1,x2,x3,...,]的每一個xi,都會在深度方向跑一遍RNN,每一個都會被這hidden_num個隱層單元處理。
h = tf.matmul(output[int(sequence_length/2)],weights)+bias#output長度為sequence_length,我們取中間位置的輸出,雙向的結果都可以兼顧到
#此時output就是一個[batch_size,sequence_length,rnn_cell.output_size]形狀的tensor
return (h)
#我們取出最後每一個序列的最後一個分量的輸出output[:,-1,:],它的形狀為[batch_size,rnn_cell.output_size]也就是:[batch_size,hidden_num]所以它可以和weights相乘。這就是2.5中weights的形狀初始化為[hidden_num,n_classes]的原因。然後再經softmax歸一化。
predy=RNN(x,weights,bias)
cost=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=predy,labels=y))
opt=tf.train.AdamOptimizer(train_rate).minimize(cost)
correct_pred=tf.equal(tf.argmax(predy,1),tf.argmax(y,1))
accuracy=tf.reduce_mean(tf.to_float(correct_pred))
testx,testy=mnist.test.next_batch(batch_size)
saver=tf.train.Saver()
with tf.Session() as sess:
srun = sess.run
init = tf.global_variables_initializer()
srun(init)
for t in range(train_step):
batch_x,batch_y=mnist.train.next_batch(batch_size)
_cost_val,_ = srun([cost,opt],{x:batch_x,y:batch_y})
if(t%display_step==0):
accuracy_val, cost_val = srun([accuracy,cost],{x:testx,y:testy})
print(t,cost_val,accuracy_val)
saver.save(sess,'./2RNN/ckpt1/mnist1.ckpt',global_step=train_step)
- 執行結果
0 2.531533 0.168
10 0.8894601 0.699
20 0.6328424 0.796
30 0.46291852 0.856
...
970 0.022114469 0.992
980 0.03192995 0.99
990 0.021659942 0.988
1000 0.023274422 0.992
- 結果分析
通過此次實戰,我們把RNN結構改進成BiRNN結構,成功將準確率進一步提升。
表明,在合適的情況下,使用BiRNN較於普通的RNN會有一定效果的提升。