日本語版WikipediaのデータからWord2Vec用データをつくる(Mac対応)


 好きなデータをベクトル化できたらやれること増える

やること

前回word2vecのデモを試せた ので、今回は日本語で試してみたいと思います。
すぐ使えるword2vec用の日本語データも公開はされているようですが、今後いろいろなデータからベクトルを作れるよう、学習用の前処理を手元でやってみたいと思います。
ここではWikipedia日本語版の公開データからベクトルデータを作成するまでを行います。

環境

  • Linux系
  • MacBookAir 2015 で検証。
  • 小型扇風機(ファイル処理の計算中にノートPCが熱くなりがち)

やり方

word2vecをインストールする

(参考)前回記事

$ cd word2vec #任意の作業ディレクトリに移動してください
$ git clone https://github.com/svn2github/word2vec.git
$ cd word2vec
$ make
$ chmod +x *.sh

すでにword2vecがインストール済みであれば、word2vecディレクトリへの移動だけを実施する。前回記事はインストールからデモで遊ぶところまでを試しました。

日本語版wikiのデータをダウンロードする。

$ curl -O https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2
 #または $ wget https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2

https://dumps.wikimedia.org/ の中の https://dumps.wikimedia.org/jawiki/latest/ で最新の日本語版wikiデータが公開されているので、本文ファイルの最新版をダウンロードします。3Gぐらいのファイルサイズになります。

日本語版wikiデータの中身をチェック

ここでファイルを解凍して中身を見ると理解が深まります。

解凍と中身チェックの工程は飛ばしてもかまいません。解凍せずとも前処理が可能です。
$ bunzip2 jawiki-latest-pages-articles.xml.bz2

.bz2の圧縮ファイルはbunzip2コマンドで解凍できます。PCが熱くなったら扇風機で冷やしましょう。解凍後は13Gぐらいになります。

$ head -n 50 jawiki-latest-pages-articles.xml 

解凍済みのファイルの先頭50行だけ覗いてみます。
タグらだけでファイルが重くなっているだろうことがわかります。

wikiextractorでデータからxmlをクリンナップする

wikiextractorという専用のツールが公開されているのでありがたく利用させていただきます。
参考)http://kzkohashi.hatenablog.com/entry/2018/07/22/212913

$ git clone https://github.com/attardi/wikiextractor.git

上記のgit URLは変更される場合があるので、もしダウンロードできなかった場合には、検索して有効なものに差し替えてください。

ダウンロードできたら、wikiextractor を jawiki-latest-pages-articles.xml.bz2に対して実行します。

$ python ./wikiextractor/WikiExtractor.py jawiki-latest-pages-articles.xml.bz2 -q -b 10M -o wiki_texts

wikiextractorは.bz2 の圧縮ファイルのまま作業をしてくれます。
オプションについては、
-q 出力レポート停止(速度UP)
-b 出力ファイルを区切るサイズの指定。今回は10M毎。
-o クリンナップしたファイルの格納場所。今回はwiki_textsを作成。
と指示しています。
作業中、いくつかのエラーが出ますが最後まで出力できます。

# エラー例
WARNING: Template errors in article '1996年全豪オープン' (744789):

ある程度時間のかかる工程です。デスクトップなどからwiki_textsのディレクトリを見るとファイルが刻々と生成されるので、進捗が確認できます。
上記の分割設定では、AA、AB、ACの3ディレクトリに収まりました。一つのディレクトリにwiki_00からwiki_99までのファイルが格納されます。
手元では1時間ちょっとぐらいで終了し、./wiki_texts/AC/wiki_94までファイルが作成されました。ノートブックが熱くなるので扇風機で冷却しながらの作業です。

$ head -n 5 ./wiki_texts/AA/wiki_00

<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"である。

head で生成されたファイルを確認すると、若干のタグや改行が残っています。
ここからさらにクリンナップしていきます。

ファイルを連結する

$ find wiki_texts/ | grep wiki | awk '{system("cat "$0" >> wiki_all.txt")}'

参考サイトのコマンドをお借りしてファイルを連結します。
この例では、./wiki_texts 配下のディレクトリでwikiを含むファイルをwiki_all.txt(容量約3.09GB)にまとめています。

うまく行かない場合は、一つずつ連結すると成功するかもしれません。

自分の環境ではうまくいかなかった(データにノイズが入った)こともあったので下記のように分けての実行も試しました。
$ find ./wiki_texts/AA/ | grep wiki_ | awk '{system("cat "$0" >> wiki_allAA.txt")}'
$ find ./wiki_texts/AB/ | grep wiki_ | awk '{system("cat "$0" >> wiki_allAB.txt")}'
$ find ./wiki_texts/AC/ | grep wiki_ | awk '{system("cat "$0" >> wiki_allAC.txt")}'
$ cat wiki_allAA.txt wiki_allAB.txt wiki_allAC.txt > wiki_all.txt
$ rm wiki_allAA.txt wiki_allAB.txt wiki_allAC.txt 



ここで念のため下記も実行。文字コードを上書きしてデータを整えます。やや時間がかかります。

$ brew install nkf
$ nkf -w --overwrite wiki_all.txt 

仕上げのクリンナップをする

残りのタグを除去する
$ sed -e 's/<[^>]*>//g' ./wiki_all.txt > ./wiki_notag.txt

各項目の文頭についているタグを削除します。この時点でファイルは2.96GBになります。

丸括弧の中身を除去する
#全角カッコを半角に
$ sed -i -e 's/(/(/g' ./wiki_notag.txt && sed -i -e 's/)/)/g' ./wiki_notag.txt

#カッコ内を削除
$ sed -i -e 's/([^)]*)//g' ./wiki_notag.txt

丸括弧の中身は大事な要素のような気もしますが、今回は削除してみました。2.77GB。

最後に改行や余白を削除しました。

#空白をすべて埋めたあと空行を削除
$ sed -i -e 's/ //g' ./wiki_notag.txt && sed -i -e '/^$/d' ./wiki_notag.txt

みっちとしたファイルになり2.75GB。

ファイルの中身を確認するには、

$ less wiki_notag.txt

とします。
それぞれの項目が前後の関係ない項目と距離が近くなってしまう気がしますが、そのあたりの処理が必要であればいずれ。

mecabをインストールして分かち書きする

文章をみっちりに作れたので、単語ごとに分かち書きを行い、スペースで区切っていきます。
参考)https://qiita.com/paulxll/items/72a2bea9b1d1486ca751
参考)http://kzkohashi.hatenablog.com/entry/2018/07/22/212913
参考)https://akamist.com/blog/archives/2815

mecabのインストール

分かち書きを行うmecab関連の必要ファイルを導入します。

$ brew install mecab
$ brew install mecab-ipadic
$ brew install xz

Mac以外ではbrewの代わりにaptなどを使います。
またコンソールで指示が出た場合には、brew reinstall mecab や brew reinstall mecab-ipadic などの再インストールも実行しておきます。

新語に対応した辞書も導入しておきます。

$ git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
$ cd mecab-ipadic-neologd
$ ./bin/install-mecab-ipadic-neologd -n -a

3行めはオプションに -a をつけることで新語全部入りでの実行となります。
少し時間がかかります。最後の方で Do you want to install mecab-ipadic-NEologd?と尋ねられるのでyesを入力。インストールが終わるとFinish..と表示されます。

python用のmecabもインストールしておきます。

$ cd ../ #作業ディレクトリに戻ります
$ pip install mecab-python3

分かち書きを実行

$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd -Owakati wiki_notag.txt -o wiki_wakati.txt -b 163840

現在のディレクトリの位置や mecab-ipadic-neologd のパス指定にミスがなければこれで分かち書きが実行されます。-b の後の数字は小さいと失敗しました。

$ nkf -w --overwrite wiki_wakati.txt

ここで再度、念のためのデータ整理をしておいても良いかもしれません。

$ less wiki_wakati.txt

分かち書き後のファイルは3.24GB。上記コマンドで分かち書き後のテキストファイルの様子を確認できます。
これで下準備が整いました。

ベクトル化を実行

word2vecによるベクトル化を実行します。

$ ./word2vec -train wiki_wakati.txt -output wiki_wakati_w2v.bin -size 200 -window 5 -sample le-3 -negative 5 -hs 0 -threads 1 -binary 1 -iter 1

参考)https://qiita.com/dskst/items/a9571bdd74a30a5e8d55

オプションの意味は、

-train 学習に使用するファイル
-output 学習結果を出力するファイル名
-size ベクトルの次元数
-window 文脈の最大単語数
-sample 単語を無視する頻度
-negative ネガティブサンプリングに用いる単語数
-hs 学習に階層化ソフトマックスを使用するかどうか
-threads 学習に使用するスレッド数
-binary バイナリ形式で出力するかどうか
-iter トレーニング反復回数

となります。他にも単語リストを出力するなど、様々なオプションがあります。次元数を下げたり学習回数を増やすなど、サイズと精度をいろいろ調整できそうです。

しばらく待てば処理終了。手元では30分ぐらいかかりました。
wiki_wakati_w2v.bin が今回生成されたベクトルデータになります。

Vocab size: 1290027
Words in train file: 1004450612
という129万語のベクトルデータになりました。
(ちなみにファイルサイズはノートだと1.06GB、デスクトップだと0.73GBでした。ほぼ同じ手順で行いましたがデスクトップの方が精度もよかったので、手元のノートはデータ作成に失敗している可能性が高いです。)

新しい辞書を試す

$ ./distance wiki_wakati_w2v.bin

関連の高い単語を探します。

Enter word or sentence (EXIT to break): アムロ

Word: アムロ  Position in vocabulary: 30293

                                              Word       Cosine distance
------------------------------------------------------------------------
                                         シャア      0.837395
                                      アスラン      0.772420
                                アムロ・レイ      0.766061
                                         シンジ      0.742949
                                            悟飯      0.739751
                                      カミーユ      0.725921
                                         鬼太郎      0.724508

それっぽいのが出ました。先ほども触れましたが、デスクトップPCで試した方がよい精度でした。ノートPCの方は2単語以上が?記号で連結された状態の単語も多く見られたので、処理に一部失敗しているようです。

日本に対する東京はフランスに対しては何?の問題もやってみました。

$ ./word-analogy wiki_wakati_w2v.bin

ルーク・スカイウォーカーに対するダース・ベイダーは、のび太に対して何でしょう?

Enter three words (EXIT to break): ルーク・スカイウォーカー ダース・ベイダー のび太

Word: ルーク・スカイウォーカー  Position in vocabulary: 95245
Word: ダース・ベイダー  Position in vocabulary: 68735
Word: のび太  Position in vocabulary: 11432

                                              Word              Distance
------------------------------------------------------------------------
                                   ジャイアン      0.652843
                                         スネ夫      0.645669
                                         しずか      0.614481
                                   ドラえもん      0.609560
                                         ケロロ      0.608829
                                         鬼太郎      0.607345

ジャイアンやスネ夫が上位に出てきて良い感じです。なぜかまた鬼太郎がいます。

おわりに

近しい手順を踏むことで、いろいろな団体がテキストファイルとして提供してくれているコーパスを活用することができるようになりそうです。
小さくて性能のよいベクトルデータを作ることにも興味が出てきました。