1. hsoccer

    Posted

    hsoccer
Changes in title
+gensimによるword2vecの利用例
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,294 @@
+# はじめに
+最近、単語の分散表現を学び、使ったので、その際に得た知識をまとめておく。
+この記事では、MeCab、gensimを用いて、夏目漱石の『こころ』に登場する単語の類似度を計算する。
+GitHubにこの記事で使用する全てのコードをアップしてある。
+https://github.com/hsoccer/my_word2vec
+# 環境
+OS X El Capitan
+Python3系
+# MeCabのインストール
+以下のページ等を参考にインストールする。
+追加の辞書であるmecab-ipadic-neologdもインストールしておく。
+https://qiita.com/taroc/items/b9afd914432da08dafc8
+# MeCabの辞書を強化する
+デフォルトの辞書では弱いので、Wikipediaの見出語を全て辞書に加えておく。
+user.dicという名前で保存しておく。
+以下のページが非常に参考になる。
+http://aidiary.hatenablog.com/entry/20101230/1293691668
+# gensimのインストール
+ターミナル上で`$ pip install gensim`でうまくいくはず。
+# データのダウンロード
+以下の青空文庫のサイトから『こころ』のzipファイルをダウンロードして、これ以降で書いていくコードと同じファイルに入れておく。
+http://www.aozora.gr.jp/cards/000148/card773.html
+zipファイルを解凍すると、全文のテキストファイルになる(kokoro.txtで保存)が、本文の前後に不要な部分があるので、削除しておく。(しなくてもそんなに変わらないかもしれない。)
+# 本文を分かち書きする
+分かち書きとは、各文章を文節ごとに分けた形で書きなおすことである。
+もしイメージが湧かなければ、調べるか、以下のコードを実行してみると良い。
+
+```python3:Wakati.py
+import MeCab
+
+class Wakati(object):
+
+ """
+ ==========================================================
+ ファイルに存在する文章を指定の辞書を用いてMeCabによって形態素に分ける
+ ==========================================================
+ 【関数説明】
+ __init__ : コンストラクタ
+ wakati : 文章を分かち書きする
+ output : 結果をファイルに出力する
+ """
+
+ def __init__(self, file_dir, dic_dir, user_dir=None, hinshis=["動詞", "形容詞", "形容動詞", "助動詞"]):
+ """
+ ==========================================================
+ コンストラクタ
+ ==========================================================
+ 【変数説明】
+ file_dir : 入力となる文章のディレクトリ
+ dic_dir : システム辞書のディレクトリ([ex] /usr/local/lib/mecab/dic/mecab-ipadic-neologd)
+ user_dir : ユーザー辞書のディレクトリ([ex] /Users/PCのユーザ名/...)
+ hinshis : 活用する語
+ tagger : MeCab用のtagger(詳細はMeCab自体のドキュメント参照)
+ f : 入力ファイル
+ splited_text : 各行を分かち書きしたもの(splited_line)をリストで格納する(Noneで初期化)
+ out_dir : 出力ファイルのディレクトリ(Noneで初期化)
+ """
+ if user_dir is not None:
+ self.tagger = MeCab.Tagger("mecabrc -d {} -u {}".format(dic_dir, user_dir))
+ else:
+ self.tagger = MeCab.Tagger("mecabrc -d {}".format(dic_dir))
+ self.f = open(file_dir, 'r')
+ self.hinshis = hinshis
+ self.splited_text = None
+ self.out_dir = None
+
+ def wakati(self):
+ """
+ ==========================================================
+ 文章全体を分かち書きし、self.splited_textに格納する
+ その際、活用された語については原形に直す
+ ==========================================================
+ 【変数説明】
+ line : 入力文章の一行(更新されていく)
+ splited_line : 各行の文章を分かち書きしたもののリスト
+ node : 各単語のノード
+ word : 単語
+ feature : 単語の情報
+ hinshi : 品詞
+ kata : 活用形
+ genkei : 原形
+ """
+ line = self.f.readline()
+ splited_text = []
+ while line:
+ node = self.tagger.parseToNode(line).next
+ splited_line = []
+ while node.surface:
+ word = node.surface
+ feature = node.feature.split(',')
+ hinshi = feature[0]
+ kata = feature[5]
+ genkei = feature[6]
+ if hinshi in self.hinshis:
+ if kata != "基本形":
+ word = genkei
+ splited_line.append(word)
+ node = node.next
+ splited_text.append(splited_line)
+ line = self.f.readline()
+ self.splited_text = splited_text
+ self.f.close()
+
+ def output(self, out_dir):
+ """
+ ==========================================================
+ self.splited_textをファイルに出力する
+ ==========================================================
+ 【変数説明】
+ out_dir : 出力ファイルのディレクトリ
+ fout : 出力ファイル
+ """
+ assert self.splited_text is not None
+ if self.out_dir is None:
+ self.out_dir = out_dir
+ self.fout = open(self.out_dir, 'w')
+ for line in self.splited_text:
+ self.fout.write(" ".join(line) + " ")
+ self.fout.close()
+```
+
+```python3:wakati_text.py
+from Wakati import Wakati
+
+w = Wakati("kokoro.txt", システム辞書のディレクトリ, ユーザ辞書のディレクトリ)
+w.wakati()
+w.output("out.txt")
+```
+
+上記のコードを実行すると、out.txtという名前で分かち書きされたテキストファイルができる。
+これを見ればやっていることが分かると思う。
+# word2vecを使って単語をベクトル化する
+先ほどインストールしたgensimというライブラリを用いて、『こころ』に登場する単語を分散表現する。
+
+```python3:Vectorizer.py
+from Wakati import Wakati
+from gensim.models import word2vec
+import logging
+
+class Vectorizer(Wakati):
+
+ """
+ ==========================================================
+ Wakatiをベースとして、分かち書きしたものを学習して分散表現する
+ ==========================================================
+ 【関数説明】
+ __init__ : コンストラクタ
+ vectorize : 分散表現を作る
+ _train : gensimを使ってword2vecする
+ save_model : 作ったモデルを保存する
+ """
+
+ def __init__(self, file_dir, dic_dir, user_dir=None, out_dir="out.txt", hinshis=["動詞", "形容詞", "形容動詞", "助動詞"]):
+ """
+ ==========================================================
+ コンストラクタ
+ Wakatiを使って文章を分かち書きしておく
+ ==========================================================
+ 【変数説明】
+ file_dir : 入力となる文章のディレクトリ
+ dic_dir : システム辞書のディレクトリ(/usr/local/lib/mecab/dic/mecab-ipadic-neologd)
+ user_dir : ユーザー辞書のディレクトリ(/Users/konoharuki/Desktop/word2vec/user.dic)
+ out_dir : 分かち書きされた文章のファイルのディレクトリ
+ hinshis : 活用する語
+ model : モデル(Noneで初期化)
+ """
+ Wakati.__init__(self, file_dir, dic_dir, user_dir, hinshis)
+ self.out_dir = out_dir
+ self.model = None
+ self.wakati()
+ self.output(self.out_dir)
+
+ def vectorize(self, other_file_dir=None, sg=1, size=300, min_count=10, window=5, hs=0, negative=15, iter=15):
+ """
+ ==========================================================
+ 単語の分散表現を作成
+ ==========================================================
+ 【変数説明】
+ out_dir : 分かち書きされた文章のファイル
+ other_file_dir : out_dirを使わない場合のファイル名(Noneで初期化)
+ sentences : 分かち書きされ、空白区切の文章全文
+ """
+ logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
+ if other_file_dir is None:
+ sentences = word2vec.Text8Corpus(self.out_dir)
+ else:
+ sentences = word2vec.Text8Corpus(other_file_dir)
+ self._train(sentences, sg, size, min_count, window, hs, negative, iter)
+
+ def _train(self, sentences, sg=1, size=300, min_count=10, window=5, hs=0, negative=15, iter=15):
+ """
+ ==========================================================
+ gensimによる学習
+ ==========================================================
+ 【変数説明】
+ sentences : 分かち書きされ、空白区切の文章全文
+ word2vecの引数 : 詳細はgensimのドキュメント参照
+ """
+ self.model = word2vec.Word2Vec(sentences, sg=sg, size=size, min_count=min_count, window=window, hs=hs, negative=negative, iter=iter)
+
+ def save_model(self, model_dir):
+ """
+ ==========================================================
+ モデルを保存する
+ ==========================================================
+ 【変数説明】
+ model_dir : モデルを保存するディレクトリ
+ """
+ assert self.model is not None
+ self.model.save(model_dir)
+
+```
+# 『こころ』に登場する単語を解析
+ここまでで作ったモデルを用いて、『こころ』における「人間」という単語との類似度が高い単語をピックアップする。
+
+```python3:example.py
+from Vectorizer import Vectorizer
+
+"""
+==========================================================
+自作のWord2Vecを使って、夏目漱石の『こころ』を解析する。
+(『こころ』は青空文庫(http://www.aozora.gr.jp/cards/000148/card773.html)から
+ダウンロードして同じディレクトリに入れておく)
+==========================================================
+【関数説明】
+kokoro : 『こころ』のモデルを作成
+"""
+
+def kokoro():
+ """
+ ==========================================================
+ 『こころ』から作成されたモデルを返す
+ ==========================================================
+ 【変数説明】
+ dic_dir : システム辞書のディレクトリ(このPCでは"/usr/local/lib/mecab/dic/mecab-ipadic-neologd")
+ user_dir : ユーザー辞書のディレクトリ(このPCでは"/Users/konoharuki/Desktop/word2vec/user.dic")
+ """
+
+ #Vectorizerインスタンスを作成
+ #__init__で文章が分かち書きされたファイル(out.txt)が同じディレクトリ内に作られる。
+ v = Vectorizer(file_dir="kokoro.txt",\
+ dic_dir="/usr/local/lib/mecab/dic/mecab-ipadic-neologd",\
+ user_dir="/Users/konoharuki/Desktop/word2vec/user.dic")
+
+ #単語の分散表現のモデル作成
+ v.vectorize()
+
+ #できたモデルを返す
+ return v.model
+
+if __name__ == "__main__":
+
+ #『こころ』から作成されたモデル
+ model = kokoro()
+
+ #「人間」という単語に最も近い10単語を表示する
+ #resultは(単語, コサイン距離)からなるリスト
+ result = model.most_similar(positive="人間")
+ for pair in result:
+ word = pair[0]
+ distance = pair[1]
+ print(word, distance)
+
+ #完成したモデルを保存したければ以下のコードのコメントアウトを外す
+ #v.save_model("kokoro.model")
+
+```
+
+このコードを実行すると、(私の環境では)以下のような結果になった。
+
+```text:result.txt
+幸福 0.7769338488578796
+矛盾 0.7726519107818604
+愛 0.763999342918396
+変化 0.7608857154846191
+判断 0.7593793869018555
+嫌い 0.7444355487823486
+つまり 0.7347712516784668
+従妹 0.7326171398162842
+苦痛 0.718117892742157
+感じ 0.7159429788589478
+```
+
+本文を読まなくても、なかなか重そうな内容であることが一目で分かる。
+
+# 最後に
+この記事では、単に単語の類似度を調べただけだが、実際には、ベクトル化した単語からなる文章を時系列データとしてLSTMに突っ込む等応用が効く。
+最初にも書いたが、[https://github.com/hsoccer/my_word2vec](https://github.com/hsoccer/my_word2vec)にほぼ同じコードが載っているので、合わせてご覧いただければ幸いである。
+コードのコメントとして、極力丁寧に変数の説明を入れたつもりなので、いろいろと変更して、結果の違いを観察すると楽しい。
+# 参考
+https://qiita.com/taroc/items/b9afd914432da08dafc8
+http://aidiary.hatenablog.com/entry/20101230/1293691668
+http://www.aozora.gr.jp/cards/000148/card773.html
+https://radimrehurek.com/gensim/models/word2vec.html