人生成り行き

立川談志とイチローに憧れるソフトウェアエンジニアの日記

word2vecをwikipediaコーパスで学習

作業動機

今更ながら練習として映画推薦サービスを作ってみようかなと思った.

とりあえず

MovieLens | GroupLens

をいじってみようと思ってダウンロード.

内容はこんな感じ.

  • genome-scores.csv: タグと映画の関連性
  • genome-tags.csv: タグID
  • links.csv: 別データ・セットとの映画ID対応表
  • movies.csv: 映画ID・タイトル・ジャンル(複数)
  • ratings.csv: ユーザの,映画に対する評価値(悪0.5~5良)と評価した時刻.
  • tags.csv: ユーザが映画に対してつけたtagとその時刻.

ぱっと思い浮かんだのは,ユーザに幾つかの映画に対する評価をしてもらい,ratingから似た評価をしているユーザを取ってきて,そのユーザの評価が高い映画を薦めるという能動学習的な方法.

その方法だときっちりユーザ登録型のWebサイト運営出来てないとキツいと思うので,出来ればその他のTwitterのツイートとか見せてもらうとかしてユーザの趣向を取ってきて,推薦したい(出来なさそうだけどとりあえず).

ということで自然言語処理のノウハウが必要になってくるけど,とりあえずはWord2Vecでお茶を濁す.日本語wikipediaのデータを学習してみる.

参考にしたWebサイトは以下の通り.


qiita.com


tjo.hatenablog.com

作業手順

WikipediaのデータをWord2Vecで使える形式に変換

まずwikipediaのデータを取ってくる.

$ curl https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2 -o jawiki-latest-pages-articles.xml.bz2

このままだとテキストデータではないので,変換する必要がある.

python環境でやっていく予定なので,WikiExtractor.pyが使える.

$ git clone https://github.com/attardi/wikiextractor
$ python3 wikiextractor/WikiExtractor.py jawiki-latest-pages-articles.xml.bz2

ここまでやるとtextディレクトリが作成されており,それらの下部には各ページのテキストが保存されている.

それらを一つにまとめるために,以下の処理を行う.

$ cat text/*/* > jawiki.txt

でword2vecに読み込ませる前にWord単位のセグメンテーションを行っておく必要がある.そこで,MeCabをhomebrewで入れてそれを使う.

$ brew install mecab
$ mecab -Owakati jawiki.txt -o data.txt

Word2Vecのインストールと実行

$ pip3 install gensim 

でipyhtonを使って学習.

from gensim.models import word2vec
data = word2vec.Text8Corpus("data.txt")
model = word2vec.Word2Vec(data, size=200)

modelが学習されるのに結構時間が掛かりそうなのでとりあえず今はここまで書いておく.

出力

後日,出力の確認をしてみた.
よく知られている例で,イチロー - 野球 + 本田 → サッカーっていう挙動をするってのがあるけど,今回のWikipediaのデータセットのみではそんな出力は出なかったので一応報告.
米googleの研究者が開発したWord2Vecで自然言語処理(独自データ) - Qiita
もちろん上サイトではfacebookデータセットを独自に作ってるみたいなので,挙動が異るのは当たり前.
実際にTwitterとかから映画のレビューを取ってくるときにはそれなりのデータセットを用意する必要がある.

out = model.most_similar(positive=["イチロー", "本田"], negative=["野球"])
for x in out:
    print(x[0], x[1])
天谷 0.5703838467597961
中嶋 0.5688130259513855
中澤 0.5668359398841858
赤木 0.565341055393219
青木 0.5472140908241272
梶谷 0.5470311641693115
斉藤 0.5417250990867615
佐山 0.541033923625946
石毛 0.5390052199363708
松井 0.5352978706359863

その他にもいくつか.

out = model.most_similar(positive=["女性", "王"], negative=["男"])
for x in out:
    print(x[0], x[1])
王族 0.5936665534973145
国王 0.5321540832519531
王室 0.5042459964752197
君主 0.49709588289260864
スルターン 0.4776388108730316
ムスリム 0.47091352939605713
王妃 0.4708373248577118
異教徒 0.4697801470756531
アノーヤター 0.4674013555049896
諸侯 0.4593404531478882

out = model.most_similar(positive=["イスラム教", "旧約聖書"], negative=["コーラン"])
KeyError: "word '旧約聖書' not in vocabulary"

前半の例では王妃が割りと上位に来てて期待通り.
そうでなくても,"王"関連のワードが出て来るWord2Vecの性能に驚き.
自然言語処理は僕の専門分野ではないので,どの程度の性能がでるのかは知らなかったけど,思っていたよりはよかったかな.
更にデータセット増やせば結構良い結果が得られそう.
一方後半の例ではエラーが発生.
ユダヤ教とかが出てほしかったけど,まさか"旧約聖書"が無いとは...
あれかな.多分MeCab分かち書きの分割が上手く行っていないorデータセットにそもそも含まれていないって感じだと思う.
前者の方があり得るかな.その辺も勉強しないとな.