基於深度學習的文本數據特徵提取方法之Glove和FastText

2019-12-03     AI公園

作者:Dipanjan (DJ) Sarkar

編譯:ronghuaiyang

導讀

今天接著昨天的內容,給大家介紹Glove模型和FastText模型,以及得到的詞向量如何應用在機器學習任務里。

(書接上回)

GloVe模型

GloVe模型指的是全局向量模型,是一種無監督學習模型,可以獲得類似於Word2Vec的dense詞向量。然而,技術是不同的,訓練是在一個聚合的全局詞-詞共現矩陣上做的,可以得到具有有意義的子結構的向量空間。這個方法是史丹福大學的Pennington等人發明的。

我們不會在這裡從頭開始詳細介紹模型的實現。我們在這裡會保持簡單,並試圖理解GloVe模型背後的基本概念。我們已經討論了基於計數的矩陣分解的方法,如LSA以及預測的方法,如Word2Vec。文章稱,目前,這兩個方法都存在明顯的缺陷。像LSA這樣的方法可以有效地利用統計信息,但在單詞類比任務上,比如我們如何發現語義相似的單詞,它們的表現相對較差。像skip-gram這樣的方法可能在類比任務上做得更好,但是它們在全局水平上沒有很好地利用語料庫的統計數據。

GloVe模型的基本方法是首先創建一個由(單詞,上下文)對組成的巨大單詞上下文共現矩陣,這樣的話,該矩陣中的每個元素表示的是這個單詞與上下文一起出現的頻率(可以是單詞序列)。接下來的想法是應用矩陣分解來逼近這個矩陣,如下圖所示。

考慮Word-Context (WC)矩陣、Word-Feature (WF)矩陣和Feature-Context (FC)矩陣,我們嘗試對WC = WF x FC進行因式分解,將WFFC相乘,重構WC。為此,我們使用一些隨機權重初始化WFFC,並嘗試將它們相乘以得到WC'(WC的近似形式),並度量它與WC的距離。我們多次使用隨機梯度下降(SGD)來降低誤差。最後,單詞特徵矩陣(WF)為每個單詞提供單詞嵌入,其中F可以預先設置為特定數量的維度。需要記住的非常重要的一點是,Word2Vec和GloVe模型的工作原理非常相似。這兩種方法的目的都是建立一個向量空間,在這個空間中,每個單詞的位置都受到其相鄰單詞的上下文和語義的影響。Word2Vec以單詞共現對的本地個別示例開始,GloVe以語料庫中所有單詞的全局聚合共現統計數據開始。

將Glove特徵應用於機器學習任務

讓我們嘗試使用基於GloVe的嵌入式技術來完成文檔聚類任務。非常流行的spacy框架具有利用基於不同語言模型來得到GloVe嵌入。你還可以獲得預先訓練好的詞向量,並根據需要使用gensim或spacy加載它們。我們將首先安裝spacy並使用en_vectors_web_lg模型,該模型由訓練在Common Crawl上的300維單詞向量組成。

 # Use the following command to install spaCy
> pip install -U spacy
OR
> conda install -c conda-forge spacy
# Download the following language model and store it in disk
https://github.com/explosion/spacy-models/releases/tag/en_vectors_web_lg-2.0.0
# Link the same to spacy
> python -m spacy link ./spacymodels/en_vectors_web_lg-2.0.0/en_vectors_web_lg en_vecs
Linking successful
./spacymodels/en_vectors_web_lg-2.0.0/en_vectors_web_lg --> ./Anaconda3/lib/site-packages/spacy/data/en_vecs
You can now load the model via spacy.load('en_vecs')

在spacy中也有自動安裝模型的方法。現在,我們將使用spacy加載語言模型。

 import spacy

nlp = spacy.load('en_vecs')

total_vectors = len(nlp.vocab.vectors)
print('Total word vectors:', total_vectors)
 Total word vectors: 1070971

這驗證了一切都在正常工作。現在讓我們在玩具語料庫中獲取每個單詞的GloVe嵌入。

 unique_words = list(set([word for sublist in [doc.split() for doc in norm_corpus] for word in sublist]))

word_glove_vectors = np.array([nlp(word).vector for word in unique_words])
pd.DataFrame(word_glove_vectors, index=unique_words)

現在我們可以使用t-SNE來可視化這些嵌入,類似於我們使用Word2Vec嵌入所做的。

 from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, random_state=0, n_iter=5000, perplexity=3)
np.set_printoptions(suppress=True)
T = tsne.fit_transform(word_glove_vectors)
labels = unique_words

plt.figure(figsize=(12, 6))
plt.scatter(T[:, 0], T[:, 1], c='orange', edgecolors='r')
for label, x, y in zip(labels, T[:, 0], T[:, 1]):
plt.annotate(label, xy=(x+1, y+1), xytext=(0, 0), textcoords='offset points')

spacy的美妙之處在於,它將自動提供每個文檔中單詞的平均嵌入,而無需像我們在Word2Vec中那樣實現函數。我們將利用相同的方法為語料庫獲取文檔特徵,並使用k-means聚類來對文檔進行聚類。

 doc_glove_vectors = np.array([nlp(str(doc)).vector for doc in norm_corpus])

km = KMeans(n_clusters=3, random_state=0)
km.fit_transform(doc_glove_vectors)
cluster_labels = km.labels_
cluster_labels = pd.DataFrame(cluster_labels, columns=['ClusterLabel'])
pd.concat([corpus_df, cluster_labels], axis=1)

我們看到一致的聚類,類似於我們從Word2Vec模型中得到的結果,這很好!GloVe模型聲稱在很多情況下都比Word2Vec模型表現得更好,如下圖所示。

上述實驗是通過在Wikipedia 2014 + Gigaword 5上訓練300維向量,使用相同的40萬個單詞詞彙表和一個大小為10的對稱上下文窗口來完成的。

FastText模型

FastText模型於2016年首次由Facebook引入,作為普通Word2Vec模型的擴展和改進。基於Mikolov等人的原始論文' Subword Information Word Vectors with Subword Information',這是一本很好的讀物,可以深入了解這個模型是如何工作的。總的來說,FastText是一個學習單詞表示的框架,還可以執行健壯、快速和準確的文本分類。該框架由Facebook在GitHub上開源,擁有以下內容。

  • 最新最先進的英文詞向量。
  • 在Wikipedia和crawl-vectors上訓練了157種語言的詞向量。
  • 用於語言識別和各種受監督的任務的模型。

雖然我並沒有從零開始實現這個模型,但是根據我的研究論文,以下是我對這個模型是如何工作的了解。一般來說,像Word2Vec模型這樣的預測模型通常將每個單詞視為一個不同的實體(例如where),並為單詞生成dense的嵌入。然而,這是一個嚴重的限制,語言有大量的詞彙和許多罕見的詞,可能不會出現在很多不同的語料庫。Word2Vec模型通常忽略每個單詞的形態學結構,而將單詞視為單個實體。FastText模型認為每個單詞都是由n個字符組成的包。這在論文中也稱為子詞模型。

我們在單詞的開頭和結尾添加了特殊的邊界符號<>。這使我們能夠從其他字符序列中區分前綴和後綴。我們還將單詞w本身包含在它的n-gram集合中,以學習每個單詞的表示(加上其字符的n-gram)。以單詞wheren=3 (tri-grams)為例,它將由這些字符n-grams來表示:以及表示整個單詞的特殊序列來表示這個單詞。注意,單詞與tri-grams中的her是不同的。

在實際應用中,本文建議提取n≥ 3n≤ 6。這是一種非常簡單的方法,可以考慮不同的n-gram集合,例如取所有前綴和後綴。我們通常將向量表示(嵌入)與單詞的每個n-gram關聯起來。因此,我們可以用一個單詞的n-gram的向量表示的和或者這些n-gram的嵌入的平均值來表示這個單詞。因此,由於利用基於字符的單個單詞的n-gram的這種效果,罕見單詞獲得良好表示的幾率更高,因為它們基於字符的n-gram應該出現在語料庫的其他單詞中。

將FastText特徵應用於機器學習任務

gensim包封裝了很好的接口,為我們提供了gensim.models.fasttext下可用的FastText模型的接口。讓我們再次把這個應用到我們的聖經語料庫上,看看我們感興趣的單詞和它們最相似的單詞。

 from gensim.models.fasttext import FastText

wpt = nltk.WordPunctTokenizer()
tokenized_corpus = [wpt.tokenize(document) for document in norm_bible]

# Set values for various parameters
feature_size = 100 # Word vector dimensionality
window_context = 50 # Context window size
min_word_count = 5 # Minimum word count
sample = 1e-3 # Downsample setting for frequent words

# sg decides whether to use the skip-gram model (1) or CBOW (0)
ft_model = FastText(tokenized_corpus, size=feature_size, window=window_context,
min_count=min_word_count,sample=sample, sg=1, iter=50)


# view similar words based on gensim's FastText model
similar_words = {search_term: [item[0] for item in ft_model.wv.most_similar([search_term], topn=5)]
for search_term in ['god', 'jesus', 'noah', 'egypt', 'john', 'gospel', 'moses','famine']}
similar_words

可以在我們的Word2Vec模型的結果中看到許多相似之處,其中每個感興趣的單詞都有相關的相似單詞。你注意到有什麼有趣的關聯和相似之處嗎?

注意:運行此模型的計算開銷較大,而且與skip-gram模型相比,通常需要更多的時間,因為它考慮了每個單詞的n-gram。如果使用GPU或好的CPU進行訓練,可以更好地工作。我在AWS的p2上訓練過這個。例如,我花了大約10分鐘,而在一個普通的系統上需要花2-3個小時。

現在讓我們使用Principal Component Analysis (PCA)將單詞嵌入維數減少到二維,然後將其可視化。

 from sklearn.decomposition import PCA

words = sum([[k] + v for k, v in similar_words.items()], [])
wvs = ft_model.wv[words]

pca = PCA(n_components=2)
np.set_printoptions(suppress=True)
P = pca.fit_transform(wvs)
labels = words

plt.figure(figsize=(18, 10))
plt.scatter(P[:, 0], P[:, 1], c='lightgreen', edgecolors='g')
for label, x, y in zip(labels, P[:, 0], P[:, 1]):
plt.annotate(label, xy=(x+0.06, y+0.03), xytext=(0, 0), textcoords='offset points')

我們可以看到很多有趣的模式!諾亞、他的兒子閃和祖父瑪土撒拉是非常接近的。我們還看到上帝與摩西和埃及有聯繫,埃及在那裡忍受了聖經中的瘟疫,包括饑荒和瘟疫。此外,耶穌和他的一些門徒關係密切。

要訪問任何單詞embeddings,你可以使用以下的代碼用單詞為模型建立索引。

 ft_model.wv['jesus']
array([-0.23493268, 0.14237943, 0.35635167, 0.34680951,
0.09342121,..., -0.15021783, -0.08518736, -0.28278247,
-0.19060139], dtype=float32)

有了這些嵌入,我們可以執行一些有趣的自然語言任務。其中之一是找出不同單詞(實體)之間的相似性。

 print(ft_model.wv.similarity(w1='god', w2='satan'))
print(ft_model.wv.similarity(w1='god', w2='jesus'))
Output
------
0.333260876685
0.698824900473

根據我們的聖經語料庫中的文本,我們可以看到「上帝」「耶穌」聯繫更緊密,而不是「撒旦」

考慮到單詞嵌入的存在,我們甚至可以從一堆單詞中找出一些奇怪的單詞,如下所示。

 st1 = "god jesus satan john"
print('Odd one out for [',st1, ']:',
ft_model.wv.doesnt_match(st1.split()))
st2 = "john peter james judas"
print('Odd one out for [',st2, ']:',
ft_model.wv.doesnt_match(st2.split()))
Output
------
Odd one out for [ god jesus satan john ]: satan
Odd one out for [ john peter james judas ]: judas

總結

這些示例應該讓你對利用深度學習語言模型從文本數據中提取特徵,以及解決諸如單詞語義、上下文和數據稀疏等問題的新策略有一個很好的了解。

英文原文:https://towardsdatascience.com/understanding-feature-engineering-part-4-deep-learning-methods-for-text-data-96c44370bbfa

請長按或掃描二維碼關注本公眾號

文章來源: https://twgreatdaily.com/CUBBzW4BMH2_cNUgMNhC.html