機器學習帶你橫掃樂壇,你就是下一個方文山
我太愛北極猴子樂隊了,但他們已經很久沒有發行新單曲了。久久欠缺精神食糧的我某天晚上突然靈機一動,我可以自給自足呀!于是我寫了個簡單的代碼,用Keras和TensorFlow訓練了一個文本生成模型,寫出一首全新的北極猴子的歌。
不過條件有限,這玩意兒無法跟真正的北極猴子的歌曲相提并論,但安慰一下長期缺新歌的自己還是可以的。
本文將簡單介紹這個代碼,完整的代碼放在筆者的GitHub上:https://github.com/Rajwrita/Sequence-Models-for-Literature/blob/master/NLP-AM2.0.ipynb。
首先,你得建立一個幾乎將全部北極猴子的歌曲包括在內的數據集(https://github.com/Rajwrita/Sequence-Models-for-Literature/blob/master/AM.txt),之后如果繼續執行此代碼,請嘗試使用自己的數據集生成文本。
導入
首先要為深度學習模型導入通用的數據幀操作庫以及TensorFlow和Keras庫包:
- 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
接著,導入數據:
- data = open('AM.txt').read()
再給文本裝上一個分詞器(tokenizer)。分詞器可以生成覆蓋整個語料庫的單詞詞典,實質上就是鍵值對。鍵是單詞,而值則是為該單詞生成的標記。簡言之,分詞器將成句的字符串分解為獨立的單詞,然后賦予每個單詞一個唯一整數值。這一步很關鍵,為后續嵌入層數據的準備打下基礎。
獲取單詞索引的長度,可以得出語料庫里單詞的總量。在此基礎上加1,就可以引入外部詞匯。相應的代碼如下:
- tokenizer = Tokenizer()data = open('AM.txt').read()
- tokenizer.fit_on_texts(corpus)
- total_words = len(tokenizer.word_index) + 1
再然后,使用token列表創建導入序列。導入序列說白了就是一個python列表,文本語料庫的每一行都通過分詞器生成一個token列表。是像這樣的一行文本:

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

建立覆蓋足夠單詞的語料庫,神經網絡就可以在語料庫上訓練,并通過預測下一個單詞,幫助我們預測一些復雜文本。
有了機器學習,產糧不再是難事,試試用這個代碼為你的心水歌手寫一首歌吧!