2019年2月21日 星期四

深度學習 - 使用循環神經網絡(LSTM)生成文本

接下來,練習的東西越來越有趣了! 這也是為什麼我想學習 AI 的原因,現在越來越多聊天機器人、會下圍棋機器人(AlphaGo),還屌虐圍棋界高手,最近 DeepMind 又發表了 AlphaStar 玩星海爭霸II 戰勝職業選手。
想想自己未來也能建個 model 來訓練自己打傳說對決,輕鬆上S排,就迫不及待想成為AI 的專家(誤...)

使用循環神經網絡(LSTM)生成文本,要怎麼做到呢?

首先,要先了解生成文本是屬於哪一類的問題?

訓練生成文本的模型,基本上也是輸入一堆的文本資料,讓模型學到文字間的分佈關係,接著輸出是預測一段文字接著要放入哪個文字的機率,ex. 我今天很開__ ,__應該預測填入"心",這種給定前面的標記(token),能夠對下一個標記的概率進行建模的網絡叫作語言模型(language model)。語言模型能夠捕捉到語言的潛在空間(latent space),即語言的統計結構。

總之,就是一個機率的問題,也是多分類的問題,損失函數(loss function)使用categorical_crossentropy。如果模型的輸出是對所有可能的字符做 softmax,得到下一個字符的概率分佈。這個 LSTM 叫作字符級的神經語言模型(character-level neural language model),如下圖,如果是對單詞,則為單詞級的神經語言模型(word-level neural language model)。

在生成文本的時候,在預測生成下一個字符或單詞要如何選擇,有兩個方法:
  1. greedy sampling,貪婪採樣,在生成過程只選擇可能性最大的,意即只有某個特定字符的概率為 1,其他字符概率都為 0,這樣做會讓生成的文章更接近真實文章,同時也會讓文章重複率提高,使文章變得無趣。
  2. stochastic sampling,隨機採樣,在概率分佈中隨機選擇其中一個,我們無法控制生成的字符,會讓文章看起來有點無俚頭,但是這樣可以讓模型產生更有意思的句子,是我們無法想像的。
我們需要採取兩種採樣間的平衡,我們引入一個叫作 softmax 溫度(softmax temperature)的參數,用於表示採樣概率分佈的熵,即表示所選擇的下一個字符會有多麼出人意料或多麼可預測。給定一個temperature 值,將按照下列方法對原始概率分佈(即模型的softmax 輸出)進行重新加權,計算得到一個新的概率分佈。

來實際練習字符級的LSTM 文本生成,書中使用範例是尼采 Nietzsche 的文學作品
#下載並解析初始文本文件
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))

#將字符序列向量化
maxlen = 60     #提取60 個字符組成的序列
step = 3        #每3個字符採樣一個新序列
sentences = []  #保存所提取的序列
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(sentences))
chars = sorted(list(set(text)))
print('Unique characters:', len(chars))
char_indices = dict((char, chars.index(char)) for char in chars)
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

#建構網絡模型
from keras import layers
model = keras.models.Sequential()
model.add(layers.LSTM(128, input_shape=(maxlen, len(chars))))
model.add(layers.Dense(len(chars), activation='softmax'))

#編譯器,優化器&損失函數
optimizer = keras.optimizers.RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)

#給定模型預測,採樣下一個字符的函數
def sample(preds, temperature=1.0):
    preds = np.asarray(preds).astype('float64')
    preds = np.log(preds) / temperature
    exp_preds = np.exp(preds)
    preds = exp_preds / np.sum(exp_preds)
    probas = np.random.multinomial(1, preds, 1)
    return np.argmax(probas)

#生成文本循環
import random
import sys
for epoch in range(1, 60):
    print('epoch', 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 + '"')
    model.save_weights('LSTM_gen_model.h5') #將model 保存起來
    for temperature in [0.2, 0.5, 1.0]:
        print('------ temperature:', temperature,"\n")
        sys.stdout.write(generated_text)
        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, temperature)
            next_char = chars[next_index]
            generated_text += next_char
            generated_text = generated_text[1:]
            sys.stdout.write(next_char)

可以看到訓練過程中,每個epochs 會隨機生成一段種子文本,然後經由模型預測生成後面的文章。隨著temperature的不同,會產生不一樣的文章。

舉例,在某一次生成的文本中
temperature=0.2 所產出的文章的其中一段"the mankind to the sense of the sense of the presence and the really the best the"

temperature=0.5 "the same noursely and the dease and person which be been an ential the great"

temperature=1.0 "may wild pain immanced to times the and turning way only his egolees lead one for bace regreelens"

可以看到隨著temperature 越大,生成的文章越有創造性,且重複性越低。一定要嘗試多種採樣策略!在學到的結構與隨機性之間,巧妙的平衡能夠讓生成的序列非常有趣。

沒有留言:

張貼留言