Chars2vec:基於字元實現的可用於處理現實世界中包含拼寫錯誤和俚語的語言模型
這篇論文介紹了我們開源的基於字元的語言模型 chars2vec。這個模型使用 Keras 庫(TensorFlow 後端)開發,現在已經可以在 Python 2.7 和 3.0+ 中使用。
引言
建立並使用詞嵌入是完成大多數 NLP 任務的主流方法。每個詞都對應著一個數值向量,當文字中出現這個詞的時候就會用上它的數值向量。有些簡單的模型會使用 one-hot 詞嵌入,也可能使用隨機向量或整數來對詞進行初始化。這類模型的缺點很明顯——這種將對詞進行向量化的方式不能表示詞之間任何的語義聯絡。
還有另外一種稱為語義化的語言模型,它們根據仿射嵌入向量對有詞義關聯的詞進行排序。事實上,這些模型表示了不同單詞的上下文鄰近性:這些模型使用諸如百科全書、新聞、文學作品之類的擁有大量文字的語料庫進行訓練。這樣訓練使得出現在相似上下文中的詞都得以用臨近的向量表示。經典的語義化語言模型包括 Word2Vec 和 GloVe。更加前沿的語義化語言模型(ULMFiT,ELMo)基於迴圈神經網路(RNNs)和其他神經網路體系結構。
語義化的模型包含從大量語料上學得的詞義相關的資訊,但是它們需要與固定的詞彙搭配(通常會遺漏一些生僻詞)。這對於 NLP 問題來說是個嚴重的缺陷。如果語義化語言模型的詞彙表裡缺少一段文字中很多的單詞,那麼它在處理某些 NLP 任務時效率就不會高——這個模型將不能夠解釋那些缺少的單詞。這種情況可能出現在處理人類編寫的文字(例如回覆、評論、申請書、文件或者網上的帖子)的時候。這些文字可能包含一些俚語、特殊領域的生僻詞或者是詞彙表中沒有的人名,所以語義化的模型中也不會出現這些詞。排印錯誤也會創造出一些不存在於任何詞嵌入中的“新”詞。
比如使用 MovieLens 的資料搭建電影推薦系統時,在處理使用者評論的過程中這個問題就很明顯。使用者的評論中經常會出現 “like Tarantino” 這個短語;他們有時會把導演的姓 “Tarantino” 弄錯,從而創造出 “Taratino”、“Torantino”、“Tarrantino” 等“新”詞。如果能從使用者的評論中提取出 “Tarantino” 這個特徵的話,就能夠顯著改善電影的相關性度量和推薦質量,提取出具體姓氏或者拼寫錯誤等詞彙表中沒有的詞所表示的特徵也能達到同樣的效果。
為了解決這個問題,前文提到過,我們需要使用一個能夠建立出詞嵌入的語言模型,建立過程完全根據拼寫,並且將向量根據其表示的詞之間的相似性排序。
關於 chars2vec 模型
我們根據單詞的符號嵌入開發了 chars2vec 這個語言模型。**這個模型將一段任意長度的符號序列用一個固定長度的向量表示出來,單詞之間拼寫的相似性則通過向量間的距離度量表示。**這個模型不基於某個詞典(它沒有儲存單詞與對應向量表示所組成的固定詞典),因此它在建立和使用的過程中並不需要大量的計算資源。使用 pip 就可以安裝 Chars2vec 庫:
>>> pip install chars2vec
複製程式碼
下面的程式碼建立了 chars2vec 詞嵌入(50 維) 並使用 PCA 將這些詞嵌入向量對映到了一個平面上,最後我們會得到一張可以描述這個模型的幾何意義圖片:
import chars2vec
import sklearn.decomposition
import matplotlib.pyplot as plt
# 載入 Inutition Engineering 預訓練的模型
# 模型的名字:'eng_50', 'eng_100', 'eng_150' 'eng_200', 'eng_300'
c2v_model = chars2vec.load_model('eng_50')
words = ['Natural', 'Language', 'Understanding',
'Naturael', 'Longuge', 'Updderctundjing',
'Motural', 'Lamnguoge', 'Understaating',
'Naturrow', 'Laguage', 'Unddertandink',
'Nattural', 'Languagge', 'Umderstoneding']
# 建立詞嵌入
word_embeddings = c2v_model.vectorize_words(words)
# 使用 PCA 將詞嵌入對映到平面上
projection_2d = sklearn.decomposition.PCA(n_components=2).fit_transform(word_embeddings)
# 在平面上寫字
f = plt.figure(figsize=(8, 6))
for j in range(len(projection_2d)):
plt.scatter(projection_2d[j, 0], projection_2d[j, 1],
marker=('$' + words[j] + '$'),
s=500 * len(words[j]), label=j,
facecolors='green' if words[j]
in ['Natural', 'Language', 'Understanding'] else 'black')
plt.show()
複製程式碼
執行這段程式碼將會生成下面這張圖片:
我們可以看到,儘管這個模型基於一個接受單詞的符號序列的迴圈神經網路,而不是去分析一個單詞中某些字母或者某種模式的出現情況,拼寫相似的詞的表示向量仍是臨近的。單詞拼寫中出現的增添、刪減或者替換越多,它的詞嵌入就會離原始詞越遠。
基於字元模型的應用
在字元層面上分析文字的想法並不新鮮——已經有一些模型對符號建立詞嵌入,然後通過平均過程建立符號詞嵌入。平均過程是這種方法的瓶頸——這種模型一定程度上解決了上面提到的問題,但不幸的是,它們還不夠完美。如果想要編碼一些除了事實之外的關於符號相對位置和符號模式的資訊,我們還需要進行更多的訓練,這樣才能從符號嵌入向量中找出每個詞嵌入的正確形式。
karpathy/char-rnn 是最早在字元級處理文字的 NLP 模型之一。它通過輸入文字訓練了一個迴圈神經網路(RNN),給定一段字元序列就能夠預測下一個符號。Character-Level Deep Learning in Sentiment Analysis 也是基於 RNN 的字元級語言模型。有時,我們會用卷積神經網路(CNNs)處理字元序列,請查閱 Character-Aware Neural Language Models;Character-level Convolutional Networks for Text Classification 這篇論文也是一個使用 CNN 進行文字分類的例子。
Facebook 的 fastText 庫中實現的模型是基於字元的語言模型的典範。fastText 模型會建立符號詞嵌入,然後基於符號表示來解決文字分類的問題。這種技術基於對多種 n 元語法生成詞的分析,而不依賴於 RNN,避免了模型對於排印錯誤和拼寫錯誤過於敏感的問題,也就不會對 n 元語法生成詞的範圍造成太大的影響。不過這個模型提供了語言詞彙表中缺失的單詞對應的的詞嵌入。
模型結構
每個 chars2vec 模型都有一個固定的字元表,用於單詞的向量化:每當列表中的一個字元出現在文字中的時候,表示這個字元的 one-hot 向量就會被反饋給模型;向量化的過程中會忽略掉列表中沒有的字元。我們訓練的模型主要用來處理英文文字;這些模型使用的列表包括了常用的 ASCII 字元——所有的英文字母、數字和常用的標點符號:
['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.',
'/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<',
'=', '>', '?', '@', '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z']
複製程式碼
該模型對大小寫不敏感,所有的符號都統一轉換成小寫形式。
Chars2vec 通過基於 TensorFlow 的 Keras 庫實現。建立詞嵌入的神經網路結構如下:
一個任意長度的 one-hot 向量序列表示一個詞中的字元序列,經過兩個 LSTM 層處理之後,會輸出這個詞的嵌入向量。
為了訓練好模型,我們要使用一個包含 chars2vec 的擴充套件神經網路。更準確地說,我們的神經網路需要將兩個 one-hot 向量序列作為輸入,它們分別代表著不同的詞,其次這個神經網路會使用一個 chars2vec 建立出它們所對應的詞嵌入,然後計算出這些嵌入向量差值的範數,最後將這個結果反饋給網路最後一層的 sigmoid 啟用函式。這個神經網路的輸出範圍是 0 到 1。
這個擴充套件神經網路使用一對詞進行訓練,在訓練樣本中,一對“相似”的詞被標記為 0 值,而“不相似”的值則被標記為 1。事實上,我們所定義的“相似性”這個標準是將一個詞變形為另一個詞所需要替換、增加、刪減的字元的數量。這也造就了我們獲取資料的方式——我們對大量的詞進行多種修改,建立出一個新的詞集。通過修改原詞得到的詞集的子集本質上和原詞是相似的,這樣的單詞對會被標記為 0。從不同子集中選出的單詞顯然有更多不同點,所以會被標記為 1。
初始詞集的大小、子集中詞的數量、對詞進行變形最大次數決定了模型的訓練結果和模型向量化的穩定性。最優的引數應該根據給定的語言和任務進行選擇。另一個重點是保持整個訓練集的均衡(我們應該協調好這兩個方面)。
擴充套件神經網路的第二個部分只有一條邊,訓練過程中可以調整它的權重;這部分網路將單調函式應用到對嵌入向量差範數。訓練集限制了這第二個部分只能對“相似的”詞對輸出 0,對“不相似的”詞對輸出 1,所以在訓練這個擴充套件模型的時候,內層的 chars2vec 模型將學會為“相似的”詞對構建臨近的嵌入向量,為“不相似的”詞對構建遠離的嵌入向量。
我們在詞嵌入維度分別為 50、100、150、200 和 300 情況下的英語中訓練了 chars2vec 模型。倉庫中已經上傳了專案原始碼以及訓練和使用模型的例子(我們還在資料集上訓練了一個適用於另一種語言的模型)。
訓練你自己的 chars2vec 模型
下面的程式碼段展示瞭如何訓練一個自己的 chars2vec 模型例項。
import chars2vec
dim = 50
path_to_model = 'path/to/model/directory'
X_train = [('mecbanizing', 'mechanizing'), # 相似詞,目標為 0
('dicovery', 'dis7overy'), # 相似詞,目標為 0
('prot$oplasmatic', 'prtoplasmatic'), # 相似詞,目標為 0
('copulateng', 'lzateful'), # 非相似詞,目標為 1
('estry', 'evadin6'), # 非相似詞,目標為 1
('cirrfosis', 'afear') # 非相似詞,目標為 1
]
y_train = [0, 0, 0, 1, 1, 1]
model_chars = ['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.',
'/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<',
'=', '>', '?', '@', '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z']
# 建立 chars2vec 模型,並使用前面的訓練資料進行訓練
my_c2v_model = chars2vec.train_model(dim, X_train, y_train, model_chars)
# 儲存預訓練的模型
chars2vec.save_model(my_c2v_model, path_to_model)
words = ['list', 'of', 'words']
# 儲存預訓練的模型,建立詞嵌入
c2v_model = chars2vec.load_model(path_to_model)
word_embeddings = c2v_model.vectorize_words(words)
複製程式碼
模型在單詞向量化是要用到的字元列表 model_chars
、模型的維度 dim
和用來儲存模型的目錄的路徑 path_to_model
。訓練集 (X_train
, y_train
) 由“相似”詞對和“非相似”詞對組成。X_train
是一個詞對列表,y_train
是它們相似度分數(0 或 1)的列表。
有一點很重要,model_chars
列表中所有的字元都應該包含在訓練資料集的詞集中——如果某一個字元不在其中,或者出現的頻率很低,那麼每當測試資料集中出現這個字元都會引起不穩定的模型行為。由於這類字元相關的 one-hot 向量很少被傳入模型的輸入層,模型第一層的權重總是乘 0,也就是說這些權重引數一直都沒有被調整到。
chars2vec 模型的另一個優勢是解決任意缺少預訓練模型的語言相關的 NLP 難題的能力。相比於使用具體詞彙處理文字的經典模型,這個模型為一些文字分類和聚類問題提供了更好的辦法。
基準測試
我們在 IMDb 資料集上對多種詞嵌入進行了評論分類任務的基準測試。IMDb 是一個開放的影評資料集,一個評論可以是正面或負面的,所以這是文字二分類任務,我們使用了 5 萬條評論作為訓練集,另外 5 萬條用於測試。
除了 chars2vec 詞嵌入,我們還測試了一些有名的詞嵌入模型,例如 GloVe、Google 的 word2vec(“預訓練向量使用一部分 Google 新聞資料集(大概 1 億個詞)進行訓練。這個模型包含了 3 百萬個詞和短語所對應的 300 維向量”)、fastText (維基新聞模型包含 300 個維度,“在維基百科 2017、UMBC webbase 語料庫和 statmt.org 新聞資料集(160 億個標識)上訓練的 1 百萬個詞向量”)。
分類模型工作步驟如下:計算每個每個評論包含的單詞的嵌入向量的平均值,以此將其向量化。如果模型詞典中沒有某個詞,就會使用零向量表示這個詞。我們使用了 NLTK 庫提供的一個包括停止詞的標準標識化過程,使用 linearSVM 作為分類器。下面的表格展示了我們對大多數流行的模型進行這樣的基準測試得到的正確率。需要指出的是,我們的 chars2vec 模型比依賴大規模詞彙表簡化了 300 倍,並且仍然能夠得到相當不錯的結果。
詞嵌入 | 正確率 | 模型大小 |
---|---|---|
GLoVe 50 | 0.7536 | 171.5 MB |
GLoVe 300 | 0.83336 | 1.04 GB |
word2vec GoogleNews-vectors-negative300 | 0.85604 | 3.64 GB |
fastText wiki-news-300d-1M | 0.85568 | 2.26 GB |
chars2vec 50 | 0.63036 | 188 KB |
chars2vec 100 | 0.6788 | 598 KB |
chars2vec 150 | 0.69592 | 1.2 MB |
chars2vec 200 | 0.70188 | 2.1 MB |
chars2vec 300 | 0.74012 | 4.6 MB |
我們發現,與語義模型相比,chars2vec 模型還有一些進步空間。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。