如何利用深度學習寫詩歌

七月線上實驗室發表於2018-04-02

原文:https://www.analyticsvidhya.com/blog/2018/03/text-generation-using-python-nlp/


由於自然語言處理(NLP)領域的重大進步,機器能夠自己理解上下文和編造故事。文字生成的例子包括,機器編寫了流行小說的整個章節,比如《權力的遊戲》和《哈利波特》,取得了不同程度的成功。在本文中,我們將使用python和文字生成的概念來構建一個機器學習模型,可以用莎士比亞的風格來寫十四行詩。讓我們來看看它!640?wx_fmt=other&wxfrom=5&wx_lazy=1


什麼是文字生成

現在,有大量的資料可以按順序分類。它們以音訊、視訊、文字、時間序列、感測器資料等形式存在。針對這樣特殊類別的資料,如果兩個事件都發生在特定的時間內,A先於B和B先於A是完全不同的兩個場景。然而,在傳統的機器學習問題中,一個特定的資料點是否被記錄在另一個資料點之前是不重要的。這種考慮使我們的序列預測問題有了新的解決方法。

文字是由一個挨著一個的字元組成的,實際中是很難處理的。這是因為在處理文字時,可以訓練一個模型來使用之前發生的序列來做出非常準確的預測,但是之前的一個錯誤的預測有可能使整個句子變得毫無意義。這就是讓文字生成器變得棘手的原因! 
為了更好地理解程式碼,請瀏覽這兩篇文章。LSTM背後的理論(連結:https://www.analyticsvidhya.com/blog/2017/12/fundamentals-of-deep-learning-introduction-to-lstm/)


文字生成的步驟

  • 匯入依賴

  • 載入資料

  • 建立對映

  • 資料預處理

  • 模型構建

  • 生成文字

讓我們詳細地看一下每一個步驟。


匯入依賴

import numpy as np
import pandas as pd
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.utils import np_utils


載入資料

text=(open("/Users/pranjal/Desktop/text_generator/sonnets.txt").read())
text=text.lower()

這裡,我們正在載入所有莎士比亞十四行詩的集合,這些十四行詩可以從這裡(連結:http://www.gutenberg.org/cache/epub/1041/pg1041.txt)下載。我清理了這個檔案以刪除開始和結束的學分,並且可以從我的git儲存庫下載。 文字檔案被開啟並儲存在text中。然後將該內容轉換為小寫,以減少可能單詞的數量(稍後將對此進行詳細介紹)。


建立對映

對映是在文字中為字元/單詞分配任意數字的步驟。這樣,所有的惟一字元/單詞都對映到一個數字。這一點很重要,因為機器比文字更能理解數字,這使得訓練過程更加容易。

characters = sorted(list(set(text)))
n_to_char = {n:char for n, char in enumerate(characters)}
char_to_n = {char:n for n, char in enumerate(characters)}

我已經建立了一個字典,其中給文字中每個獨特的字元分配一個數字。所有獨特的字元首先儲存在字元中,然後被列舉。

這裡還必須注意,我使用了字元級別的對映,而不是單詞對映。然而,與基於字元的模型相比,基於單詞的模型與其他模型相比具有更高的準確性。這是因為基於字元需要一個更大的網路來學習長期依賴關係,因為它不僅要記住單詞的順序,而且還要學會預測一個語法正確的單詞。但是,在基於單詞的模型中,後者已經被處理好了。


資料預處理

在構建LSTM模型時,這是最棘手的部分。將手頭的資料轉換成可供模型訓練的格式是一項困難的任務。 我會把這個過程分解成小的部分,讓你更容易理解。

X = []
Y = []
length = len(text)
seq_length = 100
for i in range(0, length-seq_length, 1):
sequence = text[i:i + seq_length]
label =text[i + seq_length]
X.append([char_to_n[char] for char in sequence])
Y.append(char_to_n[label])

這裡,X是我們的訓練序列,Y是我們的目標陣列。seq_length是我們在預測某個特定字元之前要考慮的字元序列的長度。for迴圈用於遍歷文字的整個長度,並建立這樣的序列(儲存在X中)和它們的真實值(儲存在Y中),為了更好地弄清楚“真實值”的概念。讓我們以一個例子來理解這一點: 


對於4的序列長度和文字“hello india”,我們將有X和Y表示如下: 
640?wx_fmt=png

現在,LSTMs接受輸入的形式是(number_of_sequence, length_of_sequence, number_of_features),這不是陣列的當前格式。另外,我們需要將陣列Y轉換成一個one-hot編碼格式。

X_modified = np.reshape(X, (len(X), seq_length, 1))
X_modified = X_modified / float(len(characters))
Y_modified = np_utils.to_categorical(Y)

我們首先將陣列X重構為所需的維度。然後,我們將X_modified的值進行縮放,這樣我們的神經網路就可以更快地訓練,並且更少的機會被困在區域性最小值中。此外,我們的Y_modified是一個熱編碼,以刪除在對映字元過程中可能引入的任何順序關係。也就是說,與“z”相比,“a”可能會被分配一個較低的數字,但這並不表示兩者之間有任何關係。 

最後的陣列將是: 
640?wx_fmt=png


建立模型

def vgg_bn_drop(input):
   def conv_block(ipt, num_filter, groups, dropouts, num_channels=None):
model = Sequential()
model.add(LSTM(400, input_shape=(X_modified.shape[1],            X_modified.shape[2]), return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(400))
model.add(Dropout(0.2))
model.add(Dense(Y_modified.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')

我們正在構建一個具有兩個LSTM層的序列模型,每個層有400個單元。第一層需要用輸入形狀輸入。為了使下一個LSTM層能夠處理相同的序列,我們輸入return_sequence引數為真。 此外,設定引數為0.2的dropout層,以檢查是否過擬合。最後一層輸出one-hot編碼的向量,它提供字元輸出。

文字生成

string_mapped = X[99]
for i in range(seq_length):
x = np.reshape(string_mapped,(1,len(string_mapped), 1))
x = x / float(len(characters))
pred_index = np.argmax(model.predict(x, verbose=0))
seq = [n_to_char[value] for value in string_mapped]
string_mapped.append(pred_index)
string_mapped = string_mapped[1:len(string_mapped)]

我們從X陣列中的隨機一行開始,這是一個100個字元的陣列。在此之後,我們的目標是預測x後面的另外100個字元,輸入將如同前面一樣被重新塑造和縮放,預測下一個具有最大概率的字元。seq用於儲存到現在為止預測行為用到的字串的解碼格式。接下來,新字串被更新,這樣第一個字元被刪除,新的預測字元被包含進來。您可以在這裡找到整個程式碼。這裡提供了訓練檔案,註釋和訓練的模型權重供您參考。

嘗試不同模型

基線模型:

當訓練為1個週期,批大小為100時,給出如下輸出:

's the riper should by time decease,
his tender heir might bear his memory:
but thou, contracted toet she the the the the the the the the
thi the the the the the the the the the the the the the the the the the
thi the the the the the the the the the the the the the the the the the
thi the the the the the the the the the the the the the the the the the
thi the the the the the the the the the the the the the the the the the
thi the the the the the the the the th'

這個輸出沒有多大意義。它只是重複同樣的預測,就好像它被困在一個迴圈中一樣。這是因為與我們訓練的微型模型相比,語言預測模型太複雜了。 
讓我們試著訓練同樣的模型,但是將時間週期變長。


加強訓練時間的模型:

這次我們訓練我們的模型為100個週期,批大小設定為50。我們至少得到了一個非重複的字元序列,其中包含了相當數量的合理單詞。此外,該模型還學會了生成一個類似14行詩的結構。

'The riper should by time decease,
his tender heir might bear his memory:
but thou, contracted to thine own besire,
that in the breath ther doomownd wron to ray,
dorh part nit backn oy steresc douh dxcel;
for that i have beauty lekeng norirness,
for all the foowing of a former sight,
which in the remame douh a foure to his,
that in the very bumees of toue mart detenese;
how ap i am nnw love, he past doth fiamee.
to diserace but in the orsths of are orider,
waie agliemt would have me '

但是,這個模型還不夠好,不能產生高質量的內容。所以現在我們要做的是當一個深度學習模型沒有產生好的結果時,每個人都會做的事情。建立一個更深層次的架構!


一個更深的模型:

一位機器學習的大牛曾經說過:如果模型做得不好,那就增加層數!我將用我的模型做同樣的事情。讓我們再新增一個LSTM層,裡面有400個單元,然後是一個引數為0.2的dropout層,看看我們得到了什麼。

"The riper should by time decease,
his tender heir might bear his memory:
but thou, contracted to the world's false sporoe,
with eyes so dond touls be thy domfornds,
which for memorion of the seasons new;
mike own self-love to shou art constant
how can i then be oy love doth give,
the rose looks fair, but fairer bomments age.
now with the seas that i have seen dychl
that mot to teed of you, if h ho bontent,
and all the ress of the heartai thee;
the world will was iis bound and farty "

結果開始變得有趣。語法實現了自我增強,保持了十四行詩的結構和標點的完整。然而,這仍然需要很大的改進。讓我們嘗試探索一個更廣泛的網路,一個擁有更多單元的網路。


一個更廣的模型:

在兩個LSTM層中,我將單元的數量增加到700個。這個調整產生了以下的詩歌:

"The riper should by time decease,
his tender heir might bear his memory:
but thou, contracted to thine own bright eyes,
feed'st thy sigh aare so beuider poise,
oor maty dis surre that gairs mot me love thee;
when i braye the would and lays in the eesire.
than thmu disgrmed stand with my bootr still nsentente;
tell ia to thou art thou wilt woon'thy sook,
and touain then tor, give thy soue semping.
whose whod the better of your befaiss nu sante,
though i, suoll oas, and i lose bouh twa"

這個做法並沒有令我滿意,因為這些詞已經失去了意義。但是,有趣的是,這個模型開始嘗試建立一些韻腳。模型畢竟是在開始嘗試更深層次理解詩歌!但是,對於訓練出既有意義又優美的詩歌的目標,我們不能妥協,對嗎? 

讓我們把它放在一個超大的模型中。


一個超大的模型:

我將層數增加到3層,每層有700個單元,並訓練了100個週期。產生的結果是一篇巨集偉的詩篇。請看:

"The riper should by time decease,
his tender heir might bear his memory:
but thou, contracted to thine own bright eyes,
feed'st thy light's flame with self-substantial fuel,
my beept is she breat oe bath dasehr ill:
tirse do i pine and turfeit day by day,
or gluttoning on all, or all away.
Lxxvi
why is my verse so barren of new pride,
so far from variation or quick change?
why with the time do i not glance aside
to new-found methods, and to compounds strange?
why write i stil"

這篇詩歌不僅用詞準確,而且也學會了押韻。我們本可以有一件更巨集偉的藝術品,但是作為入門LSTM的一個開始,這就夠了。這已經是比大多數人更好地表達了!

推薦閱讀

2018年最佳深度學習書單

如何六步教女朋友寫 Python ?

用Python實現圖片的清晰掃描

程式猿月薪過7萬可落戶北京,重點是...

使用 Python 實現資料視覺化(完整程式碼)

640?wx_fmt=png

相關文章