FastText的內部機制
來自專欄 TensorFlowNews19 人贊了文章
來源 | TowardsDataScience
譯者 | Revolver
【磐創AI導讀】:本文是對fasttext的一個詳細介紹。
fasttext是一個被用於對詞向量和句子分類進行高效學習訓練的工具庫,採用c++編寫,並支持訓練過程中的多進程處理。你可以使用這個工具在監督和非監督情況下訓練單詞和句子的向量表示。這些訓練出來的詞向量,可以應用於許多處理數據壓縮的應用程序,或者其他模型的特徵選擇,或者遷移學習的初始化。
FastText支持使用negative sampling,softmax或層次softmax損失函數等方法來訓練CBOW或Skip-gram模型。我已經使用了fastText對一個規模有千萬個單詞的語料庫進行語義詞向量訓練,對於它的表現以及它對原任務的擴展,我都感到非常滿意。在此之前,我很難找到除了 getting started(https://fasttext.cc/docs/en/support.html)之外的關於fasttext的相關說明文檔,因此在這篇文章中,我將帶您了解fastText的內部原理以及它是如何工作的。對word2vec模型如何工作的理解是需要的,克里斯·麥考密克的文章(見鏈接)很好地闡述了word2vec模型。
一. 運行fasttext
我們可以通過下面這條命令來用fastText訓練一個Skip-gram模型:
$ fasttext skipgram -input data.txt -output model
data.txt是一個包含一串文本序列的輸入文件,輸出模型保存在model.bin文件下,詞向量則保存在model.vec中。
二. 表示方法
fasttext可以在詞向量的訓練和句子分類上取得非常好的表現,尤其表現在對罕見詞進行了字元粒度上的處理。
每個單詞除了單詞本身外還被表示為多個字元級別的n-grams(有時也稱為N元模子)。例如,對於單詞matter,當n = 3時,fasttext對該詞對字元ngram就表示為<ma, mat, att, tte, ter, er>。其中<和>是作為邊界符號被添加,來將一個單詞的ngrams與單詞本身區分開來。再舉個例子,如果單詞mat屬於我們的辭彙表,則會被表示為<mat>。這麼做剛好讓一些短詞以其他詞的ngram出現,有助於更好學習到這些短詞的含義。從本質上講,這可以幫助你捕捉後綴/前綴的含義。
可以通過-minn和-maxn這兩個參數來控制ngrams的長度,這兩個標誌分別決定了ngrams的最小和最大字元數,也即控制了ngrams的範圍。這個模型被認為是一個詞袋模型,因為除了用於選擇n-gram的滑動窗口外,它並沒有考慮到對單詞的內部結構進行特徵選擇。它只要求字元落在窗口以內,但並不關心ngrams的順序。你可以將這兩個值都設為0來完全關閉n-gram,也就是不產生n-gram符號,單純用單詞作為輸入。當您的模型中的「單詞」不是特定語言的單詞時或者說字元級別的n-gram沒有意義的時候,這會變得很有用。最常見的例子是當您將id作為您的單詞輸入。在模型更新期間,fastText會學習到每個ngram以及整個單詞符號的權重。
三. 讀取數據
雖然fastText的訓練是多線程的,但是讀取數據卻是通過單線程來完成。而文本解析和分詞則在讀取輸入數據時就被完成了。讓我們來看看具體是怎麼做到的:
FastText通過-input參數獲取一個文件句柄用於輸入數據。FastText不支持從stdin讀取數據,它初始化兩個向量word2int_和words_來跟蹤輸入信息。word2int_是一個字元串到數值的映射集,索引鍵是單詞字元串,根據字元串哈希值可以得到一個數值作為它的值,同時這個數值恰好就對應到了words_數組(std:::vector)的索引。words_ 數組在讀取輸入時根據單詞出現的順序遞增創建索引,每個索引對應的值是一個結構體entry,這個entry封裝了單詞的所有信息。條目包含以下信息:
struct entry {
std::string word;
int64_t count;
entry_type type;
std::vector<int32_t> subwords;
};
在這個entry里,word是單詞的字元串表示形式,count是各個單詞在輸入序列里的出現頻次,entry_type的值是word或label中的一個,label選項僅在有監督情況下有效。所有的輸入符號,包括entry_type都存儲在同一個詞典中,這使得擴展fastText來包含其他類型的實體變得更加容易(我將在後續的文章中詳細討論這一點)。最後,subword是一個包含一個單詞所有的n-grams的向量。這個subword也會在讀取輸入數據時被創建,然後被傳遞到訓練過程中。
word2int_的大小為MAX_VOCAB_SIZE = 30000000,這是一個硬編碼的數字。當在大型語料庫上進行訓練時,這個大小可以是受限制的,但也可以在保持性能的同時有效地增加。word2int_數組的索引是由字元串得到的整數哈希值,並且是0和MAX_VOCAB_SIZE之間的唯一數字。如果出現哈希衝突,得到的哈希值已經存在,那麼這個值就會增加,直到我們找到一個唯一的id來分配給一個單詞為止。
因此,一旦辭彙表的大小達逼近MAX_VOCAB_SIZE,演算法性能就會顯著下降。為了防止這種情況,每當哈希值的大小超過MAX_VOCAB_SIZE的75%時,fastText就會對辭彙表進行刪減。刪減過程是這樣的,首先增加單詞最小計數閾值來重新確定一個單詞是否有資格出現在單詞表裡,然後對詞典里所有計數小於這個的單詞進行刪減。當添加一個新單詞時,會檢查這個單詞對應的哈希值是否超過75%閾值,因此這種自動刪減可以在文件讀取過程的任何階段進行。
除了自動刪減過程,對於已經存在於辭彙表裡的單詞的最小計數是通過使用-minCount和-minCountLabel(用於監督訓練)這兩個參數來控制的。基於這兩個參數的刪減在整個訓練文件被處理之後進行。如果單詞表的總數已經觸發了前面所說的因哈希值太大發生的自動刪減,那麼您的詞典可能就需要手動設置一個較高值的minCount閾值了。但無論如何,你都必須手動指定minCount閾值,才能確保較低詞頻的單詞不會被用作輸入的一部分。
在求解負採樣損失函數過程中,一個大小NEGATIVE_TABLE_SIZE = 10000000的負採樣單詞表會被構造。注意它的大小是MAX_VOCAB_SIZE的三分之一。該表是從每個詞詞頻的平方根的一元模型分布(unigram distribution)中進行採樣構造的,這確保了每個詞出現在負採樣單詞表中的次數與它的頻率的平方根成正比。接著再對該表打亂詞序以確保其隨機性。
圖一 U(w)是一個特定單詞的計數,W是所有單詞計數的集合
接下來,一個用於刪除高頻詞的採樣表會被構建,這個表在the original word2vec extension paper這篇論文的2.3節中有大概描述。這背後的思想是,高頻詞所能提供的信息比罕見的單詞更少,而且高頻詞即使在遇見到更多相同單詞的實例後,它們的詞向量也不會發生太大的變化。
該論文提出了一種刪除訓練詞的方法,通過下面公式計算訓練詞被丟棄的概率:
圖二 t為所選閾值,f(w)為單詞w的出現頻率
作者認為t = 10e-5是一個較為合理的默認值。該公式丟棄了丟棄頻率大於閾值的詞,並在有效對低頻詞進行採樣的同時又保持了它們的相對頻率,從而抑制了高頻詞的誇大作用。
但另一方面,FastText又重新定義了這種分布。
圖三 t = 10e-4為所選閾值,f(w)為單詞w的出現頻率
默認的閾值可以通過 -t 手動設置。閾值t在fastText中的含義和最初的word2vec論文中的含義有所不同,你應該針對自己的應用程序進行調優。
在訓練階段,只有當從(0,1)的均勻分布中隨機抽取一個值的大小大於單詞被丟棄的概率時,該單詞才會被丟棄。下面是在默認閾值情況下,單詞被丟棄概率與詞頻f(w)的關係。如圖所示,隨著單詞頻率的增加,被抽到的概率大於被丟棄的概率P(w)的概率增加。因此,隨著單詞頻率的增加,被丟棄的概率也增加。注意這隻適用於無監督模型,在有監督模型中,單詞不會被丟棄。
圖四 fasttext中默認閾值下單詞被丟棄概率與詞頻f(w)的關係
如果我們用-pretrainedVectors參數初始化訓練,輸入文件中的值將被用於初始化輸入層向量。如果未指定,一個維度MxN的矩陣將會被創建,其中M = MAX_VOCAB_SIZE + bucket_size, N = dim。bucket_size是一個數組的長度大小,這個數組是為所有的ngrams符號分配的。它通過-bucket標誌進行設置,默認設置為2000000。
所有的ngrams在矩陣里的位置信息是通過取得ngram字元串的哈希值(同一個哈希函數)來進行初始化的,並將對該哈希值取模之後的值填到初始化後的矩陣中,其位置對應到MAX_VOCAB_SIZE + hash。注意到在ngrams空間中可能存在哈希衝突,但對於原始單詞來說則是不存在這種情況。這也會影響到模型的性能。
Dim表示訓練中隱藏層的維度,因此詞向量的維度可以通過-dim參數進行設置,默認值為100。矩陣的每個值被初始化為0到1/dim之間的均勻實數分布。
四. 訓練
一旦輸入層和隱藏層向量被初始化成功,多個訓練線程就會啟動。線程數量由-thread參數指定。所有訓練線程都共享一個指向輸入層和隱藏層向量矩陣的指針。所有線程都從輸入文件中讀取數據,並使用讀取到的每一行來更新模型,其實也就相當於批次大小為1的隨機梯度下降法。如果遇到換行字元,或者讀入的單詞數量超過允許的行最大數量,則會截斷該行的後續輸入。這裡通過MAX_LINE_SIZE設置,默認值為1024。
CBOW模型和Skip-gram模型都會同時對一段上下文文本的權重進行更新,這段文本的單詞數量是1到-ws(參數設置)之間的隨機均勻分布,也就是說窗口大小是隨機的。
損失函數的目標向量是這樣計算的,先對每個輸入向量作歸一化計算,再把歸一化後的所有向量求和可得。輸入向量是原始單詞以及該詞的所有ngrams的向量表示。通過計算這個損失函數,可以在前向傳播的過程中設置權重,然後又一路將影響反向傳播傳遞到輸入層的向量。在反向傳播過程中對輸入向量權重的調整幫助我們學到了使得共現相似性(co occurrence similarity)最大化的詞向量。學習速率參數-lr會決定每條特定的實例樣本對權重的影響究竟有多大。
圖五 無監督Skip-gram fastText模型的拓撲結構
模型的輸入層權重、隱藏層權重以及傳入的參數都會保存在.bin格式的文件中,-saveOutput標誌控制了是否輸出一個包含隱藏層向量的word2vec文件格式的.vec文件。
我希望這篇文章能幫助我們了解fasttext的內部工作原理。我個人已經通過使用這個庫取得了很多成功,並強烈推薦你用它去解決你的問題。在下一篇文章中,我將討論我為fastText添加的一些可以泛化它的能力的附加功能。敬請繼續關注。(譯自:
https://towardsdatascience.com/fasttext-under-the-hood-11efc57b2b3)最後,對深度學習感興趣,熱愛Tensorflow的小夥伴,歡迎關注我們的網站!http://www.tensorflownews.com。我們的公眾號:磐創AI。
推薦閱讀: