deepblueインターン生の中山です。今回は前回の続きでColaboratory上のmecab実行を行ってみたいと思います。

対象の読者

  • python初学者
  • 自然言語処理初学者
  • Google Colaboratoryで実行したい方

mecab準備

Google Colaboratoryでは、ローカルで実行する場合と異なり、毎回mecabをinstallする必要があります。「mecabって何?」という方もいらっしゃると思いますが、何も考えず下記のコードを実行してください。

!apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 git curl python-mecab > /dev/null
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git > /dev/null
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n > /dev/null 2>&1
!pip install mecab-python3 > /dev/null
!echo mecab-config --dicdir"/mecab-ipadic-neologd"

上記コードをそのまま実行するとmecabの準備ができます。実行には、大体1分程度かかります。

mecabとは

まず、mecabについて簡潔に説明したいと思います。mecabは形態素解析を実行するために利用されます。形態素解析は文章を『意味を持つ最小限の単位』に分割することを意味します。
文章で説明しても中々理解しづらいと思うので、実際にPythonで実行してみます。

import MeCab
m = MeCab.Tagger('-Owakati')
result = m.parse('形態素解析を練習で実行してみる')
print(result)

形態素 解析 を 練習 で 実行 し て みる

これは『形態素解析を練習で実行してみる』という文章を形態素に分けたということになります。中学校の国語で習った単語切りに似ていると思います。上記のような半角スペースが含まれている文章を「分かち書き」と呼びます。
しかし、これだと『形態素解析』という語句が『形態素』と『解析』の2つに分割されています。分割する事も不正解ではないですが、『形態素解析』で1つの意味であり、極力分けずに取り扱いたいです。
そこで、「NEologd」という辞書を適用することで、この課題が解決します。実際には、NEologdを呼び出す必要がありますが、先に示したpip文に含まれているため、新たに読み込む必要はありません。
実際にNEologdを用いた形態素解析のコードは下記のようになります。

import MeCab
m = MeCab.Tagger('-Owakati -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd')
result = m.parse('形態素解析を練習で実行してみる')
print(result)

形態素解析 を 練習 で 実行 し て みる

『形態素解析』が分かれずに、1語句として認識されています。
上記のようにして、mecabでは形態素解析を実行できます。また、上記では分かち書きを示しましたが、各語句の品詞も調べることができます。品詞は、下記のようにコードを書くことで表示できます。

import MeCab
path = '-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd'
m = MeCab.Tagger(path)
result = m.parse('形態素解析を実行してみる')
print(result)

形態素解析 名詞,固有名詞,一般,*,*,*,形態素解析,ケイタイソカイセキ,ケイタイソカイセキ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
実行 名詞,サ変接続,*,*,*,*,実行,ジッコウ,ジッコー
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
みる 動詞,非自立,*,*,一段,基本形,みる,ミル,ミル
EOS

このように、形態素解析では品詞や読み方なども調べることができます。
ちなみに、「EOS」は「End Of Sentence」の略で、文末であることを意味します。

以上のようにして、mecabを実行することができます。word2vecでは、分かち書きしたテキストが必要となるため、全文章の分かち書きを行います。

形態素解析の実行

word2vecでは形態素を用いて学習を行います。しかし、word2vecを実行する上で、実行するべき前処理が2つあります。

  • 品詞を選択する
  • 原形に変換する

この2点です。この操作の理由を簡単に説明します。
1つ目の品詞を選択する点は、「名詞・動詞・形容詞」以外の品詞はあまり特徴がないためです。学習のとき無駄な影響を与えないために、それら語句を除去しました。
2つ目の原形に変換する点は、表記揺れを防ぐためです。例えば、『猫』と『ねこ』を別の単語として学習をしたくないため、それぞれの語句を原形に揃えます。
上記の操作を関数定義すると、このようになります。

import MeCab
def mecab_tokenizer(text):
    m = MeCab.Tagger("-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd")
    node = m.parseToNode(text)
    wordtype_list = ['名詞','動詞','形容詞']
    subtype_list = ["数", "非自立", "代名詞","接尾"]
    output = []
    while node:
        if node.surface != "":
            wordtype = node.feature.split(",")[0]
            subtype = node.feature.split(",")[1]
            original = node.feature.split(",")[6]
            if wordtype in wordtype_list and subtype not in subtype_list and original != '*':
                output.append(original)
        node = node.next
        if node is None:
            break
    return output

上記のコードによって、望み通りの形態素解析を行うための関数定義ができます。
試しに、前処理を含めた形態素解析を実行してみると、下記のようになります。

text = 'これで望み通りの形態素解析を行う関数定義ができます。'
mecab_tokenizer(text)

['望み', '形態素解析', '行う', '関数', '定義', 'できる']

うまく、助詞などが弾かれている事がわかると思います。

import pandas as pd
text_data = pd.read_table('吾輩は猫である_ルビなし.txt', encoding='cp932', header = None)
text_data.columns = ['text']
text_data['wakati'] = text_data['text'].apply(lambda x: ' '.join(mecab_tokenizer(x)))

これで、text_dataのdataframeが作成され、元のテキストと分かち書きの列が作成できました。text_dataを確認するとこのようになっています。

以上で形態素解析は完全に終了です。dataframeをcsv形式で保存したい方は下記コードを実行すると保存できます。

text_data.wakati.to_csv('wakati.csv', sep='\n', header=True, index=False)

word2vecの実行

形態素解析が完了したら、word2vecに入れて学習をするだけです。
Pythonで実装をすると下記のようになります。

from gensim.models import word2vec
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentence_data = word2vec.LineSentence('wakati.csv')
model_w2v = word2vec.Word2Vec(sentence_data,
                         sg=1,        # Skip-gram
                         size=200,    # 次元数
                         min_count=5, # min_count回未満の単語を破棄
                         window=5,    # 文脈の最大単語数
                         hs=1,        # 階層ソフトマックス(ネガティブサンプリングするなら0)
                         negative=5,  # ネガティブサンプリング
                         iter=50      # Epoch数
                         )

出力結果に関しては割愛します。
これで、学習が完了します。文章が短いので、30秒程度で学習が完了すると思います.
本当はパラメータの調整をするべきです。今回は、実行することに重きを置いたため割愛しております。より、精緻な学習をしたい方は、パラメータ調整を試してみてください。
word2vecを用いた類似度の測定は、下記コードのように行うことでできます.

s = input('類似度の計測をしたい単語を入力:')
try:
    results = model_w2v.most_similar(positive=[s], topn=20)
    for i, result in enumerate(results):
        print(i+1, '\t', '{0:.5f}'.format(result[1]), ' ', result[0])
except KeyError:
    print('{}が辞書の中に入っていない'.format(s))

類似度の計測をしたい単語を入力:人間
1 0.32748 動物
2 0.31177 衣服
3 0.30977 する
4 0.30956 歴史
5 0.30434 赤裸
6 0.30149 ある
7 0.29940 個人
8 0.29394 無能
9 0.28921 猫
10 0.28526 案出
11 0.27976 云う
12 0.27799 もっとも
13 0.27320 同一
14 0.27196 条件
15 0.27086 証明
16 0.26697 全能
17 0.26412 運命
18 0.26111 世の中
19 0.25952 乏しい
20 0.25749 生涯

類似度結果は上記のようになりました。人間が動物であることは間違いないですし、
吾輩が文章中で人間が服を着ていることに言及している点や、吾輩が人間を無能扱いしていることもうまく拾えているのではないでしょうか。
今回はこのような結果となりましたが、学習に乱数が用いられているため、皆さんが実行しても同じ結果にはなるとは限りません。

最後に、作成したモデルの保存についてです。下記コードを実行することで、モデルの保存をできます。ただ実際に実行するには、モデルを保存する前に、driveのマウントをする必要があります。

model_w2v.save('/content/drive/hoge/fuga/word2vec.model')

driveのマウントについて知りたい方は、こちらに記事を書いたのでご覧になってください。

まとめ

以上で、word2vecの利用は終了です。
word2vecの学習自体はそこまで大変ではないですが、word2vecより前処理の方が面倒くさいです。