Word2Vecとは
単語をベクトルで表現することで単語の意味をとらえる手法。単語をベクトルで表現することにより単語同士の意味の近さを計算したり、単語同士の意味を足したり引いたりするといったことができるようになります。
例:
王様= (0.8, 0.3, 0.1, 0.0)
王女= (0.7, 0.1, 0.4, 0.4)
男 = (0.1, 0.2, 0.2, 0.3)
女 = (0.1, 0.0, 0.6, 0.8)
王様 - 男 + 女 ≒ 王女
Word2Vecでは文中の各単語はその周辺に現れる単語と何らかの関係があるという考えをもとにある単語の周辺にはどのような単語が現れるかということを予測することで単語ベクトルを間接的に計算するそうです。
詳しい内容に関しては次のサイトの説明がわかりやすかったので載せておきます。
http://www.randpy.tokyo/entry/word2vec_skip_gram_model
https://qiita.com/Hironsan/items/11b388575a058dc8a46a
準備
Word2VecはPythonでgensimというライブラリを使うことで簡単に使うことができるらしいので以下のものをインストールしました。
・Anacond3 5.1.0(Python 3.6 version)
Python本体とよく使われるライブラリをまとめたPythonのパッケージ。
https://www.anaconda.com/download/ からインストーラーをダウンロードしてきて、実行するだけでPythonを利用する環境ができるので便利。
・gensim
Anacondaで用意したPython環境に追加でインストール。
・janome
これもAnacondaで用意したPython環境に追加でインストール。
これは文を分かち書き(単語で区切る)するためのライブラリでWord2Vecを行うのに必要。
例えば次のような文があった時
"私はモスのハンバーガーが好きです。"
これをjanomeを使って分かち書きして出力すると次のような結果が得られます。
from janome.tokenizer import Tokenizer
tokenizer = Tokenizer()
tokens = tokenizer.tokenize("私はモスのハンバーガーが好きです。")
for token in tokens:
print(token)
出力:
私 名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
モス 名詞,一般,*,*,*,*,モス,モス,モス
の 助詞,連体化,*,*,*,*,の,ノ,ノ
ハンバーガー 名詞,一般,*,*,*,*,ハンバーガー,ハンバーガー,ハンバーガー
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
好き 名詞,形容動詞語幹,*,*,*,*,好き,スキ,スキ
です 助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。 記号,句点,*,*,*,*,。,。,。
使い方
処理手順としては
1.テキストデータに対し単語の正規化や記号など文章に関係ないものの削除などの処理を行う
2.分かち書きを行い文章を単語で区切る
3.分かち書きしたテキストデータをgensimのword2vecで学習を行い、モデルを作成する
といった感じになります。
テキストデータに関しては青空文庫にある夏目漱石の小説「吾輩は猫である」を使用。
実装は下記の記事を参考にさせて頂きました。
https://qiita.com/makaishi2/items/63b7986f6da93dc55edd
テキストデータから文章に関係ない記号の削除などデータの整形に関しては利用するテキストデータによって異なるので省略してjanomeとWord2Vecを使っている部分を説明していきます。
#分かち書き
sentences = text.split(u"。")
tokenizer = Tokenizer()
word_list = []
for sentence in sentences:
tokens = tokenizer.tokenize(sentence)
temp_word = []
for token in tokens:
if token.part_of_speech.split(",")[0] in [u"名詞", u"動詞"]:
temp_word.append(token.base_form)
word_list.append(temp_word)
janomeを使った分かち書きはjanome.tokenizerのTokenizerクラスをインポートしてそのクラスのtokenizeメソッドを使うことで利用できます。tokenizeメソッドは引数に分かち書きしたい文字列を受け取って、その文字列に含まれる単語とその単語の原型や品詞や読みなどの情報をもつTokenオブジェクトのリストを返します。
上記のコードだとtoken.part_of_speechというのはその単語の品詞の表しており、token.base_formというのはその単語の原型を表します。
#学習とモデル作成
from gensim.models import word2vec
model = word2vec.Word2Vec(word_list, size=100,min_count=5,window=5,iter=100)
学習とモデルの作成は簡単でgensim.modelsのword2vecをインポートしてWord2Vecメソッドを使うだけでできます。各引数は次のようになっています。
size 単語をベクトル表現する際の次元数
min_count 出現回数がこの数以下の単語を学習に使うデータから省く
window 前後いくつまでの単語を周辺語として扱うか
iter 機械学習の繰り返し回数
できたモデルを使って単語の類似度を計算したり、単語同士の演算を行うことができます。
以下がそのコードと出力
#猫という単語を類似する単語の列挙
ret = model.wv.most_similar(positive=u'猫')
for item in ret:
print(item[0], item[1])
出力
吾輩 0.411365807056427
生涯 0.37118709087371826
己 0.34734046459198
動物 0.3392423391342163
大事 0.3345252275466919
南無阿弥陀仏 0.3320901095867157
野蛮 0.3135031461715698
ばってん 0.3095359206199646
好奇 0.30711501836776733
教師 0.3064701557159424
上にあるものほど猫という単語と属性などが近く類似度が高いとされたものになります。
横の数値はコサイン距離で-1~1の範囲で表され、1に近いものは類似度が高く、-1に近いものは類似度が低いものとなっています。
動物という単語が猫と類似度(関連)が高いのは納得できるし、学習に使ったテキストである「吾輩は猫である」が猫の視点で語られている物語であることを考えれば吾輩や己という単語が猫と類似するものというのもまぁわからなくはないかなといった感じです。ばってんとかよくわからないものもありますがこの小説における猫とは何かというのはなんとなく表せているように思います。
単語同士の演算は次のように行います。
#猫というた単語から吾輩という単語を引く
ret = model.wv.most_similar(positive=[u'猫'], negative=[u'吾輩'])
引数のpositiveに加算したい単語を、引数のnegativeに減算したい単語を指定することで単語の演算ができます。
ちなみにこの結果は
西洋 0.3285699188709259
書く 0.3183467388153076
男 0.2689993679523468
云 0.26029855012893677
質 0.2568047046661377
誂える 0.2560497522354126
手紙 0.24894019961357117
ざあ 0.24786314368247986
ざ 0.24679623544216156
観念 0.24620375037193298
となり、よくわからないものとなってしまいました。
最初はTwitterからデータを取得して、それを使って何かしようと思ったのですがTwitterのデータは前処理が大変そうだったので断念しました。機会があればもう一度挑戦してみたいと思います。
最後にWord2Vecに興味を持つきっかけとなった記事を載せておきます。
・ http://www.randpy.tokyo/entry/python_word2vec
こちらの記事はTwitterから集めたデータを用いて「マジ卍」とはどういう意味か調べさせています。
・http://ainow.ai/2017/10/31/124408/
こちらの記事では発言小町という掲示板から恋愛、結婚、離婚というカテゴリの投稿データを集めて学習させ、奥さんにあって彼女にないものは何かということを単語ベクトルを用いて計算させています。