最近因為一些學習上的需要接觸了 word2vec,覺得好像是有點神秘的東西。一般初學自然語言處理的時候,處理文字最簡單的模型就是把每個詞當作一個單位,比如說用個 id 來表示他。然後再去算詞與詞之間的統計關係。或者是利用句子的文法結構來進行其他處理。如果把每個詞出現的次數當作一個維度的話,也可以把句子或文件用一個向量來表示。
不過 word2vec 是把每個詞本身用一個多維向量來表示,把詞投影到一個向量空間裡。而且不知道為什麼投影出來的空間有些特殊的性質,比如說相同屬性的詞可能會靠得很近,甚至部份的向量有邏輯上的線性關係等等:
vector('King') - vector('Man') + vector('Woman') ~= vector('Queen')
這份筆記紀錄了使用 word2vec 處理中文資料的小小實驗。
語料
不太確定有什麼開放的中文語料可供使用,於是決定先用萌典來做實驗,可按照教學,下載萌典資料:
git clone --depth 1 https://github.com/g0v/moedict-data.git
git clone --depth 1 https://github.com/g0v/moedict-epub.git
cp -v moedict-data/dict-revised.json moedict-epub/
cd moedict-epub
perl json2unicode.pl > dict-revised.unicode.json
緊接著我自己寫了一個 extract_json.py 把當中可供訓練的句子抽出來:
python3 extract_json.py < dict-revised.unicode.json > sentences.txt
斷詞
因為 word2vec 的輸入必須是以空白隔開的詞,這樣得出來的句子還不能直接使用。於是我用了結巴斷詞,寫了一個 cut.py 來處理這些句子。
python cut.py < sentences.txt > sentences.segged.txt
斷出來的結果大約像是:
紅樓夢 . 第十七回 : 「 一 槅 一 槅 , 或 有 貯書處 , 或 有 設鼎處 , 或 安置 筆硯 處 , 或供 設瓶花 、 或 安放 盆景 處 。 」
感覺雖不是很理想,但尚可接受。
使用 word2vec 訓練詞向量
接著我就用工作站把詞全部丟給 word2vec 訓練:
./word2vec -train sentences.segged.txt -output vectors.bin -cbow 0 -size 200 -window 10 -negative 5 -hs 0 -sample 1e-4 -threads 24 -binary 1 -iter 20 -min-count 1
因為萌典抽出來的資料不算多,所以訓練起來滿快的,只是當然效果就不太好了:
測試
首先測試距離相近的字:
./distance vectors.bin
雖然效果差強人意,不過還是可以看到相同屬性的字確實有些群聚效應:
Enter word or sentence (EXIT to break): 法國
Word: 法國 Position in vocabulary: 992
Word Cosine distance
------------------------------------------------------------------------
英國 0.763948
德國 0.727664
義大利 0.724732
瑞士 0.724247
西班牙 0.715528
奧地利 0.696855
蘇格蘭 0.689557
接著測試線性關係,A 比 B 就好像 C 比什麼?:
./word-analogy vectors.bin
對於國家與都市的推理,也稍有效果:
Enter three words (EXIT to break): 法國 巴黎 英國
Word: 法國 Position in vocabulary: 992
Word: 巴黎 Position in vocabulary: 6379
Word: 英國 Position in vocabulary: 822
Word Distance
------------------------------------------------------------------------
倫敦 0.583572
七二年 0.576999
莫斯科 0.566673
四四年 0.566332
一九四一年 0.562817
芝加哥 0.559529
一九六二年 0.556010
羅浮宮 0.555593
油畫院 0.554334
訓練出來的詞向量或許可以供進一步利用,只是在實際使用之前無法確定是否適用於特定的應用。
程式碼
我把相關的程式碼放在 GitHub 上面供參考:https://github.com/shaform/experiments/tree/master/word2vec_tw