1. 程式人生 > >[TensorFlow深度學習入門]實戰十一·用雙向BiRNN(LSTM)做手寫數字識別準確率99%+

[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

100)))

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會有一定效果的提升。