機(jī)器學(xué)習(xí)帶你橫掃樂(lè)壇,你就是下一個(gè)方文山
我太愛(ài)北極猴子樂(lè)隊(duì)了,但他們已經(jīng)很久沒(méi)有發(fā)行新單曲了。久久欠缺精神食糧的我某天晚上突然靈機(jī)一動(dòng),我可以自給自足呀!于是我寫(xiě)了個(gè)簡(jiǎn)單的代碼,用Keras和TensorFlow訓(xùn)練了一個(gè)文本生成模型,寫(xiě)出一首全新的北極猴子的歌。
不過(guò)條件有限,這玩意兒無(wú)法跟真正的北極猴子的歌曲相提并論,但安慰一下長(zhǎng)期缺新歌的自己還是可以的。
本文將簡(jiǎn)單介紹這個(gè)代碼,完整的代碼放在筆者的GitHub上:https://github.com/Rajwrita/Sequence-Models-for-Literature/blob/master/NLP-AM2.0.ipynb。
首先,你得建立一個(gè)幾乎將全部北極猴子的歌曲包括在內(nèi)的數(shù)據(jù)集(https://github.com/Rajwrita/Sequence-Models-for-Literature/blob/master/AM.txt),之后如果繼續(xù)執(zhí)行此代碼,請(qǐng)嘗試使用自己的數(shù)據(jù)集生成文本。
導(dǎo)入
首先要為深度學(xué)習(xí)模型導(dǎo)入通用的數(shù)據(jù)幀操作庫(kù)以及TensorFlow和Keras庫(kù)包:
- import numpy as np
- from tensorflow.keras.preprocessing.sequence import pad_sequences
- from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout,Bidirectional
- from tensorflow.keras.preprocessing.text import Tokenizer
- from tensorflow.keras.models import Sequential
- from tensorflow.keras.optimizers import Adam
- from tensorflow.keras import regularizers
- import tensorflow.keras.utils as ku
接著,導(dǎo)入數(shù)據(jù):
- data = open('AM.txt').read()
再給文本裝上一個(gè)分詞器(tokenizer)。分詞器可以生成覆蓋整個(gè)語(yǔ)料庫(kù)的單詞詞典,實(shí)質(zhì)上就是鍵值對(duì)。鍵是單詞,而值則是為該單詞生成的標(biāo)記。簡(jiǎn)言之,分詞器將成句的字符串分解為獨(dú)立的單詞,然后賦予每個(gè)單詞一個(gè)唯一整數(shù)值。這一步很關(guān)鍵,為后續(xù)嵌入層數(shù)據(jù)的準(zhǔn)備打下基礎(chǔ)。
獲取單詞索引的長(zhǎng)度,可以得出語(yǔ)料庫(kù)里單詞的總量。在此基礎(chǔ)上加1,就可以引入外部詞匯。相應(yīng)的代碼如下:
- tokenizer = Tokenizer()data = open('AM.txt').read()
- tokenizer.fit_on_texts(corpus)
- total_words = len(tokenizer.word_index) + 1
再然后,使用token列表創(chuàng)建導(dǎo)入序列。導(dǎo)入序列說(shuō)白了就是一個(gè)python列表,文本語(yǔ)料庫(kù)的每一行都通過(guò)分詞器生成一個(gè)token列表。是像這樣的一行文本:

通過(guò)這道程序,就會(huì)將其轉(zhuǎn)化為代表這些單詞的一串token。數(shù)據(jù)集的每一行都會(huì)這樣處理。其代碼如下:
- input_sequences = []
- for line in corpus:
- token_list =tokenizer.texts_to_sequences([line])[0]
- for i in range(1, len(token_list)):
- n_gram_sequence = token_list[:i+1]
- input_sequences.append(n_gram_sequence)
可以看出,導(dǎo)入序列只是被分解為短語(yǔ)的句子,緊接著要獲取語(yǔ)料庫(kù)中最長(zhǎng)句子的長(zhǎng)度。這一步很簡(jiǎn)單,只需要將所有句子遍歷循環(huán)并找出最長(zhǎng)那句即可。
- max_sequence_len = max([len(x) for x in input_sequences])
現(xiàn)在填充所有序列使它們?nèi)恳粯娱L(zhǎng)。用零預(yù)填充序列,這樣更容易提取到標(biāo)簽值,只要抓取最后一個(gè)標(biāo)記就可以得到標(biāo)簽值了。
- input_sequences = np.array(pad_sequences(input_sequences,maxlen=max_sequence_len, padding='pre'))
填充之后,創(chuàng)建預(yù)測(cè)值和標(biāo)簽值,這樣序列基本上就被分解為x數(shù)組和y數(shù)組了。這里用到的是python的切片屬性。代碼如下:
- predictors, label = input_sequences[:,:-1],input_sequences[:,-1]
現(xiàn)在,數(shù)據(jù)已經(jīng)分為x數(shù)組和y數(shù)組,可以開(kāi)始創(chuàng)建神經(jīng)網(wǎng)絡(luò),對(duì)給定詞組進(jìn)行分類(lèi)預(yù)測(cè)了。
從嵌入層開(kāi)始
嵌入層是任何一種理解單詞的深度學(xué)習(xí)模型不可或缺的一層,其實(shí)際作用是通過(guò)賦予同樣含義詞匯以同樣數(shù)值,將較高維空間的向量投影到較低維空間,這樣就可以直接對(duì)向量進(jìn)行數(shù)學(xué)運(yùn)算了。
在一行文本中,它處理了所有詞匯并且賦予其在神經(jīng)網(wǎng)絡(luò)中的含義。第一個(gè)參數(shù)處理的是單詞,第二個(gè)參數(shù)則是繪制單詞矢量的維數(shù),最后一個(gè)參數(shù)是輸入維度的尺寸,其實(shí)就是最長(zhǎng)序列的長(zhǎng)度減1。減掉1是因?yàn)榍懊鏋榱说玫綐?biāo)簽值,我們將每個(gè)序列的最后一個(gè)詞砍掉了,所以得到的序列比最大序列長(zhǎng)度要小1。
- model.add(Embedding(total_words, 100, input_length=max_sequence_len-1))
添加LSTM(長(zhǎng)短期記憶網(wǎng)絡(luò))層
圖源:unsplash
LSTM層的細(xì)胞狀態(tài)保存了整個(gè)上下文語(yǔ)境,從而保證了對(duì)下一個(gè)詞匯產(chǎn)生影響的不止有相鄰詞匯。
除了單層的LSTM層,還可以使用堆疊多層的LSTM。使用雙向LSTM層,我們可以從頭到尾再?gòu)奈驳筋^將原始數(shù)據(jù)導(dǎo)入學(xué)習(xí)算法中,這有助于神經(jīng)網(wǎng)絡(luò)更好地理解文本。雙向LSTM還可以幫助神經(jīng)網(wǎng)絡(luò)更快收斂。
將返還序列標(biāo)注設(shè)為T(mén)rue,這樣就能將序列信息傳遞到第二層LSTM層,而不是直接到最終狀態(tài)。
- model.add(Bidirectional(LSTM(150, return_sequences = True)))
接下來(lái),運(yùn)用密集層進(jìn)一步捕獲線(xiàn)性關(guān)系,以上各層的輸出將轉(zhuǎn)化為單詞概率。softmax激活函數(shù)會(huì)將所有輸入單詞概率從(-∞,∞ ) 轉(zhuǎn)化為(0,1)。
- model.add(Dense(total_words/2, activation='relu',
- kernel_regularizer=regularizers.l2(0.01)))model.add(Dense(total_words
- ,activation='softmax'))
由于這里做的是categorical分類(lèi),所以要將定律設(shè)為分類(lèi)交叉熵。至于優(yōu)化器,這里選用adam優(yōu)化器。
最后一步——Epochs
最后要花一點(diǎn)時(shí)間訓(xùn)練模型,數(shù)據(jù)集的數(shù)據(jù)不多,大概要訓(xùn)練模型500個(gè)epoch左右。
- history = model.fit(predictors, label, epochs=100, verbose=1)
要預(yù)測(cè)的單詞越多,產(chǎn)生的亂碼也會(huì)越多,因?yàn)槊恳粋€(gè)單詞都要預(yù)測(cè),其下一個(gè)和下下個(gè)單詞也是,那么下一個(gè)單詞永遠(yuǎn)比上一個(gè)有更多不確定性。來(lái)看看網(wǎng)絡(luò)最后預(yù)測(cè)出來(lái)的文本!
- seed_text = "I really like the Arctic Monkeys and

建立覆蓋足夠單詞的語(yǔ)料庫(kù),神經(jīng)網(wǎng)絡(luò)就可以在語(yǔ)料庫(kù)上訓(xùn)練,并通過(guò)預(yù)測(cè)下一個(gè)單詞,幫助我們預(yù)測(cè)一些復(fù)雜文本。
有了機(jī)器學(xué)習(xí),產(chǎn)糧不再是難事,試試用這個(gè)代碼為你的心水歌手寫(xiě)一首歌吧!