WikipediaのデータでWord2Vecを試す
2019-05-18
- プログラミング
いまさらWord2Vecをやってみる。
やりたいこと
Wikipediaの全日本語記事をWord2Vecする。これにより、単語を単語ベクトルに変換できる。
その後、単語の関連度とかを見て遊ぶ。
環境
- OS
Ubuntu 16.04 LTS - Python
3.5.2 - gensim
3.7.3
流れ
Word2Vec使うときはいつもこんな感じっぽい。
- テキストを用意する
- 分かち書きにする
- Word2Vecにかける
テキストを用意する
ダンプデータのダウンロード
Wikipediaのダンプデータについてはここに書いてある。
これをたどると記事全体のダンプデータのリンクがわかる。以下のようにしてダウンロードする。(-o
でダウンロード先を指定している。)
curl -o ~/Downloads/wikipedia.bz2 https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2
ダンプデータをテキストにする
上で取得したダンプデータの中身はXMLであり、かつWiki記法なので、これを単純なテキストにする。
WikiExtractorというツールを使う。
python3 WikiExtractor.py -o <OUTPUT_DIRECTORY> <INPUT_FILE>
という使い方。解凍不要で、bz2ファイルのまま使える。
けっこう時間がかかる。
git clone https://github.com/attardi/wikiextractor.git
cd wikiextractor
python3 WikiExtractor.py -o ~/Downloads/wikidata ~/Downloads/wikipedia.bz2
これで ~/Downloads/wikidata
にテキストができるが、1MBごとにファイルに分割されている。これを1つのファイルにまとめる。
cd ~/Downloads/wikidata
find . -name "wiki_*" | xargs cat > ~/Downloads/wikidata_all_tmp.txt
また、WikiExtractorで変換したファイルには
<doc id="5" url="https://ja.wikipedia.org/wiki?curid=5" title="アンパサンド">
...
</doc>
のようなタグが残っているので、これを削除する。
grep -v '^<doc id.*>$' ~/Downloads/wikidata_all_tmp.txt | grep -v '^</doc>$' > ~/Downloads/wikidata_all.txt
(参考) ダンプデータとテキストの確認
上で取得したダンプデータは bzip2 -d wikipedia.bz2
で解凍して中身を見れる。こんな感じ。
<page>
<title>アンパサンド</title>
<ns>0</ns>
<id>5</id>
<revision>
<id>71254632</id>
<parentid>70334050</parentid>
<timestamp>2019-01-10T10:45:42Z</timestamp>
<contributor>
<username>タバコはマーダー</username>
<id>631644</id>
</contributor>
<minor />
<comment>曖昧さ回避の体裁</comment>
<model>wikitext</model>
<format>text/x-wiki</format>
<text xml:space="preserve">{{redirect|&}}
{{WikipediaPage|「アンパサンド (&)」の使用|WP:JPE#具体例による説明}}
{{記号文字|&}}
{{複数の問題|出典の明記=2018年10月8日 (月) 14:50 (UTC)|独自研究=2018年10月8日 (月) 14:50 (UTC)}}
[[Image:Trebuchet MS ampersand.svg|right|thumb|100px|[[Trebuchet MS]] フォント]]
'''アンパサンド''' ('''&'''、英語名:{{lang|en|ampersand}}) とは並立助詞「…と…」を意味する[[記号]]である。[[ラテン語]]の {{lang|la|"et"}} の[[合字]]で、[[Trebuchet MS]]フォントでは、[[
ファイル:Trebuchet MS ampersand.svg|10px]]と表示され "et" の合字であることが容易にわかる。ampersa、すなわち "and per se and"、その意味は"and [the symbol which] by itself [is] an
d"である。
WikiExtractorによってテキストにすると、この部分は次のようになる。
<doc id="5" url="https://ja.wikipedia.org/wiki?curid=5" title="アンパサンド">
アンパサンド
アンパサンド (&、英語名:) とは並立助詞「…と…」を意味する記号である。ラテン語の の合字で、Trebuchet MSフォントでは、と表示され "et" の合字であることが容易にわかる。ampersa、すなわち "and per se and"、その意味は"and [the symbol which] by itself [is] and"である。
分かち書きにする
Word2Vecを使うには単語に分割されてないといけない。
英語は空白などで分割されているのですぐにわかるが、日本語はそのままだと分割されていないので、分かち書きにする作業が必要。
分かち書きにするためのライブラリとしては形態素解析器のMeCabを使う。
他にはJUMAN++やJanomeというのもあるらしい。
また、標準の辞書だと新語に弱い。1語として扱ってほしい語を複数単語にされたりする。これを避けるためにNEologdという辞書を使う。
この辞書によって結果がどう変わるかはNElogdのREADMEに例が書いてあるのでそれを見れば一発でわかる。
インストール
NEologdのREADMEに書いてある通りにやる。
sudo apt update
sudo apt install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file
git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
cd mecab-ipadic-neologd
./bin/install-mecab-ipadic-neologd -n
# yes とタイプする
これでインストールが完了する。
辞書のパスは以下でわかる。
echo `mecab-config --dicdir`"/mecab-ipadic-neologd"
自分の環境では /usr/lib/mecab/dic/mecab-ipadic-neologd
だった。
標準の辞書とNEologdの辞書で形態素解析すると、それぞれこんな感じになる。
$ echo 令和元年 | mecab
令 名詞,一般,*,*,*,*,令,リョウ,リョー
和 名詞,一般,*,*,*,*,和,ワ,ワ
元年 名詞,副詞可能,*,*,*,*,元年,ガンネン,ガンネン
EOS
$ echo 令和元年 | mecab -d /usr/lib/mecab/dic/mecab-ipadic-neologd
令和元年 名詞,固有名詞,一般,*,*,*,2019年,レイワガンネン,レイワガンネン
EOS
Wikipediaのテキストを分かち書きにする
これも時間がかかる。
mecab ~/Downloads/wikidata_all.txt -d /usr/lib/mecab/dic/mecab-ipadic-neologd -Owakati -o ~/Downloads/wikidata_all.wakati.txt -b 16384
オプションの意味は以下。
+ -d
使用する辞書を指定。上で調べたパスを入れる。
+ -Owakati
分かち書きにする。これを指定しない場合、普通に形態素解析される。
+ -o
出力ファイルを指定。
+ -b
バッファサイズを指定。
なお最後の -b 16384
は
input-buffer overflow. The line is split. use -b #SIZE option.
というエラーが途中で出たので追加した。デフォルトでは8192(mecab -hでわかる)。
変換後はバイナリファイルになるという話だったが、大丈夫そう。
$ file wikidata_all.wakati.txt
wikidata_all.wakati.txt: UTF-8 Unicode text, with very long lines
(参考) 分かち書きしたファイル
こんな感じになる。
アンパサンド
アンパサンド (&、 英語名 :) と は 並立 助詞 「 … と … 」 を 意味 する 記号 で ある 。 ラテン語 の の 合字 で 、 Trebuchet MS フォント で は 、 と 表示 さ れ " et " の 合字 で ある こと が 容易 に わかる 。 ampersa 、 すなわち " and per se and "、 その 意味 は " and [ the symbol which ] by itself [ is ] and " で ある 。
Word2Vecにかける
ここまでで分かち書きテキストを用意できたので、ようやく本題。
gensimというライブラリを使う。
gensimのインストール
cythonも入れた方が早いらしいので入れる。
sudo pip3 install --upgrade gensim cython
モデルの作成
wikidata_all.wakati.txt があるディレクトリに移動して python3
を実行し、そのままREPLで以下を全部実行するのが手軽。
時間がかかるので注意。
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences = LineSentence('./wikidata_all.wakati.txt')
model = Word2Vec(sentences, size=200, window=20, min_count=5, workers=4)
model.save('./wiki_word2vec.model')
wiki_word2vec.model というファイルが保存される。
これがWikipediaのWord2Vecモデル。
ハイパーパラメータの設定について
モデル作成時に一番重要なのは以下の部分。値は調整必要。他にも指定したいパラメータがあればここを見ながらやる。
model = Word2Vec(sentences, size=200, window=20, min_count=5, workers=4)
- size
単語ベクトルの次元の数。 - window
この単語数だけ、前後の単語を見る。 - min_count
テキスト中に現れる回数がこの数未満の単語は無視。 - workers
訓練時に使うスレッド数。
使ってみる
モデルの読み込み
いろいろ試す前に、まずモデルを読み込んでおく必要がある。
(REPLでモデルの作成をした直後は、既にメモリ上にモデルが載っているので、改めて読み込まなくていい。)
from gensim.models import Word2Vec
model = Word2Vec.load('wiki_word2vec.model')
wv = model.wv
なおwvのクラスは以下になる。このクラスのドキュメントに使い方が載っている。
>>> type(wv)
<class 'gensim.models.keyedvectors.Word2VecKeyedVectors'>
似た単語の表示
wv.similar_by_word('foo')
でわかる。
>>> wv.similar_by_word('猫')
[('ネコ', 0.7882304787635803), ('ウサギ', 0.7559791207313538), ('子猫', 0.7516933679580688), ('黒猫', 0.7485260367393494), ('野良猫', 0.7470539808273315), ('犬', 0.7432234883308411), ('飼い猫', 0.7408008575439453), ('愛猫', 0.7271249890327454), ('柴犬', 0.6979021430015564), ('飼い主', 0.6935858726501465)]
>>> wv.similar_by_word('犬')
[('猟犬', 0.7577184438705444), ('猫', 0.7432234883308411), ('犬種', 0.7246831059455872), ('飼い主', 0.7098195552825928), ('牧羊犬', 0.7084189653396606), ('仔犬', 0.7059577703475952), ('番犬', 0.6994990110397339), ('プードル', 0.6932131052017212), ('子犬', 0.6915150880813599), ('柴犬', 0.688454270362854)]
>>> wv.similar_by_word('令和')
[('新元号', 0.8433001041412354), ('平成最後', 0.6003873348236084), ('生前退位', 0.5694811940193176), ('令和元年', 0.5659081935882568), ('天皇誕生日', 0.5473066568374634), ('建国記念の日', 0.5416209697723389), ('改元', 0.5325936079025269), ('退位特例法', 0.5113210678100586), ('即位礼正殿の儀', 0.5051107406616211), ('明治節', 0.49450594186782837)]
>>> wv.similar_by_word('Apple')
[('アップル', 0.7637709975242615), ('iPhone', 0.6721816062927246), ('Macintosh', 0.6700955033302307), ('VisiCalc', 0.6614382863044739), ('iMac', 0.6558083295822144), ('アップルコンピュータ', 0.6533167958259583), ('スティーブ・ジョブズ', 0.6456233263015747), ('Xerox', 0.619143009185791), ('Apple社', 0.614047110080719), ('iPad', 0.6136564016342163)]
>>> wv.similar_by_word('リンゴ')
[('サクランボ', 0.6949737668037415), ('イチゴ', 0.6846523284912109), ('りんご', 0.6731216907501221), ('果物', 0.6545817852020264), ('トマト', 0.6475458145141602), ('シードル', 0.6408026218414307), ('プルーン', 0.6374067664146423), ('レタス', 0.6371461153030396), ('ジャガイモ', 0.6367175579071045), ('ピーナッツ', 0.6305103898048401)]
2語の関連度
wv.similarity('foo', 'bar')
でわかる。
>>> wv.similarity('猫', '犬')
0.74322355
>>> wv.similarity('猫', '夏目漱石')
0.19329126
>>> wv.similarity('吾輩は猫である', '夏目漱石')
0.73291266
単語の和・差
wv.most_similar(positive=['foo', 'bar'], negative=['baz'])
これで foo + bar - baz がわかる。
>>> wv.most_similar(positive=['王様', '女'], negative=['男'])
[('お姫様', 0.6474927663803101), ('王女', 0.601335346698761), ('白雪姫', 0.5954098701477051), ('シンデレラ', 0.589481770992279), ('貴婦人', 0.5760519504547119), ('女王', 0.5691637992858887), ('妖精', 0.5630465149879456), ('花嫁', 0.5587092638015747), ('魔女', 0.5510756969451904), ('オーベロン', 0.5459994077682495)]
>>> wv.most_similar(positive=['日本', '北京'], negative=['東京都'])
[('中国', 0.6868176460266113), ('上海', 0.6344862580299377), ('天津', 0.566441535949707), ('香港', 0.5649573802947998), ('中国本土', 0.563534677028656), ('広州', 0.549053430557251), ('台湾', 0.5482343435287476), ('中華', 0.5481294393539429), ('厦門', 0.547395646572113), ('中華圏', 0.5437194108963013)]
感想
やってみるだけならわりと簡単にできた。
でも wv.similar_by_word()
の精度は良くない感じ。エポック数を増やしたり、前処理を厳密にやらないといけない。
wv.most_similar()
は想定通りの結果になった。
参考
WikipediaデータをWord2Vecする件
- 誰でも簡単にWikipediaからword2vecを生成できるツール - Qiita
スクリプトにまとめてある。 - word2vecをwikipediaコーパスで学習 - 人生成り行き
やや古いがあっさり書いてあって読みやすい。 - Wikipediaコーパスを使った,word2vecのモデル作成 - 接着剤の精進日記
WikiExtractor
- attardi/wikiextractor: A tool for extracting plain text from Wikipedia dumps
公式。 - Wikipediaのデータを使いましたって書いてある論文は実際どうやって使える状態にしてるのか調べた話 - EnsekiTT Blog
MeCab
- MeCab: Yet Another Part-of-Speech and Morphological Analyzer
公式。 - MeCab で「input-buffer overflow. The line is split. use -b #SIZE option.」エラー - KEINOS™の日記
"input-buffer overflow" エラーの解決方法。
NEologd
gensim
- gensim: models.word2vec – Word2vec embeddings
公式。モデル作成時はこれを見る。- gensim: models.word2vec – Word2vec embeddings
訓練時のパラメータの説明など。
- gensim: models.word2vec – Word2vec embeddings
- gensim: models.keyedvectors – Store and query word vectors
公式。モデルを使って何かやりたいときはこれを見る。 - 青空文庫のデータを使って、遅ればせながらword2vecと戯れてみた - 六本木で働くデータサイエンティストのブログ
gensim入れるときはcython入れると爆速になるという話。詳しくはここの上に "Make sure you have a C compiler before installing Gensim" と書いてあるところがあるのでそこを参照。