1. 程式人生 > >基於騰訊AI Lab詞向量進行未知詞、短語向量補齊與域內相似詞搜尋

基於騰訊AI Lab詞向量進行未知詞、短語向量補齊與域內相似詞搜尋

在這裡插入圖片描述
(~免費廣告位一則~)

AI Lab開源大規模高質量中文詞向量資料,800萬中文詞隨你用,質量非常高,就是一個詞向量.txt檔案都有16G之多,太誇張了。。不過的確非常有特點:

  • ⒈ 覆蓋率(Coverage):

該詞向量資料包含很多現有公開的詞向量資料所欠缺的短語,比如“不念僧面唸佛面”、“冰火兩重天”、“煮酒論英雄”、“皇帝菜”、“喀拉喀什河”等。以“喀拉喀什河”為例,利用騰訊AI Lab詞向量計算出的語義相似詞如下:

墨玉河、和田河、玉龍喀什河、白玉河、喀什河、葉爾羌河、克里雅河、瑪納斯河

  • ⒉ 新鮮度(Freshness):

該資料包含一些最近一兩年出現的新詞,如“戀與製作人”、“三生三世十里桃花”、“打call”、“十動然拒”、“供給側改革”、“因吹斯汀”等。以“因吹斯汀”為例,利用騰訊AI Lab詞向量計算出的語義相似詞如下:

一顆賽艇、因吹斯聽、城會玩、厲害了word哥、emmmmm、扎心了老鐵、神吐槽、可以說是非常爆笑了

  • ⒊ 準確性(Accuracy):

由於採用了更大規模的訓練資料和更好的訓練演算法,所生成的詞向量能夠更好地表達詞之間的語義關係。

騰訊AI Lab採用自研的Directional Skip-Gram (DSG)演算法作為詞向量的訓練演算法。DSG演算法基於廣泛採用的詞向量訓練演算法Skip-Gram (SG),在文字視窗中詞對共現關係的基礎上,額外考慮了詞對的相對位置,以提高詞向量語義表示的準確性。


文章目錄


1 Tencent_AILab_ChineseEmbedding讀入與高效查詢

來看一下一個比較常見的讀入方式:lvyufeng/keras_text_sum/load_embedding.py

import numpy as np

def load_embedding(path):
    embedding_index = {}
    f = open(path,encoding='utf8')
    for index,line in enumerate(f):
        if index == 0:
            continue
        values = line.split(' ')
        word = values[0]
        coefs = np.asarray(values[1:],dtype='float32')
        embedding_index[word] = coefs
    f.close()

    return embedding_index

load_embedding('/home/lv/data_set/Tencent_AILab_ChineseEmbedding/Tencent_AILab_ChineseEmbedding.txt')

這樣純粹就是以字典的方式讀入,當然用於建模沒有任何問題,但是筆者想在之中進行一些相似性操作,最好的就是重新載入gensim.word2vec系統之中,但是筆者發現載入半天都會報錯:

ValueError: invalid vector on line 418987 (is this really the text format?)

仔細一檢視,發現原來一些詞向量的詞就是數字,譬如-0.212157851,所以一直匯入不進去。只能自己用txt讀入後,刪除掉這一部分,儲存的格式參考下面。

5 4
是 -0.119938 0.042054504 -0.02282253 -0.10101332
中國人 0.080497965 0.103521846 -0.13045108 -0.01050107
你 -0.0788643 -0.082788676 -0.14035964 0.09101376
我 -0.14597991 0.035916027 -0.120259814 -0.06904249

第一行是一共5個詞,每個詞維度為4.

然後清洗完畢之後,就可以讀入了:

wv_from_text = gensim.models.KeyedVectors.load_word2vec_format('Tencent_AILab_ChineseEmbedding_refine.txt',binary=False)

但是又是一個問題,佔用記憶體太大,導致不能查詢相似詞,所以這裡可以用一下這個神奇的函式,可以高效執行,這樣就可以順利使用most_similar這類函數了:

wv_from_text.init_sims(replace=True)  # 神奇,很省記憶體,可以運算most_similar

該操作是指model已經不再繼續訓練了,那麼就鎖定起來,讓Model變為只讀的,這樣可以預載相似度矩陣,對於後面得相似查詢非常有利。


2 未知詞、短語向量補齊與域內相似詞搜尋

這邊未知詞語、短語的補齊手法是參考FastText的用法:極簡使用︱Gemsim-FastText 詞向量訓練以及OOV(out-of-word)問題有效解決

這邊筆者借鑑了fasttext之中的方式,當出現未登入詞或短語的時候,會:

  • 先將輸入詞進行n-grams
  • 然後去詞表之中查詢
  • 查詢到的詞向量進行平均

主要函式可見:

import numpy as np

def compute_ngrams(word, min_n, max_n):
    #BOW, EOW = ('<', '>')  # Used by FastText to attach to all words as prefix and suffix
    extended_word =  word
    ngrams = []
    for ngram_length in range(min_n, min(len(extended_word), max_n) + 1):
        for i in range(0, len(extended_word) - ngram_length + 1):
            ngrams.append(extended_word[i:i + ngram_length])
    return list(set(ngrams))


def wordVec(word,wv_from_text,min_n = 1, max_n = 3):
    '''
    ngrams_single/ngrams_more,主要是為了當出現oov的情況下,最好先不考慮單字詞向量
    '''
    # 確認詞向量維度
    word_size = wv_from_text.wv.syn0[0].shape[0]   
    # 計算word的ngrams片語
    ngrams = compute_ngrams(word,min_n = min_n, max_n = max_n)
    # 如果在詞典之中,直接返回詞向量
    if word in wv_from_text.wv.vocab.keys():
        return wv_from_text[word]
    else:  
        # 不在詞典的情況下
        word_vec = np.zeros(word_size, dtype=np.float32)
        ngrams_found = 0
        ngrams_single = [ng for ng in ngrams if len(ng) == 1]
        ngrams_more = [ng for ng in ngrams if len(ng) > 1]
        # 先只接受2個單詞長度以上的詞向量
        for ngram in ngrams_more:
            if ngram in wv_from_text.wv.vocab.keys():
                word_vec += wv_from_text[ngram]
                ngrams_found += 1
                #print(ngram)
        # 如果,沒有匹配到,那麼最後是考慮單個詞向量
        if ngrams_found == 0:
            for ngram in ngrams_single:
                word_vec += wv_from_text[ngram]
                ngrams_found += 1
        if word_vec.any():
            return word_vec / max(1, ngrams_found)
        else:
            raise KeyError('all ngrams for word %s absent from model' % word)
    
vec = wordVec('千奇百怪的詞向量',wv_from_text,min_n = 1, max_n = 3)  # 詞向量獲取
wv_from_text.most_similar(positive=[vec], topn=10)    # 相似詞查詢

compute_ngrams函式是將詞條N-grams找出來,譬如:

compute_ngrams('萌萌的噠的',min_n = 1,max_n = 3)
>>> ['噠', '的噠的', '萌的', '的噠', '噠的', '萌萌的', '萌的噠', '的', '萌萌', '萌']

這邊沒有沿用fasttext之中的<>來區分詞頭、詞尾。

wordVec函式是計算未登入詞的,其中筆者小小加了一些內容,就是:當出現oov的情況下,最好先不考慮單字詞向量,如果能匹配到兩個字以上的內容就優先進行平均。

在得到未登入詞或短語的向量之後,就可以快速進行查詢,gensim裡面是支援給入向量進行相似詞查詢:

wv_from_text.most_similar(positive=[vec], topn=10)

其實,有了這麼一個小函式 + 稍微大記憶體的伺服器,就可以開始挖金礦了,筆者在此給出一部分可供參考與使用的小案例,案例中找出來的相似肯定還是不那麼幹淨,需要自行清洗一下:

網路用語挖掘:

vec = wordVec('天了嚕',wv_from_text,min_n = 1, max_n = 3)
wv_from_text.most_similar(positive=[vec], topn=20)

[('天了嚕', 1.0),
 ('天啦嚕', 0.910751223564148),
 ('天惹', 0.8336831331253052),
 ('我的天吶', 0.8315592408180237),
 ('天哪嚕', 0.8200887441635132),
 ('也是醉了', 0.8048921823501587),
 ('哦買噶', 0.7951157093048096),
 ('我也是醉了', 0.7925893664360046),
 ('我的天哪', 0.7903991937637329),
 ('天吶', 0.7862901091575623)
......
]

評論觀點

vec = wordVec('真難吃',wv_from_text,min_n = 1, max_n = 3)
wv_from_text.most_similar(positive=[vec], negative=['好吃'], topn=20)

[('真難', 0.8344259858131409),
 ('難吃', 0.8344259262084961),
 ('不好吃', 0.7413374185562134),
 ('難啊', 0.7120314836502075),
 ('難喝', 0.6996017694473267),
 ('難以下嚥', 0.6920732259750366),
 ('好難', 0.6856701374053955),
 ('挺好吃', 0.6801191568374634),
 ('真不容易', 0.6788320541381836),
 ('真的很難', 0.671592116355896),
 ('真的很好吃', 0.6692471504211426),
...

例子2:

vec = wordVec('環境乾淨',wv_from_text,min_n = 1, max_n = 3)
wv_from_text.most_similar(positive=[vec], topn=20)

[('環境乾淨', 0.9999998807907104),
 ('環境乾淨整潔', 0.8523852825164795),
 ('環境舒適', 0.8281853199005127),
 ('環境乾淨衛生', 0.8241869211196899),
 ('衛生乾淨', 0.8118663430213928),
 ('乾淨衛生', 0.7971832156181335),
 ('乾淨舒適', 0.796349287033081),
 ('環境清新', 0.7937666773796082),
 ('衛生好', 0.7925254702568054),
 ('環境整潔', 0.7919654846191406),
 ('環境好', 0.7814522981643677),
 ('房間乾淨', 0.7802159786224365),
 ('環境優雅', 0.7685255408287048),

同義詞挖掘

vec = wordVec('蘋果',wv_from_text,min_n = 1, max_n = 3)
wv_from_text.most_similar(positive=[vec],negative=['水果'], topn=20)

[('蘋果公司', 0.5877306461334229),
 ('蘋果開發', 0.5226757526397705),
 ('高通', 0.5215991735458374),
 ('谷歌', 0.5213730335235596),
 ('蘋果的iphone', 0.5150437355041504),
 ('微軟', 0.5127487778663635),
 ('蘋果新', 0.5012987852096558),
 ('pixel手機', 0.49072039127349854),
 ('蘋果高管', 0.4897959530353546),
 ('蘋果iphone', 0.4875335991382599),
 ('蘋果手機iphone', 0.4791686534881592),
 ('蘋果晶片', 0.47766292095184326),
 ('iphone', 0.4754045307636261),