變長(zhǎng)雙向rnn的正確使用姿勢(shì)教學(xué)
如何使用雙向RNN
在《深度學(xué)習(xí)之TensorFlow入門(mén)、原理與進(jìn)階實(shí)戰(zhàn)》一書(shū)的9.4.2中的第4小節(jié)中,介紹過(guò)變長(zhǎng)動(dòng)態(tài)RNN的實(shí)現(xiàn)。
這里在來(lái)延伸的講解一下雙向動(dòng)態(tài)rnn在處理變長(zhǎng)序列時(shí)的應(yīng)用。其實(shí)雙向RNN的使用中,有一個(gè)隱含的注意事項(xiàng),非常容易犯錯(cuò)。
本文就在介紹下雙向RNN的常用函數(shù)、用法及注意事項(xiàng)。
動(dòng)態(tài)雙向rnn有兩個(gè)函數(shù):
stack_bidirectional_dynamic_rnn bidirectional_dynamic_rnn
二者的實(shí)現(xiàn)上大同小異,放置的位置也不一樣,前者放在contrib下面,而后者顯得更加根紅苗正,放在了tf的核心庫(kù)下面。在使用時(shí)二者的返回值也有所區(qū)別。下面就來(lái)一一介紹。
示例代碼
先以GRU的cell代碼為例:
import tensorflow as tf import numpy as np tf.reset_default_graph() # 創(chuàng)建輸入數(shù)據(jù) X = np.random.randn(2, 4, 5)# 批次 、序列長(zhǎng)度、樣本維度 # 第二個(gè)樣本長(zhǎng)度為3 X[1,2:] = 0 seq_lengths = [4, 2] Gstacked_rnn = [] Gstacked_bw_rnn = [] for i in range(3): Gstacked_rnn.append(tf.contrib.rnn.GRUCell(3)) Gstacked_bw_rnn.append(tf.contrib.rnn.GRUCell(3)) #建立前向和后向的三層RNN Gmcell = tf.contrib.rnn.MultiRNNCell(Gstacked_rnn) Gmcell_bw = tf.contrib.rnn.MultiRNNCell(Gstacked_bw_rnn) sGbioutputs, sGoutput_state_fw, sGoutput_state_bw = tf.contrib.rnn.stack_bidirectional_dynamic_rnn([Gmcell],[Gmcell_bw], X,sequence_length=seq_lengths, dtype=tf.float64) Gbioutputs, Goutput_state_fw = tf.nn.bidirectional_dynamic_rnn(Gmcell,Gmcell_bw, X,sequence_length=seq_lengths,dtype=tf.float64)
上面例子中是創(chuàng)建雙向RNN的方法示例??梢钥吹綆в衧tack的雙向RNN會(huì)輸出3個(gè)返回值,而不帶有stack的雙向RNN會(huì)輸出2個(gè)返回值。
這里面還要注意的是,在沒(méi)有未cell初始化時(shí)必須要將dtype參數(shù)賦值。不然會(huì)報(bào)錯(cuò)。
代碼:BiRNN輸出
下面添加代碼,將輸出的值打印出來(lái),看一下,這兩個(gè)函數(shù)到底是輸出的是啥?
#建立一個(gè)會(huì)話 sess = tf.InteractiveSession() sess.run(tf.global_variables_initializer()) sgbresult,sgstate_fw,sgstate_bw=sess.run([sGbioutputs,sGoutput_state_fw,sGoutput_state_bw]) print("全序列:\n", sgbresult[0]) print("短序列:\n", sgbresult[1]) print('Gru的狀態(tài):',len(sgstate_fw[0]),'\n',sgstate_fw[0][0],'\n',sgstate_fw[0][1],'\n',sgstate_fw[0][2]) print('Gru的狀態(tài):',len(sgstate_bw[0]),'\n',sgstate_bw[0][0],'\n',sgstate_bw[0][1],'\n',sgstate_bw[0][2])
先看一下帶有stack的雙向RNN輸出的內(nèi)容:
我們輸入的數(shù)據(jù)的批次是2,第一個(gè)序列長(zhǎng)度是4,第二個(gè)序列長(zhǎng)度是2.
圖中共有4部分輸出,可以看到,第一部分(全序列)就是序列長(zhǎng)度為4的結(jié)果,第二部分(短序列)就是序列長(zhǎng)度為2的結(jié)果。由于沒(méi)一層都是由3個(gè)RNN的GRU cell組成,所以每個(gè)序列的輸出都為3.很顯然,對(duì)于這樣的結(jié)果輸出,必須要將短序列后面的0去掉才可以用。
好在該函數(shù)還有第二個(gè)輸出值,GRU的狀態(tài)??梢灾苯邮褂脿顟B(tài)里的值,而不需要對(duì)原始結(jié)果進(jìn)行去0的變化。
由于單個(gè)GRU本來(lái)就是沒(méi)有狀態(tài)的。所以該函數(shù)將最后的輸出作為狀態(tài)返回。該函數(shù)有兩個(gè)狀態(tài)返回,分別代表前向和后向。每一個(gè)方向的狀態(tài)都會(huì)返回3個(gè)元素。這是因?yàn)槊總€(gè)方向的網(wǎng)絡(luò)都有3層GRU組成。在使用時(shí),一般都會(huì)取最后一個(gè)狀態(tài)。圖中紅色部分為前向中,兩個(gè)樣本對(duì)應(yīng)的輸出,這個(gè)很好理解。
重點(diǎn)要看藍(lán)色的部分,即反向的狀態(tài)值對(duì)應(yīng)的是原始數(shù)據(jù)中最其實(shí)的序列輸入。因?yàn)槭欠聪騌NN,在反向循環(huán)時(shí),是會(huì)把序列中最后的放在最前面,所以反向網(wǎng)絡(luò)的生成結(jié)果就會(huì)與最開(kāi)始的序列相對(duì)應(yīng)。
對(duì)于特征提取任務(wù)處理時(shí),正向與反向的最后值都為該序列的特征,需要合并起來(lái)統(tǒng)一處理。但是對(duì)于下一個(gè)序列預(yù)測(cè)任務(wù)時(shí),建議直接使用正向的RNN網(wǎng)絡(luò)就可以了。
如果要獲取雙向RNN的結(jié)果,尤其是變長(zhǎng)情況下,通過(guò)狀態(tài)拿到值直接拼接起來(lái)才是正確的做法。即便不是變長(zhǎng)。直接使用輸出值來(lái)拼接,會(huì)損失掉反向的一部分特征結(jié)果。這是需要值得注意的地方。
代碼:BiRNN輸出
好了。在接著看下不帶stack的函數(shù)輸出是什么樣子的
gbresult,state_fw=sess.run([Gbioutputs,Goutput_state_fw]) print("正向:\n", gbresult[0]) print("反向:\n", gbresult[1]) print('狀態(tài):',len(state_fw),'\n',state_fw[0],'\n',state_fw[1]) #state_fw[0]:【層,批次,cell個(gè)數(shù)】 重頭到最后一個(gè)序列 print(state_fw[0][-1],state_fw[1][-1]) out = np.concatenate((state_fw[0][-1],state_fw[1][-1]),axis = 1) print("拼接",out)
這次,在輸出基本內(nèi)容基礎(chǔ)上,直接將結(jié)果拼接起來(lái)。上面代碼運(yùn)行后會(huì)輸出如下內(nèi)容。
同樣正向用紅色,反向用藍(lán)色。改函數(shù)返回的輸出值,沒(méi)有將正反向拼接。輸出的狀態(tài)雖然是一個(gè)值,但是里面有兩個(gè)元素,一個(gè)代表正向狀態(tài),一個(gè)代表反向狀態(tài).
從輸出中可以看到,最后一行實(shí)現(xiàn)了最終結(jié)果的真正拼接。在使用雙向rnn時(shí)可以按照上面的例子代碼將其狀態(tài)拼接成一條完整輸出,然后在進(jìn)行處理。
代碼:LSTM的雙向RNN
類(lèi)似的如果想使用LSTM cell。將前面的GRU部分替換即可,代碼如下:
stacked_rnn = [] stacked_bw_rnn = [] for i in range(3): stacked_rnn.append(tf.contrib.rnn.LSTMCell(3)) stacked_bw_rnn.append(tf.contrib.rnn.LSTMCell(3)) mcell = tf.contrib.rnn.MultiRNNCell(stacked_rnn) mcell_bw = tf.contrib.rnn.MultiRNNCell(stacked_bw_rnn) bioutputs, output_state_fw, output_state_bw = tf.contrib.rnn.stack_bidirectional_dynamic_rnn([mcell],[mcell_bw], X,sequence_length=seq_lengths, dtype=tf.float64) bioutputs, output_state_fw = tf.nn.bidirectional_dynamic_rnn(mcell,mcell_bw, X,sequence_length=seq_lengths, dtype=tf.float64)
至于輸出的內(nèi)容是什么,可以按照前面GRU的輸出部分顯示出來(lái)自己觀察。如何拼接,也可以參照GRU的例子來(lái)做。
通過(guò)將正反向的狀態(tài)拼接起來(lái)才可以獲得雙向RNN的最終輸出特征。千萬(wàn)不要直接拿著輸出不加處理的來(lái)進(jìn)行后續(xù)的運(yùn)算,這會(huì)損失一大部分的運(yùn)算特征。
該部分內(nèi)容屬于《深度學(xué)習(xí)之TensorFlow入門(mén)、原理與進(jìn)階實(shí)戰(zhàn)》一書(shū)的內(nèi)容補(bǔ)充。關(guān)于RNN的更多介紹可以參看書(shū)中第九章的詳細(xì)內(nèi)容。
我對(duì)雙向RNN 的理解
1、雙向RNN使用的場(chǎng)景:有些情況下,當(dāng)前的輸出不只依賴(lài)于之前的序列元素,還可能依賴(lài)之后的序列元素; 比如做完形填空,機(jī)器翻譯等應(yīng)用。
2、Tensorflow 中實(shí)現(xiàn)雙向RNN 的API是:bidirectional_dynamic_rnn; 其本質(zhì)主要是做了兩次reverse:
第一次reverse:將輸入序列進(jìn)行reverse,然后送入dynamic_rnn做一次運(yùn)算.
第二次reverse:將上面dynamic_rnn返回的outputs進(jìn)行reverse,保證正向和反向輸出的time是對(duì)上的.
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python自動(dòng)化辦公之讀取Excel數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Python實(shí)現(xiàn)Excel數(shù)據(jù)的讀取,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)有一定幫助,需要的可以參考一下2022-05-05python3.6+opencv3.4實(shí)現(xiàn)鼠標(biāo)交互查看圖片像素
這篇文章主要為大家詳細(xì)介紹了python3.6+opencv3.4實(shí)現(xiàn)鼠標(biāo)交互查看圖片像素,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02Python基礎(chǔ)教程之控制結(jié)構(gòu)詳解
Python中有三大控制結(jié)構(gòu),分別是順序結(jié)構(gòu)、分支結(jié)構(gòu)(選擇結(jié)構(gòu))以及循環(huán)結(jié)構(gòu),任何一個(gè)項(xiàng)目或者算法都可以使用這三種結(jié)構(gòu)來(lái)設(shè)計(jì)完成,這篇文章主要給大家介紹了關(guān)于Python基礎(chǔ)教程之控制結(jié)構(gòu)的相關(guān)資料,需要的朋友可以參考下2021-11-11Python+selenium實(shí)現(xiàn)瀏覽器基本操作詳解
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)python腳本實(shí)現(xiàn)瀏覽器的一些基本操作,如:瀏覽器的前進(jìn)后退、頁(yè)面刷新等,感興趣的可以學(xué)習(xí)一下2022-06-06Python 數(shù)據(jù)處理庫(kù) pandas進(jìn)階教程
在前面一篇文章中,我們對(duì)pandas做了一些入門(mén)介紹。本文是它的進(jìn)階篇。在這篇文章中,我們會(huì)講解一些更深入的知識(shí)2018-04-04pycharm之英文輸入法變成全角字符無(wú)法輸入問(wèn)題
這篇文章主要介紹了pycharm之英文輸入法變成全角字符無(wú)法輸入問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Python中的數(shù)據(jù)標(biāo)準(zhǔn)化與反標(biāo)準(zhǔn)化全面指南
在數(shù)據(jù)處理和機(jī)器學(xué)習(xí)中,數(shù)據(jù)標(biāo)準(zhǔn)化是一項(xiàng)至關(guān)重要的預(yù)處理步驟,標(biāo)準(zhǔn)化能夠?qū)⒉煌叨群头秶臄?shù)據(jù)轉(zhuǎn)換為相同的標(biāo)準(zhǔn),有助于提高模型的性能和穩(wěn)定性,Python提供了多種庫(kù)和函數(shù)來(lái)執(zhí)行數(shù)據(jù)標(biāo)準(zhǔn)化和反標(biāo)準(zhǔn)化,如Scikit-learn和TensorFlow2024-01-01學(xué)生如何免費(fèi)使用Pycharm專(zhuān)業(yè)版學(xué)生認(rèn)證教程
這篇文章主要介紹了學(xué)生如何免費(fèi)使用Pycharm專(zhuān)業(yè)版,學(xué)生認(rèn)證教程,有了這個(gè)教程在校期間就可以免費(fèi)使用Pycharm,簡(jiǎn)直是學(xué)生黨的福音,快來(lái)一起看看吧2023-03-03