Turing Bot(2)Wikipediaページからの埋め込み抽出

Embedded extraction from Wikipedia pages


2021/06/23
藤田昭人


前回wikipedia-tokens.txtwikipedia-papers.txt の 生成を試みましたが、本稿では残る wikipedia-embeddings.txt の生成を試みます。


もうひとつの難物、Word2Vec学習済みデータ

wiki-xml-to-txt.py の2つの入力データはいずれも、 2GBを超えるビッグデータです。 先の記事では、 Python でも手に余るほど巨大な Wikipedia のバックアップデータを取り込むためのCプログラム wikiPageSelector を作成しましたが、 Word2Vecの学習済みデータから wikipedia-embeddings.txt を作成する場合も同じアプローチを選択せざるえませんでした。

ターゲットである GoogleNews-vectors-negative300.bin は総データサイズは約3.4GB。

$ ls -l
total 7123392
-rw-r--r--@ 1 fujita  staff  3644258522  6  1 00:08 GoogleNews-vectors-negative300.bin
$ 

中身を調べてみると、 1単語を表現する300次元のベクトルで 300万の単語を収蔵したファイルになります。 Word2Vecの学習済みデータもまた巨大にであることは 以前紹介した しましたが、扱えるファイルサイズの上限が2Gの JavaScript では手の出しようがありません。


Cプログラム make_embeddings

…ということで埋め込みを抽出するための C言語を使ったプログラム make_embeddings を作成しました*1ソースコードは下記に置きましたので、 参考にしてください。

github.com

Word2Vecの紹介記事 で説明したように学習済みWord2Vecデータはバイナリ形式でファイルに出力します。 wiki-xml-to-txt.py ではこのバイナリ形式を読むために(たぶんあまり一般的ではない)コードが実装されていたので、 そのコードは追わずに、前述の紹介記事で使用した distance コマンドのソースの一部を流用することにしました*2

$  ./make_embeddings wikipedia-tokens.txt GoogleNews-vectors-negative300.bin > wikipedia-embeddings.txt
$ head -2 wikipedia-embeddings.txt
25889 300
0 -0.004532 -0.022022 0.049640 -0.037960 -0.028227 0.030660 0.127507 -0.078840 0.008273 -0.048180 -0.031633 -0.075433 0.037230 0.069107 -0.114854 0.031025 0.088087 0.108040 -0.124587 -0.020927 -0.100254 0.024090 0.059617 0.013870 0.055967 -0.067160 -0.054507 -0.029443 -0.103174 0.041123 -0.061320 0.023117 0.028105 0.016425 0.036987 -0.015817 0.021413 0.025672 -0.077380 -0.000806 -0.014296 -0.046963 0.039663 -0.098793 -0.103174 -0.038690 -0.057670 -0.047450 -0.007057 0.075433 0.086627 -0.011558 0.075920 -0.053047 0.027375 0.071540 -0.054507 0.006935 0.054993 -0.049397 -0.091980 0.022508 -0.047207 0.007756 -0.014235 0.066673 0.021048 0.000121 -0.067160 0.090033 -0.042827 -0.029930 0.065213 0.055237 -0.079327 0.019223 0.027983 0.139187 0.071053 0.023603 0.083707 0.041123 0.069593 0.065213 -0.027740 -0.073000 -0.079327 0.154760 -0.011437 -0.022265 0.167414 -0.014113 0.046233 0.009794 -0.027618 -0.073000 0.008517 0.103660 -0.032120 -0.073487 -0.056453 -0.037230 -0.010585 0.061077 0.072027 0.034553 -0.023847 -0.089060 -0.073000 0.024577 0.017763 -0.059617 0.001589 0.010220 -0.068133 -0.120207 -0.004775 0.015817 0.029200 0.042340 -0.107067 0.115827 -0.029565 0.063753 -0.057427 -0.100254 0.043800 -0.027497 0.015026 0.013201 0.006722 -0.066187 -0.016060 -0.039907 -0.007361 -0.061077 0.026888 0.075433 0.045017 0.033580 -0.017277 -0.014478 0.002661 -0.036013 0.004380 0.063753 -0.011437 -0.119720 0.014539 0.026280 0.033580 0.133347 -0.059617 0.114854 0.032363 0.011802 -0.020562 0.000631 -0.016790 0.021778 0.020683 0.041123 0.036743 0.023360 0.029078 -0.005019 0.001452 -0.025185 0.148920 -0.028105 0.016547 0.047207 0.041610 -0.058887 -0.011619 -0.010828 0.106094 0.039907 -0.024820 0.099280 0.027740 -0.026037 0.019710 0.030173 0.062293 0.006753 -0.034553 0.073487 0.018737 0.074460 -0.033580 -0.078353 -0.092953 0.060833 -0.052803 -0.003285 0.090033 0.055237 -0.085653 -0.005019 0.044287 -0.085653 0.032120 -0.025672 -0.033093 0.041610 0.029930 0.019953 -0.024333 0.001795 -0.013140 -0.009186 0.007057 0.005749 0.022630 -0.031390 0.050127 0.037230 -0.006175 0.014600 0.101714 -0.061807 -0.064240 -0.030295 0.094900 -0.077867 -0.002403 0.115340 0.009247 0.033337 0.001384 0.023603 -0.054020 -0.073973 0.028105 -0.035040 0.009308 -0.047207 0.020318 -0.021170 0.029808 0.018980 -0.068620 0.016912 0.079813 0.049153 -0.019710 -0.064240 0.094900 -0.013992 -0.052560 0.005232 0.041367 -0.015817 0.090033 -0.057183 0.030782 0.001080 -0.017642 -0.099767 -0.039663 -0.049397 0.014783 0.021535 0.050127 0.151840 0.134320 -0.045260 -0.044287 0.096847 0.117287 -0.103660 0.016060 0.009368 -0.069107 -0.012958 0.007848 -0.014296 0.039177 -0.057427 -0.090520 0.082247 -0.057670 -0.058400 0.085167 0.062780 -0.006235 -0.016425 0.026037 0.013566 -0.096360 0.014904 -0.046477 0.026767 -0.058643 0.034797 0.052803 0.072027 0.090033 -0.043313
$ 

処理内容を簡単に説明しておくと…

  • 第1引数で指定した wikipedia-tokens.txt から全てのトークンとIDを取り込む
    • 単語総数は25889
  • 第2引数で指定した学習済みWord2Vecデータ(GoogleNews-vectors-negative300.bin)を走査し、トークンが一致するエントリーを見つける
  • 見つかったエントリについてトークンをIDに置き換え、ベクトルと共に表示
    • ベクトルの各数値の桁数はfloatの書式%fのデフォルトを使っているので%8.6fで表示される(好みに合わせて調整を)

今回の出力はオリジナルの仕様に則ったテキストファイルにしました。 というのも、よく考えたらこのファイルは改造版 fastWMD コマンドで読み込むから。 前回紹介した JSON フォーマットの出力ファイルもテキストファイルに直します。


まとめ

以上、本稿では wikipedia-embeddings.txt の生成を試みました。

これで Turing Bot のための WMD 実装の外堀は埋まった格好ですが、
改めて Word Mover's Distance の処理を俯瞰しておくと、 以下の4項目になります。

  1. 対象文章をトークン化する(前回
  2. トークン毎に埋め込み(分散表現)を抽出(今回)
    • 既存の学習済みWord2Vecデータを抽出
  3. トークン毎の埋め込み(分散表現)を合成し対象文章の埋め込み(分散表現)を生成する
  4. 比較する2つの文章の埋め込み(分散表現)で最適輸送問題を解き最小距離を算出する

これまで1、2、4については触れてきましたが、 実は3については全く触れていませんでした。 fastWMD の実装では Tools::getTripletsDocuments で実装されているようですが、次回は 改造版 fastWMD コマンドの作業を進めながら 3についても解説する予定です。

以上

*1:PythonJavaScript などのスクリプト言語で プログラミングを始めた若い方々にはピンと来ないかもしれませんが、 マシンの物理メモリを目一杯搭載しても16MBだった 30年前にプログラミングを始めた我々の世代にとって、 ストリームを扱うプログラミングこそが一般的でした。

当時はパイプが使える Unix の上で Cでプログラミングするのが大変便利だったのですが、 30年経過した今でもCがリーサルウェポンになる世界に住んでいるとは なんだか感慨深いです。

*2:プログラマの立場からコメントすると、 distanceソースコードはかなりまどろっこしいです😀