使用Python可視化Word2vec的結果

做者|Mate Pocs
編譯|VK
來源|Towards Data Sciencepython

Word2vec絕對是我在天然語言處理研究中遇到的最有趣的概念。想象一下,有一種算法能夠成功地模擬理解單詞的含義及其在語言中的功能,它能夠在不一樣的主題內來衡量單詞之間的接近程度。git

我認爲可視化地表示word2vec向量會頗有趣:本質上,咱們能夠獲取國家或城市的向量,應用主成分分析來減小維度,並將它們放在二維圖表上。而後,咱們能夠觀察可視化的結果。github

在本文中,咱們將:算法

  • 從廣義上討論word2vec理論;api

  • 下載原始的預訓練向量;網絡

  • 看看一些有趣的應用程序:好比對一些單詞進行算術運算,好比著名的king-man+woman=queen等式app

  • 根據word2vec向量看看咱們能多精確地來繪製歐洲的首都。機器學習

word2vec的原始研究論文和預訓練模型來自2013年,考慮到NLP文獻的擴展速度,目前它是老技術。較新的方法包括GloVe(更快,能夠在較小的語料庫上訓練)和fastText(可以處理字符級的n-gram)。函數

Quick Word2Vec簡介

天然語言處理的核心概念之一是如何量化單詞和表達式,以便可以在模型環境中使用它們。語言元素到數值表示的這種映射稱爲詞嵌入。學習

Word2vec是一個詞嵌入過程。這個概念相對簡單:經過一個句子一個句子地在語料庫中循環去擬合一個模型,根據預先定義的窗口中的相鄰單詞預測當前單詞。

爲此,它使用了一個神經網絡,但實際上最後咱們並不使用預測的結果。一旦模型被保存,咱們只保存隱藏層的權重。在咱們將要使用的原始模型中,有300個權重,所以每一個單詞都由一個300維向量表示。

請注意,兩個單詞沒必要彼此接近的地方纔被認爲是類似的。若是兩個詞歷來沒有出如今同一個句子中,但它們一般被相同的包圍,那麼能夠確定它們有類似的意思。

word2vec中有兩種建模方法:skip-gram和continuous bag of words,這兩種方法都有各自的優勢和對某些超參數的敏感性……可是你知道嗎?咱們將不擬合咱們本身的模型,因此我不會花時間在它上面。

固然,你獲得的詞向量取決於你訓練模型的語料庫。通常來講,你確實須要一個龐大的語料庫,有維基百科上訓練過的版本,或者來自不一樣來源的新聞文章。咱們將要使用的結果是在Google新聞上訓練出來的。

如何下載和安裝

首先,你須要下載預訓練word2vec向量。你能夠從各類各樣的模型中進行選擇,這些模型是針對不一樣類型的文檔進行訓練的。

我用的是最初的模型,在Google新聞上受過訓練,你能夠從不少來源下載,只需搜索「Google News vectors negative 300」。或者, 在這裏下載:https://github.com/mmihaltz/word2vec-GoogleNews-vectors。

注意,這個文件是1.66gb,但它包含了30億字的300維表示。

當談到在Python中使用word2vec時,再一次,你有不少包可供選擇,咱們將使用gensim庫。假設文件保存在word2vec_pretrained文件夾中,能夠用Python加載,代碼以下所示:

from gensim.models.keyedvectors import KeyedVectors

word_vectors = KeyedVectors.load_word2vec_format(\
    './word2vec_pretrained/GoogleNews-vectors-negative300.bin.gz', \
    binary = True, limit = 1000000)

limit參數定義了要導入的單詞數,100萬對於我來講已經足夠了。

探索Word2vec

如今咱們已經有了word2vec向量,咱們能夠查看它的一些相關有趣的用法。

首先,你能夠實際檢查任何單詞的向量表示:

word_vectors['dog']

結果,正如咱們預期的,是一個300維的向量,而且這個向量很難解釋。咱們經過對這些向量的加和減來計算新向量,而後計算餘弦類似度來找到最接近的匹配詞。

你可使用most_similar函數找到同義詞,topn參數定義要列出的單詞數:

word_vectors.most_similar(positive = ['nice'], topn = 5)

結果

[('good', 0.6836092472076416),
 ('lovely', 0.6676311492919922),
 ('neat', 0.6616737246513367),
 ('fantastic', 0.6569241285324097),
 ('wonderful', 0.6561347246170044)]

如今,你可能認爲用相似的方法,你也能夠找到反義詞,你可能認爲只須要把「nice」這個詞做爲negative輸入。但結果倒是

[('J.Gordon_###-###', 0.38660115003585815),
 ('M.Kenseth_###-###', 0.35581791400909424),
 ('D.Earnhardt_Jr._###-###', 0.34227001667022705),
 ('G.Biffle_###-###', 0.3420777916908264),
 ('HuMax_TAC_TM', 0.3141660690307617)]

這些詞實際上表示離「nice」這個詞最遠的詞。

使用doesnt_match函數能夠找出異常詞:

word_vectors.doesnt_match(
['Hitler', 'Churchill', 'Stalin', 'Beethoven'])

返回Beethoven。我想這很方便。

最後,讓咱們看看一些操做的例子,這些操做經過賦予算法一種虛假的智能感而出名。若是咱們想合併father和woman這兩個詞的向量,而且減去man這個詞的向量,代碼以下

word_vectors.most_similar(
positive = ['father', 'woman'], negative = ['man'], topn = 1)

咱們獲得:

[('mother', 0.8462507128715515)]

腦子先轉一轉,想象一下咱們只有兩個維度:親子關係和性別。「女人」這個詞能夠用這個向量來表示:[0,1],「男人」是[0,-1],「父親」是[1,-1],「母親」是[1,1]。如今,若是咱們作一樣的運算,咱們獲得一樣的結果。固然,區別在於咱們有300個維度,而不是示例中僅有的2個維度,維度的含義幾乎沒法解釋。

在word2vec操做中,有一個著名的性別偏見例子,「doctor」這個詞的女性版本過去被計算爲「nurse」。我試着複製,但沒有獲得一樣的結果:

word_vectors.most_similar(
positive = ['doctor', 'woman'], negative = ['man'], topn = 1)

[('gynecologist', 0.7093892097473145)]

咱們獲得了婦科醫生,因此,我想這多是進步吧?

好吧,如今咱們已經檢查了一些基本的函數,讓咱們來研究咱們的可視化吧!

Map函數

首先,咱們須要一個Map函數。假設咱們有一個要可視化的字符串列表和一個詞嵌入,咱們但願:

  1. 找到列表中每一個單詞的詞向量表示;

  2. 利用主成分分析法將維數降到2;

  3. 建立散點圖,將單詞做爲每一個數據點的標籤;

  4. 另一個額外的好處是,能夠從任何維度「旋轉」結果——主成分分析的向量是任意方向的,當咱們繪製地理單詞時,咱們可能想要改變這個方向,看是否能夠與現實世界的方向一致。

咱們須要如下庫:

import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.decomposition import PCA

import adjustText

列表中不經常使用的一個庫是adjustText,這是一個很是方便的包,它使得在散點圖中編寫圖例變得簡單,而不會重疊。對於我來講,找到這個解決方案很是困難,並且據我所知,在matplotlib或seaborn中沒有辦法作到這一點。

無需進一步說明,此函數將徹底知足咱們的須要:

def plot_2d_representation_of_words(
    word_list, 
    word_vectors, 
    flip_x_axis = False,
    flip_y_axis = False,
    label_x_axis = "x",
    label_y_axis = "y", 
    label_label = "city"):
    
    pca = PCA(n_components = 2)
    
    word_plus_coordinates=[]
    
    for word in word_list: 
    
        current_row = []
        current_row.append(word)
        current_row.extend(word_vectors[word])
    word_plus_coordinates.append(current_row)
    
    word_plus_coordinates = pd.DataFrame(word_plus_coordinates)
        
    coordinates_2d = pca.fit_transform(
        word_plus_coordinates.iloc[:,1:300])
    coordinates_2d = pd.DataFrame(
        coordinates_2d, columns=[label_x_axis, label_y_axis])
    coordinates_2d[label_label] = word_plus_coordinates.iloc[:,0]
    if flip_x_axis:
        coordinates_2d[label_x_axis] = \
        coordinates_2d[label_x_axis] * (-1)
    if flip_y_axis:
        coordinates_2d[label_y_axis] = \
        coordinates_2d[label_y_axis] * (-1)
            
    plt.figure(figsize = (15,10))
    p1=sns.scatterplot(
        data=coordinates_2d, x=label_x_axis, y=label_y_axis)
    
    x = coordinates_2d[label_x_axis]
    y = coordinates_2d[label_y_axis]
    label = coordinates_2d[label_label]
    
    texts = [plt.text(x[i], y[i], label[i]) for i in range(len(x))]
    adjustText.adjust_text(texts)

如今是測試函數的時候了。我畫出了歐洲國家的首都。你可使用任何列表,例如總統或其餘歷史人物的名字,汽車品牌,烹飪原料,搖滾樂隊等等,只要在word_list參數中傳遞它。頗有意思的是看到一堆堆的東西在兩個軸後面造成一個意思。

若是你想重現結果,如下是城市:

capitals = [
    'Amsterdam', 'Athens', 'Belgrade', 'Berlin', 'Bern', 
    'Bratislava', 'Brussels', 'Bucharest', 'Budapest', 
    'Chisinau', 'Copenhagen','Dublin', 'Helsinki', 'Kiev',
    'Lisbon', 'Ljubljana', 'London', 'Luxembourg','Madrid',
    'Minsk', 'Monaco', 'Moscow', 'Nicosia', 'Nuuk', 'Oslo', 
    'Paris','Podgorica', 'Prague', 'Reykjavik', 'Riga', 
    'Rome', 'San_Marino', 'Sarajevo','Skopje', 'Sofia', 
    'Stockholm', 'Tallinn', 'Tirana', 'Vaduz', 'Valletta',
    'Vatican', 'Vienna', 'Vilnius', 'Warsaw', 'Zagreb']

假設你仍然有咱們在上一節中建立的word_vectors對象,能夠這樣調用函數:

plot_2d_representation_of_words(
    word_list = capitals, 
    word_vectors = word_vectors, 
    flip_y_axis = True)

(翻轉y軸是爲了建立更像真實貼圖的表示。)

結果是:

我不知道你的感覺,當我第一次看到地圖的時候,我真不敢相信結果會有多好!是的,固然,你看得越久,你發現的「錯誤」就越多,一個很差的結果就是莫斯科離東方的距離並不像它應該的那麼遠……儘管如此,東西方几乎徹底分離,斯堪的納維亞和波羅的海國家被很好地組合在一塊兒,意大利周圍的首都也是如此。

須要強調的是,這毫不是純粹的地理位置,例如,雅典離西方很遠,但這是有緣由的。讓咱們回顧一下上面的地圖是如何導出的,這樣咱們就能夠充分理解它了:

  • 谷歌的一組研究人員訓練了一個龐大的神經網絡,根據上下文預測單詞;

  • 他們將每一個單詞的權重保存在一個300維的向量表示中;

  • 咱們計算歐洲各國首都的向量;

  • 利用主成分分析法將維數降到2;

  • 把計算出的成分放在圖表上。

因此,語義的信息不能表明真實地理信息。但我以爲這個嘗試頗有趣。


參考引用

原文連接:https://towardsdatascience.com/how-to-draw-a-map-using-python-and-word2vec-e9627b4eae34

歡迎關注磐創AI博客站:
http://panchuang.net/

sklearn機器學習中文官方文檔:
http://sklearn123.com/

歡迎關注磐創博客資源彙總站:
http://docs.panchuang.net/

相關文章
相關標籤/搜索