App下載

【Python】RNN實現(xiàn)文本生成

猿友 2018-08-06 18:47:18 瀏覽數(shù) (7451)
反饋

本文轉(zhuǎn)載至知乎ID:Charles(白露未晞)知乎個人專欄

導語

本文將在此基礎(chǔ)上介紹LSTM網(wǎng)絡(luò)。最后舉一個類似“Python學寫作”的例子來實現(xiàn)文本生成,如生成詩歌、小說等等。

讓我們愉快地開始吧~~~


參考文獻

Understanding LSTM Networks:

http://colah.github.io/posts/2015-08-Understanding-LSTMs/


相關(guān)文件

百度網(wǎng)盤下載鏈接: https://pan.baidu.com/s/16hjmw1NtU9Oa4iwyj4qJuQ

密碼: gmpi


開發(fā)工具

Python版本:3.6.4

相關(guān)模塊:

tensorflow-gpu模塊;

numpy模塊;

以及一些Python自帶的模塊。

其中TensorFlow-GPU版本為:

1.7.0


環(huán)境搭建

安裝Python并添加到環(huán)境變量,pip安裝需要的相關(guān)模塊即可。

另外,TensorFlow-GPU的環(huán)境搭建請自行參考相關(guān)的網(wǎng)絡(luò)教程,注意版本和驅(qū)動嚴格對應(yīng)即可。


原理介紹

一. RNNs

人們的思維總是具有延續(xù)性的,比如當你閱讀這篇文章時,你對每個詞的理解都會依賴于你前面看到的一些詞,而不是把你前面看的內(nèi)容全部拋棄,再去理解每個詞。而傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)(CNN)無法做到這一點,因此有了循環(huán)神經(jīng)網(wǎng)絡(luò)(RNNs)。

在RNNs中,存在循環(huán)操作,使得它們能夠保留之前學習到的內(nèi)容:

在上圖網(wǎng)絡(luò)結(jié)構(gòu)中,對于矩形塊A的那部分,通過輸入Xt(t時刻的特征向量),它會輸出一個結(jié)果ht(t時刻的狀態(tài)或者輸出),網(wǎng)絡(luò)中的循環(huán)結(jié)構(gòu)使得當前狀態(tài)作為下一時刻輸入的一部分。

將RNNs在時間步上進行展開,就可以得到下圖:

也就是“Python實現(xiàn)簡單的機器翻譯模型”一文中所使用的RNNs鏈狀的結(jié)構(gòu)。顯然,這樣的結(jié)構(gòu)是有利于處理序列相關(guān)問題的。近年來,其在語音識別、語言翻譯等等領(lǐng)域都取得了巨大的成功。

而RNNs的成功,主要歸功于LSTMs這種特殊RNNs結(jié)構(gòu)的使用,而非普通的RNNs結(jié)構(gòu)。

二. LSTMs

全稱為Long Short Term Memory networks.

即長短期記憶網(wǎng)絡(luò)。

普通RNNs的局限性在于當我們所要預(yù)測的內(nèi)容和相關(guān)信息之間的間隔很大時,普通RNNs很難去把它們關(guān)聯(lián)起來:

盡管從理論上來講,只要參數(shù)合適,還是可以解決長時期依賴關(guān)系無法很好聯(lián)系這一問題的,但具體實現(xiàn)起來似乎并不容易,至少目前為止是不容易的。

幸運的是,LSTMs能夠很好地解決這一問題。它被設(shè)計的初衷就是為了能夠記住長時期內(nèi)的信息。

循環(huán)神經(jīng)網(wǎng)絡(luò)是由相同結(jié)構(gòu)的神經(jīng)網(wǎng)絡(luò)模塊進行復(fù)制而形成的。在標準的RNNs中,神經(jīng)網(wǎng)絡(luò)模塊的結(jié)構(gòu)非常簡單,比如可以由單一的tanh層構(gòu)成:

LSTMs也有類似的結(jié)構(gòu),不過神經(jīng)網(wǎng)絡(luò)模塊的結(jié)構(gòu)變得相對復(fù)雜了一些:

接下來,我們來詳細介紹一下這個結(jié)構(gòu)。首先,我們來定義一下用到的符號:

粉紅色的圈:

代表向量加之類的逐點操作;

黃色矩形框:

代表神經(jīng)網(wǎng)絡(luò)層;

普通的線:

用于攜帶并傳遞向量;

合并的線:

代表對兩條線上所攜帶的向量進行合并;

分開的線:

代表將線上所攜帶的向量復(fù)制后傳給兩個地方。

2.1 LSTMs的核心思想

假設(shè)一個綠色的框就是一個cell。

向量通過結(jié)構(gòu)圖最上面的那條貫穿cell的水平線穿過整個cell,而cell僅對其做了少量的線性操作:

顯然,這樣的結(jié)構(gòu)能夠很輕松地讓信息從整個cell中穿過而不發(fā)生變化。

當然,只有一條水平線是無法實現(xiàn)添加或者刪除信息的,也就是實現(xiàn)讓信息有選擇地通過cell,這需要通過一種叫做門(gates)的結(jié)構(gòu)來實現(xiàn)。

門結(jié)構(gòu)主要由一個sigmoid神經(jīng)網(wǎng)絡(luò)層和一個逐點相乘的操作來實現(xiàn):

sigmoid層輸出的向量每個元素都是介于0和1之間的實數(shù),表示此時通過的信息的權(quán)重,當其為0時表示“此時不讓任何信息通過”,為1時表示“此時讓所有信息通過”。每個LSTM都有三個這樣的門結(jié)構(gòu),來實現(xiàn)保護和控制信息。

2.2 逐步理解LSTM

遺忘門(forget gate layer):

首先,LSTM需要決定哪些信息需要丟棄,哪些信息需要保留。這是通過一個叫做遺忘門的sigmoid層來實現(xiàn)的。它的輸入是ht-1xt,輸出是一個數(shù)值都在0到1之間的向量,表示Ct-1中各部分信息的權(quán)重,0表示不讓該部分信息通過,1表示讓該部分信息全部通過。

具體而言,比如在語言模型中,我們要根據(jù)所有的上下文信息來預(yù)測下一個詞。在這種情況下,每個cell的狀態(tài)中都應(yīng)該包含了當前主語的性別信息。這樣,接下來我們才能夠正確地使用代詞。但是,當我們開始描述一個新的主語時,就應(yīng)該把之前的主語性別給丟棄了才對。

傳入門(input gate layer):

其次,LSTM將決定讓哪些新的信息加入到cell的狀態(tài)中來。該實現(xiàn)分兩個步驟進行:

① 用一個tanh層生成一個備選向量,用于表示獲得的所有可添加信息;

② 用一個叫做傳入門的sigmoid層來決定步驟①中獲得的可添加信息各自的權(quán)重。

具體而言,比如在語言模型中,我們需要把新主語的性別信息添加到cell狀態(tài)中,來替換掉之前的主語性別信息。

有了遺忘門和傳入門,我們就能夠更新cell的狀態(tài)了,即把Ct-1更新為Ct

還是以語言模型為例,假設(shè)我們的模型剛輸出了一個代詞,接下來可能要輸出一個動詞,那么這個動詞應(yīng)該采用單數(shù)形式還是復(fù)數(shù)形式呢?顯然,我們需要把代詞相關(guān)的信息和當前的預(yù)測信息都加入到cell的狀態(tài)中來,才能夠進行正確的預(yù)測。

具體計算方式如下圖所示:

輸出門(Output):

最后,我們需要決定輸出值。輸出值的計算方式為:

① 使用sigmoid層來決定/計算出Ct中的哪部分信息會被輸出;

② 利用tanh層將Ct的取值壓縮到-1到1之間;

③ 將tanh層的輸出和sigmoid層的輸出相乘即為最終的輸出結(jié)果。

三. LSTMs的變種

① 將cell的狀態(tài)作為門結(jié)構(gòu)輸入的一部分。

② 將遺忘門與傳入門耦合,即不再分開決定要遺忘和添加的信息。

③ GRU

GRU模型“簡化”了LSTM模型的設(shè)計,其中rt由LSTM中的遺忘門和傳入門合并而得,稱為重置門;zt為更新門,作用相當于LSTM中的輸出門。

實際應(yīng)用

為了貫徹理論與實踐相結(jié)合的理念,本文將舉一個簡單的小例子,該例子使用的模型與“Python學寫作”類似,本文不再作多余的介紹。

具體實現(xiàn)過程詳見相關(guān)文件中的源代碼。

使用演示

模型訓練:

在cmd窗口運行'train.py'文件即可:

如有需要,可自行修改相關(guān)參數(shù):

模型使用:

在cmd窗口運行“generate.py”文件即可。

注意模型參數(shù)需和train.py文件中的模型參數(shù)一致:

結(jié)果展示

生成英文文本:

以莎士比亞的作品為訓練素材獲得的結(jié)果:

生成中文文本:

以周杰倫的作品為訓練素材獲得的結(jié)果:


更多

代碼截止2018-06-24測試無誤。

模型比較簡單,有興趣的朋友可以在此基礎(chǔ)上進行優(yōu)化,當然RNN的作用可不僅僅是文本生成哦~

以后有機會再舉其他例子吧~~~

0 人點贊