1. 程式人生 > >比賽必備 ︱ 省力搞定三款詞向量訓練 + OOV詞向量問題的可性方案

比賽必備 ︱ 省力搞定三款詞向量訓練 + OOV詞向量問題的可性方案

本篇為資源彙總,一些NLP的比賽在抽取文字特徵的時候會使用非常多的方式。

  • 傳統的有:TFIDF/LDA/LSI等
  • 偏深度的有:word2vec/glove/fasttext等
  • 還有一些預訓練方式:elmo / bert

在這裡插入圖片描述

1 之前的幾款詞向量介紹與訓練帖子

2 極簡訓練glove/word2vec/fasttext

2.1 word2vec的訓練與簡易使用

gensim裡面可以快速的進行訓練word2vec。

# 最簡單的開始
import gensim
sentences = [['first', 'sentence'], ['second', 'sentence','is']]

# 模型訓練
model = gensim.models.Word2Vec(sentences, min_count=1)
    # min_count,頻數閾值,大於等於1的保留
    # size,神經網路 NN 層單元數,它也對應了訓練演算法的自由程度
    # workers=4,default = 1 worker = no parallelization 只有在機器已安裝 Cython 情況下才會起到作用。如沒有 Cython,則只能單核執行。

幾個常用功能的列舉:

  • 如何獲取詞向量?
model['computer']  # raw NumPy vector of a word
array([-0.00449447, -0.00310097,  0.02421786, ...], dtype=float32)

model.wv['word']
  • 如何獲取詞表?
model.wv.vocab.keys()
  • 如何求相似?
y2=model.similarity(u"好", u"還行")     # 比較兩個詞的相似
print(y2)

for i in model.most_similar(u"滋潤"):  # 一個詞相似詞有哪些
    print i[0],i[1]

2.2 glove的訓練與簡易使用

pip install glove_python

該庫可以快速訓練了,而且與gensim使用方式即為相似,給好評:

from __future__ import print_function
import argparse
import pprint
import gensim
from glove import Glove
from glove import Corpus

sentense = [['你','是','誰'],['我','是','中國人']]
corpus_model = Corpus()
corpus_model.fit(sentense, window=10)
#corpus_model.save('corpus.model')
print('Dict size: %s' % len(corpus_model.dictionary))
print('Collocations: %s' % corpus_model.matrix.nnz)

其中corpus_model.fit(corpus, window=10, ignore_missing=False)ignore_missing代表如果出現OOV的詞,該如何處理。
來看一下原文描述這個ignore_missing:

whether to ignore words missing from the dictionary (if it was supplied).Context window distances will be preserved even if out-of-vocabulary words are ignored. If False, a KeyError is raised.

幾個常用功能的列舉:

  • 如何獲取詞向量?
# 全部詞向量矩陣
glove.word_vectors
# 指定詞條詞向量
glove.word_vectors[glove.dictionary['你']]
  • 如何求相似?
glove.most_similar('我', number=10)

>>> [('中國人', 0.15130809810072138),
>>>  ('你', 0.0739901044877504),
>>>  ('誰', -0.05137569131012555),
>>>  ('是', -0.08668606334919005)]

2.3 fasttext的訓練與簡易使用

fasttext的訓練,facebook有自己的訓練方式:facebookresearch/fastText,不過訓練起來還挺費勁,對新手不友好。那麼gensim在新版本里面已經封裝了fasttext,也挺好用,已經滿足了基本要求。

from gensim.models import FastText
sentences = [["你", "是", "誰"], ["我", "是", "中國人"]]

model = FastText(sentences,  size=4, window=3, min_count=1, iter=10,min_n = 3 , max_n = 6,word_ngrams = 0)
model['你']  # 詞向量獲得的方式
model.wv['你'] # 詞向量獲得的方式

同時gensim裡面既有py版本的,也有c++版本的。

# 使用c++ 版本的fasttext
from gensim.models.wrappers.fasttext import FastText as FT_wrapper

# Set FastText home to the path to the FastText executable
ft_home = '/home/chinmaya/GSOC/Gensim/fastText/fasttext'

# train the model
model_wrapper = FT_wrapper.train(ft_home, lee_train_file)

print(model_wrapper)

幾個常用功能的列舉:

  • 如何獲取詞向量?
model['你']  # 詞向量獲得的方式
model.wv['你'] # 詞向量獲得的方式
  • 如何獲取詞表?
model.wv.vocab
  • 如何求相似?
model.wv.similarity('你', '是')  # 求相似
model.n_similarity(['cat', 'say'], ['dog', 'say'])  # 多個詞條求相似
model.most_similar("滋潤")  # 求詞附近的相似詞

similarity求兩個詞之間的相似性;n_similarity為求多個詞之間的相似性
其中還可以求詞條之間的WMD距離:

# !pip3 install pyemd 
model.wmdistance(['cat', 'say'], ['dog', 'say']) # 求詞條之間的WMD距離

2.4 elmo 預訓練模型

在ELMo 中,每個單詞被賦予一個表示,它是它們所屬的整個語料庫句子的函式。所述的嵌入來自於計算一個兩層雙向語言模型(LM)的內部狀態,因此得名「ELMo」:Embeddings from Language Models。
筆者在本篇裡面記敘了一下自己在之前嘗試的時候看到比較好的訓練開源專案:
流水賬︱Elmo詞向量中文訓練過程雜記
一共有三個中文訓練的源頭:
(1)可參考:searobbersduck/ELMo_Chin,不過好像過程中有些問題,筆者還沒證實原因。
(2)博文:《如何將ELMo詞向量用於中文》,該教程用glove作為初始化向量,思路如下:

  • 將預訓練的詞向量讀入
  • 修改bilm-tf程式碼
    • option部分
    • 新增給embedding weight賦初值
    • 新增儲存embedding weight的程式碼
  • 開始訓練,獲得checkpoint和option檔案
  • 執行指令碼,獲得language model的weight檔案
  • 將embedding weight儲存為hdf5檔案形式
  • 執行指令碼,將語料轉化成ELMo embedding。

2.5 BERT預訓練模型

2.6 已有的中文的詞向量舉例

2.6.1 facebook Pre-trained word vectors

facebook公開了90種語言的Pre-trained word vectors,使用預設引數,300維度,連結:

在這裡插入圖片描述

This project provides 100+ Chinese Word Vectors (embeddings) trained with different representations (dense and sparse), context features (word, ngram, character, and more), and corpora. One can easily obtain pre-trained vectors with different properties and use them for downstream tasks.

在這裡插入圖片描述

2.6.3 騰訊AI Lab開源大規模高質量中文詞向量資料

This corpus provides 200-dimension vector representations, a.k.a. embeddings, for over 8 million Chinese words and phrases, which are pre-trained on large-scale high-quality data. These vectors, capturing semantic meanings for Chinese words and phrases, can be widely applied in many downstream Chinese processing tasks (e.g., named entity recognition and text classification) and in further research.

3 OOV(out of vocabulary,OOV)未登入詞向量問題

未登入詞又稱為生詞(unknown word),可以有兩種解釋:一是指已有的詞表中沒有收錄的詞;二是指已有的訓練語料中未曾出現過的詞。在第二種含義下,未登入詞又稱為集外詞(out of vocabulary, OOV),即訓練集以外的詞。通常情況下將OOV與未登入詞看作一回事。

未登入詞可以粗略劃分為如下幾種型別:
①新出現的普通詞彙,如部落格、房奴、給力等,尤其在網路用語中這種詞彙層出不窮。
②專有名詞(proper names)。專有名詞在早期主要是指人名、地名和組織機構名這三類實體名稱。1996年第六屆資訊理解會議對此進行了擴充套件,首次提出了命名實體(named entity)的概念,新增了時間和數字表達(日期、時刻、時段、數量值、百分比、序數、貨幣數量等),並且地名被進一步細化為城市名、州(省)名和國家名稱等。
③專業名詞和研究領域名稱。特定領域的專業名詞和新出現的研究領域名稱也是造成生詞的原因之一,如三聚氰胺、蘇丹紅、禽流感、堰塞湖等。
④其他專用名詞,如新出現的產品名,電影、書籍等文藝作品的名稱,等等。

該問題在kaggle的《Toxic Comment Classification Challenge》提供了一些解決辦法。

3.1 fasttext 解決OOV的詞向量最佳方案

其中在《About my 0.9872 single model》中提及fasttext的使用:

Fixed misspellings by finding word vector neighborhoods. Fasttext tool can create vectors for out-of-dictionary words which is really nice. I trained my own fasttext vectors on Wikipedia comments corpus and used them to do this. I also used those vectors as embeddings but results were not as good as with regular fasttext vectors.

如果按照上面的訓練方式,也能夠快速解決OOV問題嗎?在gensim之中訓練fasttext:

from gensim.models import FastText
sentences = [["你", "是", "誰"], ["我", "是", "中國人"]]
model = FastText(sentences,  size=4, window=3, min_count=1, iter=10,min_n = 3 , max_n = 6,word_ngrams = 0)
model .most_similar("滋潤") 

在gensim之中,直接使用most_similar就可以實現,筆者來舉幾個例子:
案例一:

model.most_similar("萌萌的的的噠")

>>> [('胸上', 0.5790194272994995),
 ('萌萌噠', 0.5606832504272461),
 ('西子湖畔', 0.5515443086624146),
 ('門用', 0.5502334833145142),
 ('憨態可掬', 0.5367637872695923),
 ('古箏曲', 0.5365685224533081),
 ('濟濟一堂', 0.5358039140701294),
 ('雕塑感', 0.5351789593696594),
 ('ハ', 0.5332954525947571),
 ('桃江', 0.5323261022567749)]

案例二:

model.most_similar("這是一個未登入詞")

>>>[('馬忠真', 0.6866541504859924),
 ('聞訊', 0.6599311828613281),
 ('手板', 0.6508469581604004),
 ('米仁', 0.6501662731170654),
 ('彈指神功', 0.6470856666564941),
 ('表交', 0.6461164951324463),
 ('和級', 0.645912766456604),
 ('膏厚', 0.6436258554458618),
 ('帶彈', 0.643358588218689),
 ('宴請', 0.6427510976791382)]

雖然第二個有點無厘頭,但是一些比較合理地未登入詞會找到比較合理地答案。
使用這個工具可以很快地利用未登入詞中的字詞片段來找到最相似的詞是哪些,然後可以賦值。

3.2 兩個詞向量空間對齊

上面提到的fasttext是解決單個OOV,筆者看到比賽中也有嘗試兩個詞向量集合對齊的方案,比較簡單易懂,而且使用的該方案能排在比賽的33rd,應該屬於比較合理地方案,來看看mattmotoki/toxic-comment-classification

we get a vector for every word in our vocabulary. We can now find the most similar vector in the intersection of the local vocabulary (from this competition) with the external vocabulary (from pretrained embeddings).

local = {local_words: local_vectors}
external = {external_words: external_vectors}
shared_words = intersect(local_words, external_words)
missing_words = setdiff(local_words, external_words)
reference_matrix = array(local[w] for w in shared_words).T

for w in missing_words:
     similarity = local[w] * reference_matrix
     most_similar_word = shared_words[argmax(similarity)]
     external[w] = external_vectors[most_similar_word]

return {w: external[w] for w in local_words}

With this technique, GloVe performed just as well if not better than the fastText with OOV prediction; LexVec performed slightly worse but added valuable diversity to ensembles.
筆者理解的大致意思就是,A詞向量集合—>B詞向量集合:

  • 先找出A & B 詞向量集合都擁有的詞shared_words ;
  • 找出 A - B,A中B沒有的詞missing_words ;
  • A詞向量集合中,共同擁有的詞shared_words 的詞向量矩陣reference_matrix (標準化);
  • 在missing_words 詞中,譬如a詞,一一找出與shared_words 詞集合最相近的詞b;
  • 在B詞向量集合中,B(a) = B(b),B詞向量集合中就有a詞的向量了。

筆者說的比較繞口,可以直接看code,該作者寫了:

  • 一個一個迴圈查詢;
  • 整個missing_words空間一起查詢;
  • 用torch GPU加速查詢

比較適合拿來用,供觀眾參考。