RNN循環神經網絡的自我理解:基於Tensorflow的簡單句子使用(通俗理解RNN)

解讀tensorflow之rnn:

該開始接觸RNN我們都會看到這樣的張圖:


 rnn_arch

 如上圖可以看到每t-1時的forward的結果和t時的輸入共同作爲這一次forward的輸入

所以RNN存在一定的弊端,就是如果輸入足夠的長,因爲每一次forward都會帶有之前數據的信息,就會使效果變差:

張三走了!天氣也不錯,我要去打籃球。”這句話的重點肯定在於天氣不錯所以我去打球,而不是因爲張三走了,所以如果句子太長就會造成RNN在之後的訓練中含有太多前面的冗餘信息,使得效果變差。

爲解決這類問題便有了LSTM,這裏只做簡單的RNN實現,便先不介紹。


RNN爲什麼有倆種形態? 先說結論:

  1. 左側是RNN的原始結構, 右側是RNN在時間上展開的圖
  2. 我認爲RNN本質上和全連接網絡相同(全連接網絡對一個樣本做一次forward,RNN對一個樣本做多次forward)

爲什麼可以根據時間維度展開,這主要是因爲RNN的的輸入是具有時間序列的。這一點是和全連接網絡最大的不同,輸入決定了RNN的結構 
假設RNN的輸入是一句話,這句話中有多個單詞,那麼RNN需要forward多次,如下圖 

 

  • 橙色部分是上一個時刻的隱層的值,可以直觀的理解爲“記憶”
  • 當前時刻的輸出與當前時刻的輸入還有記憶有關。
  • RNN對一個樣本需要做多次forward,這一點與全連接網絡不一樣,全連接網絡對一個樣本只做一次forward。

 


 下面進行一個簡單句子的RNN實現:(輸入前一個字符預測它的後一個字符)

基於tensorflow的RNN實現中我們的難點只在與它參數的設置規則,深層次的處理不做研究。

1.首先做一個最基本的文本處理:

獲取原字符串中含有字符的列表,循環遍歷出列表內的值以及它的索引,構建新的字典(列表的值作爲鍵,對應值的索引作爲值),在字典裏取出原句中包含所有字符的索引構成新的列表(由數字組成)。

看一張圖: 這是一句 “hihello” 它進行rnn訓練時某一時刻的流程圖

 總體意思就是通過輸入h預測i,i預測h,h預測e,以此類推,最後再次從頭來過,通過這種不斷循環更新這個字母后下面是哪一個字母出現的概率,來獲得模型。

所以對於一句話來說:輸入要去掉最後一個。輸出要去掉第一個

sample = " 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) "
idx2char = list(set(sample)) # 去重放列表裏

char2idx = {c: i for i, c in enumerate(idx2char)} # 轉爲字典  把字母作爲鍵 它的索引作爲值
sample_idx = [char2idx[c] for c in sample] # char to index 在字典裏取出對應值
x_data = [sample_idx[:-1]] # 輸入 去掉最後一個
y_data = [sample_idx[1:]]   #輸出 去掉第一個
print(x_data)
print(y_data)

2.設置構建RNN所需要的參數

rnn_hidden_size是可以自己設定的,在下一篇

實驗:構建cell時num_units的個數怎麼影響輸出

中展示。

dic_size = len(char2idx) #字典長度
rnn_hidden_size = len(char2idx)  # 單元個數 cell中神經元的個數
num_classes = len(char2idx) 
batch_size = 1 #    一個樣本數據,一個批次
sequence_length = len(sample) - 1 #   序列的個數

3.定義佔位符並且進行獨熱編碼的轉化

X = tf.placeholder(tf.int32, [None, sequence_length]) # X data
Y = tf.placeholder(tf.int32, [None, sequence_length]) # Y label
X_one_hot = tf.one_hot(X, num_classes) # one hot: 1 -> 0 1 0 0 0 0 0 0 0 0

4.構建RNN

state_is_tuple=True 必須寫 它規定了狀態信息的格式

initial_state = cell.zero_state(batch_size, tf.float32)爲RNN的初始化狀態,一般都這樣定義

cell = tf.contrib.rnn.BasicLSTMCell(num_units=rnn_hidden_size, state_is_tuple=True)
initial_state = cell.zero_state(batch_size, tf.float32)

outputs, _states = tf.nn.dynamic_rnn(cell,  X_one_hot , initial_state=initial_state, dtype=tf.float32)

 5.加一層全連接,相當於加一層深度,使預測更準確

outputs = contrib.layers.fully_connected(inputs=outputs, num_outputs=num_classes, activation_fn=None)

6.定義損失、訓練過程和結果的獲取

weight爲t時與t+1時之間的權重

最後的outputs是三維的 所以axis=2

weights = tf.ones([batch_size, sequence_length])

sequence_loss = tf.contrib.seq2seq.sequence_loss(logits=outputs, targets=Y,weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)

prediction = tf.argmax(outputs, axis=2)

 7.進行訓練的輸入和打印結果

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    # print(sess.run(X_one_hot,feed_dict={X: x_data}))
    for i in range(3000):
        l, _ = sess.run([loss, train], feed_dict={X: x_data, Y: y_data})
        result = sess.run(prediction, feed_dict={X: x_data})
        # print char using dic
        result_str = [idx2char[c] for c in np.squeeze(result)]
        print(i, "loss:", l, "Prediction:", ''.join(result_str))
    print(len(result_str))

整體代碼爲:

import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
from tensorflow.contrib import rnn
from tensorflow import contrib
import numpy as np

sample = " 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) "
idx2char = list(set(sample)) # 去重放列表裏

char2idx = {c: i for i, c in enumerate(idx2char)} # 轉爲字典  把字母作爲鍵 它的索引作爲值
sample_idx = [char2idx[c] for c in sample] #  在字典裏取出對應值
x_data = [sample_idx[:-1]] # 輸入 去掉最後一個
y_data = [sample_idx[1:]]   #輸出 去掉第一個
print(x_data)
print(y_data)
# 一些參數
dic_size = len(char2idx) 
rnn_hidden_size = len(char2idx)  
num_classes = len(char2idx) 
batch_size = 1 
sequence_length = len(sample) - 1

X = tf.placeholder(tf.int32, [None, sequence_length]) # X data
Y = tf.placeholder(tf.int32, [None, sequence_length]) # Y label
X_one_hot = tf.one_hot(X, num_classes) 

cell = tf.contrib.rnn.BasicLSTMCell(num_units=rnn_hidden_size, state_is_tuple=True)
initial_state = cell.zero_state(batch_size, tf.float32)

outputs, _states = tf.nn.dynamic_rnn(cell,  X_one_hot , initial_state=initial_state, dtype=tf.float32)

outputs = contrib.layers.fully_connected(inputs=outputs, num_outputs=num_classes, activation_fn=None)

weights = tf.ones([batch_size, sequence_length])

sequence_loss = tf.contrib.seq2seq.sequence_loss(logits=outputs, targets=Y,weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)

prediction = tf.argmax(outputs, axis=2)


with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    # print(sess.run(X_one_hot,feed_dict={X: x_data}))
    for i in range(3000):
        l, _ = sess.run([loss, train], feed_dict={X: x_data, Y: y_data})
        result = sess.run(prediction, feed_dict={X: x_data})
        # print char using dic
        result_str = [idx2char[c] for c in np.squeeze(result)]
        print(i, "loss:", l, "Prediction:", ''.join(result_str))
    print(len(result_str))

輸出結果爲:

可以看到循環3000次訓練的結果與原句子完全一致!!!

2991 loss: 0.101450786 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2992 loss: 0.10137266 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2993 loss: 0.1012946 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2994 loss: 0.10121668 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2995 loss: 0.10113874 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2996 loss: 0.101061 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2997 loss: 0.10098329 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2998 loss: 0.10090562 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 
2999 loss: 0.10082806 Prediction: 這是一個基於tensorflow的RNN短句子練習 (CSDN_qihao) 

 遺留問題:

構建cell時num_units的個數怎麼影響輸出???

解決方式點這裏!!RNN:構建cell時num_units的個數怎麼影響輸出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章