巨人の肩の上に登る

先人の積み重ねた発見に基づいて、なにかを発見しようとすることを指す。

Python で word2vec を使ってみる

f:id:mayo_yamasaki:20140714161338p:plain

word2vecとは

単語のベクトル表現を獲得する一つの手法です。
word2vecの特徴としては、意味的な計算が可能な表現であるということです。
例えば次の式のように、kingのベクトルからmanのベクトルを差し引いたベクトルにwomanのベクトルを足し合わすことで、queenのベクトルと近似するベクトルが得られます。

king - man + woman = queen
事前知識として

word2vec install

pip でインストールできます。

$ sudo pip install word2vec

英語のモデルファイルが、下記の使い方のページよりダウンロードできるので、簡単に試すことができます。

word2vec を試してみる

今回はWeb文書を適当にクローリングしてきて、ベクトル表現を獲得してみます。

  1. news.yahoo.co.jp をシードにして、Webページをクローリング(PythonでN-gramで書いたコードを利用)
  2. BeautifulSoupでHTMLをParseして、MeCab分かち書きます。
  3. 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による自然言語処理が出版されているので読んでみたい。


関連記事