Keras 文字生成系統

博文視點發表於2017-10-13

  同是深度學習“檻內人”,我怎麼不知道這樣高大上的文字生成對話系統

  文字資訊是存在最廣泛的資訊形式之一,而深度學習的序列模型(Sequential Model)在對文字生成建模(Generative Model)方面具備獨特的優勢。文字自動生成可應用於自然語言對話建模和自動文稿生成,極大地提高了零售客服、網路導購以及新聞業的生產效率。目前比較成熟的是單輪對話系統以及基於單輪對話系統的簡單多輪對話系統,這類系統應用範圍廣泛,技術相對成熟,在零售客服、網路導購等領域都有很高的邊際收益。例如蘋果手機中的Siri,以及微軟早期的小冰機器人,都屬於這種系統的嘗試。

  從應用範圍來看,自然對話系統分為所謂的閒聊(Chit-Chat)對話系統和專業(DomainSpecific)對話系統兩種。

  從技術上看,短對話聊天系統分為兩種:基於檢索的系統(Retrieval Based System)和基於文字生成的系統(Generative System)。構造對話機器人的方法可以分為三種,其中有兩種屬於索引式模型,一種屬於最新的生成式模型。

  第一種是基於深度學習流行之前的技術,使用AIML 這種標識語言構造大量的應答庫,通過現有的對文字結構的理解來構造的簡單對話系統。這種系統的構造費時費力,靈活性差,擴充套件性差,智慧度低,很難構造多輪對話系統,但是因為應答都是真人生成的,不會有語法錯誤,語言標準,所以適用於簡單集中的業務環境。

  第二種是使用深度學習方法來尋找對應於當前對話背景的最佳應答,相對於第一種方法降低了很多人工構造應答庫的工作,靈活性高,擴充套件性強,有一定智慧度,可以用來構造多輪對話系統。

  第三種是目前最新研究的領域,使用深度學習技術實時生成應答,靈活度和智慧度都極高,屬於自動擴充套件,但是需要極大量的資料積累和比較複雜的模型才能得到較好的結果。通常第三種系統需要與第二種系統相結合,在第二種系統已有的應答庫中無法找到足夠滿意的選項時,可以啟用第三種系統來實時生成應答。

  願做一次檻外人,學習這基於文字生成的對話系統。這裡我們著重介紹第三種。

基於文字生成的對話系統

  這裡使用作家老舍的小說《四世同堂》作為訓練資料進行演示。讀者可以根據自己的應用和業務環境,選擇合適的資料。

  對於訓練文字可以一次性讀入:

alltext = open("e:\\data\\Text\\四世同堂.txt", encoding='utf-8').read()

  得到的結果是一個巨大的字串列表。因為我們將以每個單字作為建模物件,因此這樣讀入資料是最方便以後操作的。如果要以片語和單句進行建模,則分段讀入最佳。《四世同堂》這本書一共有 3545 個不重複的單字和符號。

  按照對文字序列建模的順序,我們依次進行下面的操作。

  (1) 首先對所有待建模的單字和字元進行索引。

  (2) 其次構造句子序列。

  (3) 然後建立神經網路模型,對索引標號序列進行向量嵌入後的向量構造長短記憶神經網路。

  (4) 最後我們來檢驗建模效果。

  對單字和字元建立索引非常簡單,用下面三句命令即可:

1 sortedcharset = sorted(set(alltext))
2 char_indices = dict((c, i) for i, c in enumerate(sortedcharset))
3 indices_char = dict((i, c) for i, c in enumerate(sortedcharset))

  第一句的對用 set 函式抽取的每個單字的集合按照編碼從小到大排序。第二句對一個單字進行編號索引,第三句進行反向操作,對每個索引建立單字的詞典,主要是為了方便預測出來的索引標號向量轉換為人能夠閱讀的文字。

  構造句子序列也非常簡單:

1 maxlen = 40
2 step = 3
3 sentences = []
4 next_chars = []
5 for i in range(0, len(alltext) - maxlen, step):
6 sentences.append(alltext[i: i + maxlen])
7 next_chars.append(alltext[i + maxlen])
8 print('nb sequences:', len(sentences))

  構造句子序列的原因是原始資料是單字列表,因此需要人為構造句子的序列來模仿句子序列。在上面的程式碼中,maxlen=40 標識人工構造的句子長度為 40 個單字,step=3表示在構造句子時每次跳過 3 個單字,比如用這一串單字列表“這首小令是李清照的奠定才女地位之作,轟動朝野。傳聞就是這首詞,使得趙明誠日夜作相思之夢,充分說明了這首小令在當時引起的轟動。又說此詞是化用韓偓《懶起》詩意。”來構造句子的時候,假設句子長度為 10,那麼第一句是“這首小令是李清照的奠”,而第二句則是移動 3 個單字以後的“令是李清照的奠定才女”。跳字的目的是為了增加句子與句子之間的變化,否則每兩個相鄰句子之間只有一個單字的差異,但是這兩個相鄰句子是用來構造前後對話序列的,缺乏變化使得建模效果不好。當然,如果跳字太多,那麼會大大降低資料量。比如《四世同堂》一共有 711501 個單字和符號,每隔三個字或者符號進行跳字操作構造的句子只有 237154 個,是原資料量的 1/3。如何選擇跳字的個數是讀者在建模的時候要根據情況調整的一個引數。

  需要注意的是,因為句子是人工構造的,都有固定的長度,因此這裡不需要進行句子補齊操作。同時,這些句子的向量其實都是一個稀疏矩陣,因為它們只將包含資料的索引編號計入。人工構造句子完畢後就可以對其矩陣化,即對於每一句話,將其中的索引標號對映到所有出現的單字和符號,每一句話所對應的 40 個字元的向量被投影到一個 3545 個元素的向量中,在這個向量中,如果某個元素出現在這句話中,則其值為 1,否則為 0。

  下面的程式碼執行這個操作:

1 print('Vectorization...')
2 X = np.zeros((len(sentences), maxlen, len(sortedcharset)), dtype=np.bool)
3 y = np.zeros((len(sentences), len(sortedcharset)), dtype=np.bool)
4 for i, sentence in enumerate(sentences):
5 if (i % 30000 == 0):
6 print(i)
7 for t in range(maxlen):
8 char=sentence[t]
9 X[i, t, char_indices[char]] = 1
10 y[i, char_indices[next_chars[i]]] = 1

  當然這個新生成的資料會非常大,比如 X 會是一個 237154 × 40 × 3545 的實數矩陣,實際計算的時候佔用的記憶體會超過 20GB。因此這裡需要使用前面提到的資料生成器(data generator)方法,對一個具有較小批量數的樣本進行投影操作。可以通過下面這個很簡單的函式實現:

1 def data_generator(X, y, batch_size):
2 if batch_size<1:
3 batch_size=256
4 number_of_batches = X.shape[0]//batch_size
5 counter=0
6 shuffle_index = np.arange(np.shape(y)[0])
7 np.random.shuffle(shuffle_index)
8 #reset generator
9 while 1:
10 index_batch = shuffle_index[batch_size*counter:batch_size*(counter+1)]
11 X_batch = (X[index_batch,:,:]).astype('float32')
12 y_batch = (y[index_batch,:]).astype('float32')
13 counter += 1
14 yield(np.array(X_batch),y_batch)
15 if (counter < number_of_batches):
16 np.random.shuffle(shuffle_index)
17 counter=0

  這個函式與前面的 batch_generator 函式非常相似,主要區別是這個函式同時處理X 和 Y 矩陣的小批量生成,另外要求輸入和輸出資料都是 NumPy 多維矩陣而不是列表的列表。另外 Python 裡的數值資料是 float64 型別的,因此專門使用 astype(‘float32’)將矩陣的資料型別強制定為 32 位浮點數以符合 CNTK 對資料型別的要求,這樣不需要在後臺再進行資料型別轉換,從而提高效率。現在可以構造我們的長短記憶神經網路模型了。這時候再次體現了 Keras 的高效建模能力。下面短短几個命令就可以讓我們構造一個深度學習模型:

1 # build the model: a single LSTM
2 batch_size=256
3 print('Build model...')
4 model = Sequential()
5 model.add(LSTM(256, batch_size=batch_size, input_shape=(maxlen, len(sortedcharset)), recurrent_dropout=0.1, dropout=0.1))
6 model.add(Dense(len(sortedcharset)))
7 model.add(Activation('softmax'))
8
9 optimizer = RMSprop(lr=0.01)
10 model.compile(loss='categorical_crossentropy', optimizer=optimizer)

  其中第一句命令指定要生成一個序列模型,第二到第四句命令要求依次新增三層網路,分別是一個長短記憶網路和一個全連線網路,最後使用一個softmax 的啟用層輸出預測。在長短記憶網路裡,規定輸入資料的維度為(時間步數,所有出現的不重複字元的個數),即輸入的資料是對應每一句話處理以後的形式,並且對輸入神經元權重和隱藏狀態權重分別設定了10% 的放棄率。全連線層的輸出維度為所有字元的個數,方便最後的啟用函式計算。最後兩條命令指定網路優化演算法的引數,比如裡面指定損失函式為典型的categorical_crossentropy,優化演算法是指定的學習速率為0.01 的RMSprop演算法。對於迴圈神經網路,這個優化演算法通常表現較好。

  深度學習和人工智慧無疑是現在最熱門的技術之一,很多人希望能掌握這方面的技能,但是擔心門檻太高。這本書可謂是及時雨,給大家提供了非常好的入門學習資料,也是目前國內唯一一本 Keras簡單易用的深度學習框架書。其內容不僅涵蓋了當前深度學習的幾個主要應用領域,而且實用性強,同時也延伸到相關的系搭建、資料獲取以及可預見的未來物聯網方面的應用,非常值得一讀。願你輕鬆跨過這檻,化身時代弄潮的深度學習“檻內人”。

  以上內容節選自《Keras快速上手:基於Python的深度學習實戰》,點此連結可在博文視點官網檢視此書。
                 圖片描述
  想及時獲得更多精彩文章,可在微信中搜尋“博文視點”或者掃描下方二維碼並關注。
                    圖片描述

相關文章