天天看點

用Keras開發字元級神經網絡語言模型

語言模型可根據序列中出現的特定單詞來預測下一個單詞。可以使用神經網絡在字元級别上開發語言模型。基于字元的語言模型有一個最大的優點,就是在處理單詞、标點符号和其他文檔結構的時候,能保持較小的詞彙量和較強的靈活性。但所付出的代價是模型較大、訓練較慢。然而,在神經網絡語言模型領域,基于字元的模型為語言模組化提供了一種通用、靈活和強大的方法。

在本教程中,你将了解到如何開發基于字元的神經網絡語言模型。

學習完本教程,你将學會:

如何針對基于字元的語言模組化準備文本。

如何使用LSTM開發基于字元的語言模型。

如何使用訓練過的基于字元的語言模型來生成文本。

本教程分為四個部分:

Sing a Song of Sixpence(譯者注:一首英文童謠)

資料準備

訓練語言模型

生成文本

這首童謠很短,是以模型的拟合會很快,但不能太短,那樣我們就不會看到任何有意思的東西。下面是這首童謠完整歌詞:

Sing a song of sixpence, A pocket full of rye. Four and twenty blackbirds, Baked in a pie. When the pie was opened The birds began to sing; Wasn’t that a dainty dish, To set before the king. The king was in his counting house, Counting out his money; The queen was in the parlour, Eating bread and honey. The maid was in the garden, Hanging out the clothes, When down came a blackbird And pecked off her nose.

複制這段文本,并将其儲存到目前工作目錄中的一個新檔案中,檔案名為“rhyme.txt”。

第一步是準備文本資料。我們将首先定義語言模型的類型。

語言模型必須用文本進行訓練,在基于字元的語言模型中,輸入和輸出序列必須是字元。用于輸入的字元的個數決定了需要提供給模型以引出第一個預測字元的字元數。在第一個字元生成之後,可以将其添加到輸入序列上,作為模型的輸入以生成下一個字元。

序列越長,則為模型提供的上下文也越多,同時,模型将耗費更長的時間來進行訓練。我們這個模型使用的字元的長度是10。

下面我們将把原始文本轉換成模型可以學習的形式。

童謠的歌詞必須加載到記憶體之後才能使用。下面是一個名為<code>load_doc()</code>的函數,用于加載指定檔案名的文本檔案并傳回加載的文本。

可以使用這個函數來加載名為“rhyme.txt”的檔案,并将内容放入記憶體中。然後将檔案的内容列印到螢幕上進行完整性檢查。

接下來,需要淨化加載的文本。

這裡我們不會做太多的事情,隻是删除所有的換行符,轉換成一段按空格進行分割的長字元序列。

你可能需要探索一下淨化資料的其他一些方法,例如将文本轉換為小寫字母或删除标點符号,以減少最終的詞彙量,這樣可以開發出更小、更精簡的模型。

長字元清單有了,下面就可以建立用于訓練模型的輸入輸出序列了。

每個輸入序列包含十個字元和一個輸出字元,是以,每個序列包含了11個字元。我們可以通過枚舉文本中的字元來建立序列,從索引為10也就是第11個字元開始。

運作這段代碼,我們可以看到,用來訓練語言模型的序列其實隻有不到400個字元。

最後,将準備好的資料儲存到檔案中,後面在開發模型的時候再加載。

下面是<code>save_doc()</code>函數,給定字元串清單和檔案名,将字元串儲存到檔案中,每行一個字元串。

調用這個函數,将準備好的序列儲存到目前工作目錄下的“char_sequences.txt”檔案中。

将上面那些代碼片段組合到一起,組成下面這份完整的代碼:

運作該示例,将生成“char_seqiences.txt”檔案,内容如下:

下面準備訓練基于字元的神經語言模型。

本章節将為上面準備好的序列資料開發一個神經語言模型。該模型将讀取已經編碼的字元,并預測出序列的下一個字元。

第一步是從"char_sequences.txt"加載字元序列資料。

我們可以使用上一章節中開發的<code>load_doc()</code>函數。載入後,将文本按換行符進行分割以得到序列清單。

字元序列必須編碼為整數。也就是說每個字元都會被配置設定一個指定的整數值,每個字元序列都會被編碼為一個整數序列。

我們可以根據原始輸入資料來建立映射關系。該映射關系是字元值映射到整數值的字典。

接下來,逐個處理每個字元序列,并使用字典映射來查找每個字元的整數值。

運作的結果是整數序列清單。

字典映射表的大小即詞彙表的大小。

運作這段代碼,我們可以看到輸入資料中的字元剔重後有38個。

現在,序列已經被編碼成整數了,下面可以将列分割成輸入和輸出字元序列。可以使用一個簡單的數組切片來完成此操作。

接下來,将對每個字元進行獨熱編碼,也就是說,每個字元都會變成一個向量。這為神經網絡提供了更精确的輸入表示,還為網絡預測提供了一個明确的目标。

我們可以使用Keras API中的<code>to_categorical()</code>函數來對輸入和輸出序列進行獨熱編碼。

現在,我們已經為模型的拟合做好準備了。

該模型使用了一個針對獨熱編碼輸入序列采用10個時間步長和38個特征的輸入層進行定義。我們在X輸入資料上使用第二和第三個次元,而不是指定這些數字。這是因為當序列的長度或詞彙表的大小發生改變的實話,無需改變模型的定義。

該模型有一個包含75個存儲器單元的LSTM隐藏層,通過一些試驗和錯誤進行選擇。

該模型有一個完全連接配接的輸出層,輸出一個詞彙表中所有字元機率分布的向量。在輸出層上使用softmax激活函數來確定輸出具有機率分布的屬性。

運作這段代碼将列印出網絡的概要資訊以進行完整性檢查。

該模型将執行100次訓練疊代來進行拟合。

模型拟合完成之後,将其儲存到檔案以備後面使用。Keras提供了<code>save()</code>函數,可以使用該函數将模型儲存到單個檔案中,包括權重和拓撲資訊。

另外還要儲存從字元到整數的映射關系,因為在使用模型的時候,需要對任意的輸入進行編碼,并對模型的輸出進行解碼。

将上面那些代碼片段組合到一起,組成下面這份基于字元的神經網絡語言模型的完整代碼:

這個例程的運作時間可能需要一分鐘。

運作結束之後,會在目前目錄生成兩個檔案,model.h5和mapping.pkl。

接下來,看一下如何使用這個學習過的模型。

我們将使用這個學習過的語言模型來生成具有相同統計特性的新的文本序列。

第一步是加載檔案“model.h5”中的模型,可以使用Keras API中的<code>load_model()</code>函數進行加載。

還需要加載檔案“mapping.pkl”檔案中的字典,用于将字元映射為整數。

下面可以使用這個模型了。

為了啟動生成過程,必須提供包含10個字元的序列作為模型的輸入。

首先,字元序列必須使用加載進來的映射關系編碼為整數值。

接下來,使用Keras中的<code>pad_sequences()</code>函數對整數值進行獨熱編碼,并将序列重塑為三個次元。因為我們隻有一個序列,而且LSTM需要所有的輸入都有三個次元(樣本、時間步長、特征)。

下面,就可以使用模型來預測序列中的下一個字元了。

使用<code>predict_classes()</code>而不是<code>predict()</code>來直接選擇具有最高機率的字元整數。

可以通過查找映射中的整數-字元關系來對整數進行解碼。

這個字元随後可以添加到輸入序列中去。然後通過截斷輸入序列文本中的第一個字元來確定輸入序列是10個字元的長度。可以使用Keras API中的<code>pad_sequences()</code>函數來執行截斷操作。

把上面這些放在一起,定義一個名為<code>generate_seq()</code>的新函數來使用模型生成新的文本序列。

運作該示例将生成三個文本序列。

第一個是測試這個模型在從童謠的開頭進行預測的話表現如何。第二個是測試從某一行的中間開始預測表現如何。最後是測試模型遇到從未見過的字元序列時表現如何。

我們可以看到,這個模型在前兩個例子中的表現得還不錯,符合預期。但對于新的文本來說,預測的結果就有點匪夷所思了。

通過閱讀本教程,你已經學會了如何開發基于字元的神經網絡語言模型,包括:

文章原标題《How to Develop a Character-Based Neural Language Model in Keras》,作者: Jason Brownlee,譯者:夏天,審校:主題曲。