keras使用LSTM生成文字
阿新 • • 發佈:2018-11-30
本文主要介紹使用LSTM實現字元級文字生成。
下面是示例程式碼:
# coding: utf-8 # In[1]: # 下載語料庫並將其轉化為小寫 import keras import numpy as np path = keras.utils.get_file( 'nietzsche.txt', origin='https://s3.amazonaws.com/text-datasets/nietzsche.txt') text = open(path).read().lower() print('Corpus length:', len(text)) # In[11]: ''' 接下來,將提取長度為“maxlen”的部分重疊序列,對它們進行one-hot 編碼並將它們打包成形狀為“(sequence,maxlen,unique_characters)” 的3D Numpy陣列`x`。 同時,準備一個包含相應目標的陣列`y`:在每個提取序列之後的one-hot編碼字元。 ''' # 提取的字元序列的長度 maxlen = 60 # 對每‘step’個字元序列取樣一個新序列 step = 3 # 用於儲存提取到的序列 sentences = [] # 用於儲存targets next_chars = [] for i in range(0, len(text) - maxlen, step): sentences.append(text[i: i + maxlen]) next_chars.append(text[i + maxlen]) print('Number of sequences:', len(setences)) # 語料庫中的唯一字元列表 chars = sorted(list(set(text))) print('Unique characters:', len(chars)) # 將唯一字元對映到`chars`中索引的字典 char_indices = dict((char, chars.index(char)) for char in chars) # 接下來,將字元one-hot編碼為二維陣列 print('Vectorization...') x = np.zeros((len(sentences),maxlen, len(chars)), dtype=np.bool) y = np.zeros((len(sentences), len(chars)), dtype=np.bool) for i, sentence in enumerate(sentences): for t, char in enumerate(sentence): x[i, t, char_indices[char]] = 1 y[i, char_indices[next_chars[i]]] = 1 # In[15]: ''' 構建網路 網路是一個單獨的'LSTM`層,後跟一個'Dense'分類器和所有可能字元的softmax。 迴圈神經網路不是生成序列資料的唯一方法; 1D convnets也被證明非常成功。 ''' from keras import layers from keras.models import Sequential from keras.optimizers import RMSprop model = Sequential() model.add(layers.LSTM(128, input_shape=(maxlen, len(chars)))) model.add(layers.Dense(len(chars), activation='softmax')) # In[16]: # 由於targets是one-hot編碼,因此使用`categorical_crossentropy`作為訓練模型的損失 optimizer = RMSprop(lr=1e-2) model.compile(loss='categorical_crossentropy', optimizer=optimizer) ''' 訓練語言模型並從中抽樣 給定已訓練的模型和原文字片段,重複生成新文字: * 1)從模型中得出目前可用文字的下一個字元的概率分佈 * 2)將分佈重新調整到某個“temperature” * 3)根據重新加權的分佈隨機抽樣下一個字元 * 4)在可用文字的末尾新增新字元 這是用來重新加權模型中出現的原始概率分佈的程式碼,並從中繪製一個字元索引(“抽樣函式”): ''' def sample(preds, temperatue=1.0): preds = np.array(preds).astype('float64') preds = np.log(preds) / temperatue exp_preds = np.exp(preds) preds = exp_preds / np.sum(exp_preds) probas = np.random.multinomial(1, preds, 1) return np.argmax(probas) # In[ ]: ''' 最後,反覆訓練和生成文字的迴圈。 開始在每個epoch之後使用一系列不同 的溫度生成文字。 可以看到生成的文字在模型開始收斂時如何演變, 以及溫度對抽樣策略的影響。 ''' import random import sys for epoch in range(1, 60): print('epoch', epoch) # 在可用的訓練資料上使模型適合1個epoch model.fit(x, y, batch_size=128, epochs=1) # 隨機選擇一個原文字片段 start_index = random.randint(0, len(text) - maxlen - 1) generated_text = text[start_index: start_index + maxlen] print('--- Generating with seed:“ ' + generated_text + ' ”') for temperature in [0.2, 0.5, 1.0, 1.2]: print('-----temperature:', temperature) sys.stdout.write(generated_text) # 生成400字元 for i in range(400): sampled = np.zeros((1, maxlen, len(chars))) for t, char in enumerate(generated_text): sampled[0, t, char_indices[char]] = 1 preds = model.predict(sampled, verbose=0)[0] next_index = sample(preds=preds, temperatue=temperature) next_char = char[next_index] generated_text += next_char generated_text = generated_text[1:] sys.stdout.write(next_char) sys.stdout.flush() print() # In[ ]: ''' 如上所示,低的temperature會產生極其重複且可預測的文字,但是在本地結構非常逼真的情況下: 特別是所有單詞(一個單詞是本地字元模式)都是真正的英語單詞。隨著溫度的升高,生成的文字變得更有趣,令人驚訝,甚至創造性;它有時可能會發明一些聽起來有些合理的新詞(例如“eterned”或“troveration”)。在高溫下,區域性結構開始分解,大多數單詞看起來像半隨機字串。毫無疑問,這裡的0.5是這個特定設定中文字生成最有趣的溫度。始終嘗試多種取樣策略!學習結構和隨機性之間的巧妙平衡是讓生成有趣的原因。 請注意,通過訓練更大的模型,更長的時間,更多的資料,您可以獲得生成的樣本,這些樣本看起來比我們的更連貫和更真實。但是,當然,除了隨機機會之外,不要期望生成任何有意義的文字:我們所做的只是從統計模型中取樣資料,其中字元來自哪些字元。語言是一種通訊渠道,通訊的內容與通訊編碼的訊息的統計結構之間存在區別。為了證明這種區別,這裡有一個思想實驗:如果人類語言在壓縮通訊方面做得更好,就像我們的計算機對大多數數字通訊做的那樣?那麼語言就沒那麼有意義,但它缺乏任何內在的統計結構,因此無法像我們一樣學習語言模型。 拿走 *我們可以通過訓練模型來生成離散序列資料,以預測給定前一個令牌的下一個令牌。 *在文字的情況下,這種模型被稱為“語言模型”,可以基於單詞或字元。 *取樣下一個標記需要在遵守模型判斷的可能性和引入隨機性之間取得平衡。 *處理這個的一種方法是_softmax temperature_的概念。總是嘗試不同的溫度來找到“正確”的溫度。 '''