1. 循環(huán)神經(jīng)網(wǎng)絡(luò)RNN
1) 什么是RNN?
循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)是一種節(jié)點(diǎn)定向連接成環(huán)的人工神經(jīng)網(wǎng)絡(luò)底瓣。具體應(yīng)用有語(yǔ)音識(shí)別,手寫(xiě)識(shí)別,翻譯等.
2) 什么時(shí)候使用RNN丙笋?
FNN(前饋神經(jīng)網(wǎng)絡(luò)谢澈,如BP,CNN等)效果已經(jīng)不錯(cuò)了御板,RNN還需要更大量的計(jì)算锥忿,為什么要用RNN呢?如果訓(xùn)練N次怠肋,每次和每次都沒(méi)什么關(guān)系敬鬓,那就不需要RNN,但如果每個(gè)后一次都可能和前一次訓(xùn)練相關(guān)笙各,比如說(shuō)翻譯:一個(gè)句子里面N個(gè)詞钉答,一個(gè)詞為一次訓(xùn)練(train instance),一個(gè)詞的意思很可能依賴它的上下文杈抢,也就是其前次或后次訓(xùn)練数尿,這個(gè)時(shí)候就需要RNN.
3) RNN與FNN有何不同?
如圖所示惶楼,左邊的是前饋神經(jīng)網(wǎng)絡(luò)右蹦,數(shù)據(jù)按黑箭頭方向從輸入層經(jīng)過(guò)隱藏層流入輸出層,向前流動(dòng)歼捐,因此叫做前饋網(wǎng)絡(luò).右圖中何陆,隱藏層中的數(shù)據(jù)除了傳向輸出層,還和下次輸入一起訓(xùn)練后續(xù)的隱藏層豹储,不再是單向甲献,而是包含了循環(huán),則構(gòu)成了循環(huán)神經(jīng)網(wǎng)絡(luò).下圖是將各個(gè)時(shí)間點(diǎn)畫(huà)在同一圖上颂翼,左邊前饋FNN的展開(kāi)圖晃洒,右邊是RNN的展開(kāi)圖.
簡(jiǎn)單地說(shuō),它只是在隱藏層處加了一個(gè)"循環(huán)"朦乏,但實(shí)際上問(wèn)題沒(méi)這么簡(jiǎn)單.之前說(shuō)過(guò)(詳見(jiàn):深度學(xué)習(xí)_BP神經(jīng)網(wǎng)絡(luò))球及,輸入層向隱藏層傳數(shù)據(jù)時(shí),根據(jù)權(quán)重U計(jì)算(為簡(jiǎn)化說(shuō)明省略偏置)呻疹,隱藏層向輸出層傳數(shù)據(jù)時(shí)吃引,根據(jù)權(quán)重V計(jì)算(之前文檔用字體w1,w2表示),循環(huán)神經(jīng)網(wǎng)絡(luò)又加了參數(shù)W刽锤,用于控制隱藏層的權(quán)重.看起來(lái)好像只是多了一次矩陣乘法和加法镊尺,但實(shí)際上RNN計(jì)算要比前饋網(wǎng)絡(luò)復(fù)雜很多.原因我們看紅色箭頭,它標(biāo)記的是誤差反向傳播并思,也就是根據(jù)實(shí)際結(jié)果y和輸出層的預(yù)測(cè)結(jié)果o計(jì)算出的誤差傳回網(wǎng)絡(luò)以調(diào)整權(quán)重UVW.由于每個(gè)隱藏層都依賴前一隱藏層的結(jié)果庐氮,因此誤差不只要從隱藏層傳回輸入層,還要一層一層傳回上一隱藏層.它使用計(jì)算變得很復(fù)雜宋彼,且無(wú)法并行.
于是又有了下圖中的變種弄砍,使用實(shí)際輸出y仙畦,和下個(gè)x一次訓(xùn)練下一隱藏層,因?yàn)閥中信息并不像h中那么豐富音婶,因此可能效果會(huì)差一些.這里只是循環(huán)神經(jīng)網(wǎng)絡(luò)的幾種情況慨畸,其它就不一一列舉了,總的來(lái)說(shuō)循環(huán)神經(jīng)網(wǎng)絡(luò)并無(wú)定式衣式,主要指數(shù)據(jù)的流向中包含循環(huán).
2. LSTM
經(jīng)常聽(tīng)到LSTM神經(jīng)網(wǎng)絡(luò)如何如何寸士,其實(shí)LSTM不是一種網(wǎng)絡(luò),而是一種對(duì)RNN隱藏層的改進(jìn)算法(改進(jìn)算法有很多碴卧,這個(gè)因?yàn)樾Ч萌蹩ǎ员容^著名)
LSTM(Long short-term memory)是長(zhǎng)短期記憶的簡(jiǎn)寫(xiě).
如果不斷用隱藏層去計(jì)算下一時(shí)間隱藏層,當(dāng)計(jì)算隱藏層的特征向量大于1時(shí)螟深,經(jīng)過(guò)N次迭代后值就會(huì)越來(lái)越大谐宙,最終發(fā)生爆炸烫葬,如果小于1界弧,最后越來(lái)越小,導(dǎo)致消失.換句話說(shuō)搭综,過(guò)去會(huì)給我們啟發(fā)垢箕,所以不能忘記過(guò)去,但如果每時(shí)每刻都被過(guò)去影響兑巾,就像滾雪球一樣条获,最后也會(huì)悲劇.最好是把重點(diǎn)記住蒋歌,然后在開(kāi)始新篇章的時(shí)候更多地忘記過(guò)去.
更直觀的說(shuō)帅掘,原來(lái)在輸入層和隱藏層間是仿射變換加激活函數(shù),現(xiàn)在用輸入門堂油,遺忘門輸出門和狀態(tài)層來(lái)代替隱藏單元的生成算法.其中每個(gè)門都有非線性變換.這幾個(gè)門的關(guān)系修档,詳見(jiàn)代碼:
input_gate = tf.sigmoid(tf.matmul(i, ix) + tf.matmul(o, im) + ib) #輸入門
forget_gate = tf.sigmoid(tf.matmul(i, fx) + tf.matmul(o, fm) + fb) #遺忘門
update = tf.tanh(tf.matmul(i, cx) + tf.matmul(o, cm) + cb) #更新
state = forget_gate * state + input_gate * update # 更新?tīng)顟B(tài), 遺忘門控制是否忘記舊時(shí)狀態(tài)
output_gate = tf.sigmoid(tf.matmul(i, ox) + tf.matmul(o, om) + ob) #輸出門
return output_gate * tf.tanh(state), state #返回輸出值
3. 程序
1) 說(shuō)明
與前篇的BP網(wǎng)絡(luò)和CNN網(wǎng)絡(luò)一樣,這次使用的仍然是MNIST手寫(xiě)數(shù)據(jù)識(shí)別.在練習(xí)了純Python和Keras框架之后府框, 此次使用更低層的TensorFlow代碼實(shí)現(xiàn)RNN.也順便了解一個(gè)高級(jí)工具都封裝了什么吱窝?
每個(gè)圖片仍然是28x28像素,前饋網(wǎng)絡(luò)把28x28共748個(gè)像素值作為一個(gè)輸入x數(shù)據(jù)傳入輸入層迫靖,而RNN把每張圖當(dāng)成一個(gè)序列院峡,序列有28個(gè)元素(一行為一個(gè)元素),以每行的28個(gè)點(diǎn)為輸入x傳入輸入層.簡(jiǎn)單地說(shuō)就是切成一行一行訓(xùn)練系宜,每行與下一行有一定聯(lián)系.
下圖是時(shí)序圖(為簡(jiǎn)化邏輯照激,此處只畫(huà)了一個(gè)隱藏層),相對(duì)最一般的RNN盹牧,下圖是一個(gè)變種:整個(gè)序列(28行)的輸入對(duì)應(yīng)同一個(gè)輸出y(手寫(xiě)對(duì)應(yīng)的數(shù)字).
2) 代碼
# -*- coding: utf-8 -*-
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import numpy as np
# 在MINIST_data目錄下載 mnist數(shù)據(jù)
mnist = input_data.read_data_sets("MINIST_data/", one_hot=True)
# RNN學(xué)習(xí)時(shí)使用的參數(shù)
learning_rate = 0.001 # 學(xué)習(xí)率
training_iters = 100000 # 訓(xùn)練實(shí)例數(shù)
batch_size = 120 # 批大小
display_step = 10 # 訓(xùn)練10批顯示一次
n_input = 28 # 每28個(gè)作為一個(gè)輸入層的節(jié)點(diǎn)數(shù)(行中點(diǎn))
n_steps = 28 # 28個(gè)連續(xù)序列(列)
n_hidden = 128 # 隱含層的節(jié)點(diǎn)數(shù)
n_classes = 10 # 輸出的節(jié)點(diǎn)數(shù)实抡,0~9個(gè)數(shù)字欠母,這里一共有10個(gè)
x = tf.placeholder("float", [None, n_steps, n_input]) # 構(gòu)建輸入節(jié)點(diǎn)
istate = tf.placeholder("float", [None, 2 * n_hidden]) # 構(gòu)建隱藏節(jié)點(diǎn),一個(gè)存節(jié)點(diǎn)吆寨,一個(gè)存狀態(tài)
y = tf.placeholder("float", [None, n_classes]) # 構(gòu)建輸出節(jié)點(diǎn)
# 隨機(jī)初始化各層的權(quán)值和偏置
weights = {
'hidden': tf.Variable(tf.random_normal([n_input, n_hidden])), # 輸入到隱藏
'out': tf.Variable(tf.random_normal([n_hidden, n_classes])) # 隱藏到輸出
}
biases = {
'hidden': tf.Variable(tf.random_normal([n_hidden])),
'out': tf.Variable(tf.random_normal([n_classes]))
}
# 建立RNN模型赏淌,_X是批訓(xùn)練數(shù)據(jù),_istate為隱藏節(jié)點(diǎn)
def RNN(_X, _istate, _weights, _biases):
_X = tf.transpose(_X, [1, 0, 2]) # 把batch_size,n_steps,n_input順序變?yōu)閚_steps,batch_size,n_input
_X = tf.reshape(_X, [-1, n_input]) # 再轉(zhuǎn)換為n_steps*batch_size, n_input
# 兩個(gè)隱藏層:第一層直接計(jì)算啄清,第二層用LSTM
_X = tf.matmul(_X, _weights['hidden']) + _biases['hidden'] # 計(jì)算隱藏層的節(jié)點(diǎn),此時(shí)X從輸入節(jié)點(diǎn)轉(zhuǎn)為隱藏節(jié)點(diǎn)
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0,state_is_tuple=False) # 定義lstm
_X = tf.split(_X, n_steps, 0) # 序列切片六水,每片是一個(gè)(batch_size, n_hidden)
outputs, states = tf.nn.static_rnn(lstm_cell, _X, initial_state=_istate) # 計(jì)算lstm rnn,states存狀態(tài)
return tf.matmul(outputs[-1], _weights['out']) + _biases['out'] #計(jì)算輸出層
pred = RNN(x, istate, weights, biases)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y)) # 損失函數(shù)為交叉熵
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # 優(yōu)化方法為Adam
correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) # 計(jì)算錯(cuò)誤數(shù)
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 計(jì)算錯(cuò)誤率
init = tf.global_variables_initializer()
sess = tf.InteractiveSession()
sess.run(init)
step = 1
while step * batch_size < training_iters:
batch_xs, batch_ys = mnist.train.next_batch(batch_size) # 隨機(jī)抽取訓(xùn)練數(shù)據(jù)
batch_xs = batch_xs.reshape((batch_size, n_steps, n_input))
# 用feed_dict傳入數(shù)據(jù):輸入,輸出辣卒,隱藏層, 數(shù)據(jù)由placeholder定義, 運(yùn)行optimizer
sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys, istate: np.zeros((batch_size, 2 * n_hidden))})
if step % display_step == 0: # 每display_step次批處理顯示一次, 通過(guò)run運(yùn)行accuracy,cost
acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, istate: np.zeros((batch_size, 2 * n_hidden))})
loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, istate: np.zeros((batch_size, 2 * n_hidden))})
print("Iter " + str(step * batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc))
step += 1
print("Optimization Finished!")
test_len = 256
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, istate: np.zeros((test_len, 2 * n_hidden))}))
3) 分析
TensorFlow涉及了更多具體計(jì)算掷贾,比如格式轉(zhuǎn)換,矩陣乘法等等荣茫,不像Keras從外面基本看不到具體的步驟的動(dòng)作和結(jié)果.
從代碼中很容易明白為什么說(shuō)TensorFlow是一個(gè)"框架"想帅,程序的前50行,具體數(shù)據(jù)還沒(méi)出現(xiàn)啡莉,程序就指定了數(shù)據(jù)的結(jié)構(gòu)和流向港准,在接下來(lái)的sess部分,tf 才真正開(kāi)始運(yùn)算咧欣,把數(shù)據(jù)切塊"喂"給框架浅缸,使其運(yùn)行.它和一般程序調(diào)函數(shù),確實(shí)不太一樣.
這里比較不容易理解的是數(shù)據(jù)怎么從feed_dict轉(zhuǎn)入了RNN函數(shù).在pred = RNN(x, istate, weights, biases)被運(yùn)行時(shí)魄咕,其實(shí)里面并沒(méi)有真正的數(shù)據(jù)在做轉(zhuǎn)換和乘法衩椒,這里定義的是數(shù)據(jù)的流程.此時(shí)的x,istate里面還沒(méi)有數(shù)據(jù)哮兰,而只是定義了數(shù)據(jù)形式毛萌,并告訴TensorFlow該數(shù)據(jù)需要如何處理.在后面feed_dict處理時(shí)才傳入了真正的數(shù)據(jù),并通過(guò)run()間接地調(diào)用了RNN(). sess.run()調(diào)用函數(shù)時(shí)喝滞,函數(shù)的各個(gè)參數(shù)都是從當(dāng)前環(huán)境里取的.我理解這里的placeholder意思有點(diǎn)像C中定義的數(shù)據(jù)結(jié)構(gòu).
4. 問(wèn)題與解答
1) RNN的隱藏層是一個(gè)還是多個(gè)阁将?
隱藏層可以是一個(gè),也可以是多個(gè)(多層循環(huán)網(wǎng)絡(luò))囤躁,比如說(shuō)可以有三個(gè)隱藏層h1,h2,h3冀痕,其中h2將結(jié)果轉(zhuǎn)給下一個(gè)實(shí)例的訓(xùn)練,還可以是雙向的(一個(gè)傳向前一時(shí)間點(diǎn)狸演,一個(gè)傳向后一時(shí)間點(diǎn)言蛇,即雙向循環(huán)網(wǎng)絡(luò)),一般為了簡(jiǎn)化宵距,例子里都有單層的.
2) RNN中反向傳播梯度是怎么進(jìn)行的腊尚?
對(duì)于訓(xùn)練序列來(lái)說(shuō),如果序列中每個(gè)時(shí)間點(diǎn)的輸入x都有對(duì)應(yīng)的輸出y满哪,總損失就是所有時(shí)間步的損失之和.如果像MNIST中整個(gè)序列對(duì)應(yīng)一個(gè)結(jié)果婿斥,則使用該結(jié)果與預(yù)測(cè)的誤差.具體方法還是梯度下降劝篷,只是計(jì)算隱藏層與隱藏層之間權(quán)重的方法需要按時(shí)間往前推.
5. 參考
1) TensorFlow學(xué)習(xí)筆記(8):基于MNIST數(shù)據(jù)的循環(huán)神經(jīng)網(wǎng)絡(luò)RNN
https://segmentfault.com/a/1190000008346992
2) 解讀tensorflow之rnn
3) TensorFlow遇到的問(wèn)題匯總(持續(xù)更新中......)
http://www.cnblogs.com/hunttown/p/6866586.html
4) 利用 Keras 下的 LSTM 進(jìn)行情感分析
http://blog.csdn.net/william_2015/article/details/72978387
5) 基于Theano的深度學(xué)習(xí)(Deep Learning)框架Keras學(xué)習(xí)隨筆-02-Example
http://blog.csdn.net/niuwei22007/article/details/49053771
6) 循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN, Recurrent Neural Networks)介紹
http://blog.csdn.net/heyongluoyao8/article/details/48636251