用深度學習來獲取文字語義:詞向量應用於自然語言處理

OReillyData發表於2016-12-05

詞向量是一種把詞處理成向量的技術,並且保證向量間的相對相似度和語義相似度是相關的。這個技術是在無監督學習方面最成功的應用之一。傳統上,自然語言處理(NLP)系統把詞編碼成字串。這種方式是隨意確定的,且對於獲取詞之間可能存在的關係並沒有提供有用的資訊。詞向量是NLP領域的一個替代方案。它把詞或短語對映成實數向量,把特徵從詞彙表大小的高維度空間降低到一個相對低的維度空間。

例如,讓我們看看四個詞:“woman”(女人)、“man”(男人)、“queen”(女王)和“king”(國王)。我們把它們都向量化,再使用簡單的代數運算來發現它們之間的語義相似度。計算向量間的相似度可以採用諸如餘弦相似度的方法。當我們把詞“woman”的向量減去詞“man”後,這個差值的餘弦相似度應該和詞“queen”的向量減去“king”的向量的差值比較接近(參見圖1)。

W(“woman”)−W(“man”) ≃ W(“queen”)−W(“king”)

640?wx_fmt=jpeg

圖1 性別的向量。來源:Lior Shkiller

有很多不同的模型可以被用來把詞轉換成實數性的向量,包括隱含語義分析(LSA)隱含狄利克雷分佈(LDA)。這些模型背後的思路是:相關的詞彙一般都會在相同的文件裡同時出現。例如,backpack(揹包)、school(學校)、notebook(筆記本)和teacher(教師)一般都會一起出現。而school(學校)、tiger(老虎)、apple(蘋果)和basketball(籃球)一般都不會持續同時出現。基於這個相關的詞會在相關的文件裡出現的基本假設,為了把詞轉化為向量,LSA會構建一個矩陣。矩陣的行是(語料庫或資料裡)所有出現過的詞,而列則是對應於文件裡的一個段落。LSA使用奇異值分解(SVD)的方法,在儲存列之間相似性的同時降低矩陣的行數。不過這些模型的主要問題是:在資料量非常大的時候,計算量也非常得大。

為了避免計算和儲存大量的資料,我們試圖創造一個神經網路模型來計算詞間的關係,並提高效率。

Word2Vec

目前最流行的詞向量模型是由Mikolov等人在2013年提出的word2vec。這個模型的效果很好,且計算效率有了很大的提升。Mikolov等提出的負取樣方法是一個更有效的產生詞向量的方法。更多的資訊可以在這裡找到。

這一模型可以使用下述兩種架構的任一種來生成詞的分佈:連續詞袋(CBOW)連續跳躍元語法(skip-gram)

下面讓我們分別來看看這兩種架構。

CBOW模型

在CBOW架構裡,模型根據目標詞的上下文來預測目標詞。因此,Mikolov等使用了目標詞w的前n個詞和後n個詞。

一個序列的詞等同於一個物品集。因此,就可以把“詞”理解為“物品”。對於“物品”我們可以使用推薦系統以及協同過濾裡的方法。CBOW模型的訓練速度是跳躍元語法模型的七倍,而且預測準確性也稍好(參見圖2)。

640?wx_fmt=jpeg

圖2 基於上下文來預測詞。來源:Lior Shkiller

連續跳躍元語法模型

與使用目標詞的上下文的方法不同,連續跳躍元語法模型是使用目標詞去預測它的前後詞(參見圖3)。據Mikolov等的論文,在訓練資料量比較小的時候,跳躍元語法模型比較好,且對於罕見的詞和短語的處理較好。

640?wx_fmt=jpeg

圖3 用給定的詞來預測上下文。來源:Lior Shkiller

程式碼

(你可以在這個GitHub庫裡找到下面例子的程式碼)

這個模型(word2vec)的一大好處就是,它可以用於很多種語言。

我們所要做的就是下載一個所要處理的語言的大資料集。

從維基百科上找一個大資料集

我們可以從維基百科裡面找到很多語言的資料。用下面的步驟就可以獲得一個大資料集。

  • 找到你想處理的語言的ISO 639程式碼:ISO 639程式碼的列表

  • 登入https://dumps.wikimedia.org/wiki/latest/ (譯者注:此連結已失效)

  • 下載wiki-latest-pages-articles.xml.bz2

接著,為了讓後續的事情變簡單,我們會安裝gensim。它是一個實現了word2vec的Python庫。

pip install –upgrade gensim

我們需要用維基百科的下載檔案來建立語料庫,以用於後續的word2vec模型的訓練。下面這段程式碼的輸出就是一個“wiki..text”的檔案。其中包括了維基百科的所有文章的所有詞彙,並按照語言分開。

from gensim.corpora import WikiCorpus

language_code = “he”

inp = language_code+”wiki-latest-pages-articles.xml.bz2″

outp = “wiki.{}.text”.format(language_code)

i = 0

print(“Starting to create wiki corpus”)

output = open(outp, ‘w’)

space = ” ”

wiki = WikiCorpus(inp, lemmatize=False, dictionary={})

for text in wiki.get_texts():

article = space.join([t.decode(“utf-8”) for t in text])

output.write(article + “\n”)

i = i + 1

if (i % 1000 == 0):

print(“Saved ” + str(i) + ” articles”)

output.close()

print(“Finished – Saved ” + str(i) + ” articles”)

訓練模型

引數的說明如下:

  • size:向量的維度

    • 大的size值會要求更多的訓練資料,但能帶來更準確的模型

  • window:在一個句子內,目標詞與預測詞之間的最大距離

  • min_count:忽略所有總詞頻低於這個值的詞。

import multiprocessing

from gensim.models import Word2Vec

from gensim.models.word2vec import LineSentence

language_code = “he”

inp = “wiki.{}.text”.format(language_code)

out_model = “wiki.{}.word2vec.model”.format(language_code)

size = 100

window = 5

min_count = 5

start = time.time()

model = Word2Vec(LineSentence(inp), sg = 0, # 0=CBOW , 1= SkipGram

size=size, window=window, min_count=min_count, workers=multiprocessing.cpu_count())

# trim unneeded model memory = use (much) less RAM

model.init_sims(replace=True)

print(time.time()-start)

model.save(out_model)

整個word2vec訓練過程用了18分鐘。

fastText

臉書的人工智慧研究(FAIR)實驗室最近釋出了fastText庫。它是基於Bojanowski等的論文《Enriching Word Vectors with Subword Information》所開發的模型。與word2vec不同,fastText把詞表示成一個n元的字母袋。每個向量代表字元袋裡的一個n元字母,而一個詞則是這些向量的和。

使用臉書的新庫很簡單。安裝命令:

pip install fasttext

訓練模型的命令:

start = time.time()

language_code = “he”

inp = “wiki.{}.text”.format(language_code)

output = “wiki.{}.fasttext.model”.format(language_code)

model = fasttext.cbow(inp,output)

print(time.time()-start)

整個fastText模型訓練用了13分鐘。

評估向量:類比性

下面讓我們用之前的那個例子來評估這兩個模型的準確度。

W(“woman”) ≃ W(“man”)+ W(“queen”)− W(“king”)

下面的程式碼首先計算正負詞的加權平均值。

隨後,程式碼計算了所有的測試詞彙的向量與加權平均的點乘積。

我們的評估例子裡,測試詞彙是整個詞彙表。程式碼的最後是列印出和正詞與負詞的加權平均值的餘弦相似度最高的詞。

import numpy as np

from gensim.matutils import unitvec

def test(model,positive,negative,test_words):

mean = []

for pos_word in positive:

mean.append(1.0 * np.array(model[pos_word]))

for neg_word in negative:

mean.append(-1.0 * np.array(model[neg_word]))

# compute the weighted average of all words

mean = unitvec(np.array(mean).mean(axis=0))

scores = {}

for word in test_words:

if word not in positive + negative:

test_word = unitvec(np.array(model[word]))

# Cosine Similarity

scores[word] = np.dot(test_word, mean)

print(sorted(scores, key=scores.get, reverse=True)[:1])

接著,用我們最初的那個例子來做測試。

用fastText和gensim的word2vec模型來預測:

positive_words = [“queen”,”man”]

negative_words = [“king”]

# Test Word2vec

print(“Testing Word2vec”)

model = word2vec.getModel()

test(model,positive_words,negative_words,model.vocab)

# Test Fasttext

print(“Testing Fasttext”)

model = fasttxt.getModel()

test(model,positive_words,negative_words,model.words)

結果

Testing Word2vec

[‘woman’]

Testing Fasttext

[‘woman’]

結果顯示fastText和gensim的word2vec都能正確預測。

W(“woman”) ≃ W(“man”)+ W(“queen”)− W(“king”)

可見,詞向量確實能找到詞彙之間的語義關係。

我們這裡所介紹的模型的基本思路可以被運用到很多的應用場景。如預測商業機構需要的下一個應用做情感分析替換生物序列、做語義圖片搜尋等。

640?wx_fmt=jpeg

Lior Shkiller

Lior Shkiller是Deep Solution的聯合創始人。作為一個機器學習的實踐者,他積極熱忱地投身於人工智慧和認知科學。Lior擁有以色列特拉維夫大學的電腦科學與心理學學位,並有超過10年的軟體開發經驗。Deep Solutions提供端到端的軟體解決方案,其中包括為計算機視覺、自然語言處理、異常檢測和推薦系統等應用所開發的創新的深度學習的新演算法。

640?wx_fmt=png

相關文章