Python で word2vec を使ってみる
word2vecとは
単語のベクトル表現を獲得する一つの手法です。
word2vecの特徴としては、意味的な計算が可能な表現であるということです。
例えば次の式のように、kingのベクトルからmanのベクトルを差し引いたベクトルにwomanのベクトルを足し合わすことで、queenのベクトルと近似するベクトルが得られます。
king - man + woman = queen
事前知識として
word2vec install
pip でインストールできます。
$ sudo pip install word2vec
英語のモデルファイルが、下記の使い方のページよりダウンロードできるので、簡単に試すことができます。
word2vec を試してみる
今回はWeb文書を適当にクローリングしてきて、ベクトル表現を獲得してみます。
- news.yahoo.co.jp をシードにして、Webページをクローリング(PythonでN-gramで書いたコードを利用)
- BeautifulSoupでHTMLをParseして、MeCabで分かち書きます。
- word2vecでモデルを学習します。
1. Web文書のクロール
http://news.yahoo.co.jp をシードにして、深さ2のURLを収集。
本文書執筆時点で、184URLを取得し、jsonでダンプします。
具体的な実装は、PythonでN-gramを参照されたい。
2. MeCabで分かち書き
すべてのURLから文書を取得し、分かち書きしたのちに、ファイルに保存しています。
# coding: utf-8 import sys import json import MeCab import urllib2 from collections import defaultdict from operator import itemgetter from BeautifulSoup import * class HTMLParser(): def get(self, url): try: c = urllib2.urlopen(url) except: print "Could not open %s" % url return "" soup = BeautifulSoup(c.read()) text = '\n'.join(self.__getNavigableStrings(soup)) return text def __getNavigableStrings(self, soup): if isinstance(soup, NavigableString): if type(soup) not in (Comment, Declaration) and soup.strip(): yield soup elif soup.name not in ('script', 'style'): for c in soup.contents: for g in self.__getNavigableStrings(c): yield g if __name__ == "__main__": f = open("urls.json", "r") urls = json.load(f) f.close() print "Count of urls : " + str(len(urls)) STOP_WORD = " 。 、 「 」 ( ) ? ? : , , . ! ! # $ % & ' ( ) = ~ | ` { } * + ? _ > [ ] @ : ; / . ¥ ^ 【 】 ¥ _ / 『 』 > ? _ * + ` | 〜 * + > ? # ” # $ % & ’ \" ・".split() hp = HTMLParser() tagger = MeCab.Tagger("-O wakati") wakati_text = [] for url in urls: text = hp.get(url) wakati_raw = tagger.parse(text.encode('utf-8')) wakati_formalize = [] for row in wakati_raw.split('\n'): row = row.rstrip().lower() for sw in STOP_WORD: row = row.replace(sw, '') wakati_formalize.append(row) wakati_text.append(' '.join(wakati_formalize)) f = open("wakati_text.txt", 'w') f.write('\n'.join(wakati_text)) f.close()
3. word2vecでベクトル表現の獲得
次のように訓練を行うと、python interface の使い方 のページと同様に、cosineやanalogyを計算できます。
$python > import word2vec # Training > word2vec.word2vec('wakati_text.txt', 'wakati_text.bin', size=300, verbose=True) # Predictions > model = word2vec.load('wakati_text.bin') > model.cosine('ブラジル')
実際に計算してみた
計算用のプログラム
#coding: utf-8 import word2vec def preprocess(): word2vec.word2vec('wakati_text.txt', 'wakati_text.bin', size=300, verbose=True) def echo(label, tlist): print "==============================" print label print '------------------------------' for x in tlist: print x[0], x[1] if __name__ == "__main__": # run only onece preprocess() model = word2vec.load('wakati_text.bin') key = 'yahoo' tlist = model.cosine(key)[key] echo(key, tlist) key = 'ブラジル' tlist = model.cosine(key)[key] echo(key, tlist)
出力結果
============================== yahoo ------------------------------ japan 0.745264967594 reserved 0.724144233549 corporation 0.721037877617 rights 0.715372173546 翻訳 0.711609108271 all 0.706314503681 エンタメニュース 0.672859051865 c 0.658193204546 wordleaf 0.654228512159 copyright 0.646804855186 ============================== ブラジル ------------------------------ ドイツ 0.796991224026 W杯 0.78659559174 大会 0.779938226056 アルゼンチン 0.771944338876 サッカー 0.748730168265 杯 0.746366345547 w 0.743728945102 戦 0.743359927214 準決勝 0.726702564219 オランダ 0.717565046411
考察
Yahooと共起しやすいJapanだったり、ブラジルの準決勝対戦国のドイツが上位にきたり、類似するベクトル表現はそれなりにとれてる気がします。
意味的な計算もできるのですが、あまり良い例が見つけられなかったので割愛。
データ量が少な過ぎて、上手く学習できていない感も否めない。
おわりに
word2vec を使うだけなら非常に簡単に利用できます。
一方で、その内部を理解するためには、前提となる知識が必要になります。
O'Reilly Japanから、word2vecによる自然言語処理が出版されているので読んでみたい。
関連記事