rubyで形態素解析をやってみる

# これはなに？
駆け出しrubyエンジニアのnyagato_00です。
業務で、メルマガやコメント内容などから、どのような単語が多く頻出してるのか調べるために、rubyで形態素解析を行う方法をまとめました。

# 形態素解析とは
文字列を形態素、すなわち意味を持つ最小の単位にまで分割する技術のことです。
英語などの文章は、単語ごとに分かれていますが、日本語などは句読点以外は連結した文章ですよね。例えば、日本語を習得している方は、与えられた文章から単語を選別することができますが、コンピュータの場合そう簡単にはいきません。
例えば、文字列の解析をする前段処理として、まず形態素解析を行い頻出単語などを抽出しこれらの意味から良い感情の文章かそうでないかなど判断させたりします。

こちらの記事に直近の形態素解析ツールについてまとめられていました。
https://qiita.com/sugiyamath/items/69047b6667256034fa5e
今回は、`MeCab`と`Juman++`の2つを使って形態素解析を行ってみました。

# MeCabを使った形態素解析

## MeCabとは
MeCabは 京都大学情報学研究科−日本電信電話株式会社コミュニケーション科学基礎研究所 共同研究ユニットプロジェクトを通じて開発されたオープンソース 形態素解析エンジンです。 
http://taku910.github.io/mecab/

## ダウンロード
こちらから、MeCabと辞書をダウンロードします。
http://taku910.github.io/mecab/#download

## インストール

MeCabのインストール

```
$ tar zxfv mecab-0.996.tar
$ cd mecab-0.996
$ ./configure 
$ make
$ make check
$ sudo make install
```

辞書のインストール

```
$ tar zxfv mecab-ipadic-2.7.0-20070801.tar
$ cd mecab-ipadic-2.7.0-20070801
$ ./configure --with-charset=utf8
$ make
$ sudo make install
```

## 形態素解析をしてみる
```
$ mecab
公務員だぞ、地方公務員。お前達が乗車しているのはグレートマジンガーか？ ダンガイオーか？ 自閉症児や不良少年が主人公のロボットアニメじゃないんだよ。分かっとるのか？ 本当に
公務員	名詞,一般,*,*,*,*,公務員,コウムイン,コームイン
だ	助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ
ぞ	助詞,終助詞,*,*,*,*,ぞ,ゾ,ゾ
、	記号,読点,*,*,*,*,、,、,、
地方	名詞,一般,*,*,*,*,地方,チホウ,チホー
公務員	名詞,一般,*,*,*,*,公務員,コウムイン,コームイン
。	記号,句点,*,*,*,*,。,。,。
お前	名詞,代名詞,一般,*,*,*,お前,オマエ,オマエ
達	名詞,接尾,一般,*,*,*,達,タチ,タチ
が	助詞,格助詞,一般,*,*,*,が,ガ,ガ
乗車	名詞,サ変接続,*,*,*,*,乗車,ジョウシャ,ジョーシャ
し	動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て	助詞,接続助詞,*,*,*,*,て,テ,テ
いる	動詞,非自立,*,*,一段,基本形,いる,イル,イル
の	名詞,非自立,一般,*,*,*,の,ノ,ノ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
グレートマジンガー	名詞,一般,*,*,*,*,*
か	助詞,副助詞／並立助詞／終助詞,*,*,*,*,か,カ,カ
？	記号,一般,*,*,*,*,？,？,？
ダンガイオー	名詞,一般,*,*,*,*,*
か	助詞,副助詞／並立助詞／終助詞,*,*,*,*,か,カ,カ
？	記号,一般,*,*,*,*,？,？,？
自閉症	名詞,一般,*,*,*,*,自閉症,ジヘイショウ,ジヘイショー
児	名詞,接尾,一般,*,*,*,児,ジ,ジ
や	助詞,並立助詞,*,*,*,*,や,ヤ,ヤ
不良	名詞,形容動詞語幹,*,*,*,*,不良,フリョウ,フリョー
少年	名詞,一般,*,*,*,*,少年,ショウネン,ショーネン
が	助詞,格助詞,一般,*,*,*,が,ガ,ガ
主人公	名詞,一般,*,*,*,*,主人公,シュジンコウ,シュジンコー
の	助詞,連体化,*,*,*,*,の,ノ,ノ
ロボット	名詞,一般,*,*,*,*,ロボット,ロボット,ロボット
アニメ	名詞,一般,*,*,*,*,アニメ,アニメ,アニメ
じゃ	助詞,副助詞,*,*,*,*,じゃ,ジャ,ジャ
ない	助動詞,*,*,*,特殊・ナイ,基本形,ない,ナイ,ナイ
ん	名詞,非自立,一般,*,*,*,ん,ン,ン
だ	助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ
よ	助詞,終助詞,*,*,*,*,よ,ヨ,ヨ
。	記号,句点,*,*,*,*,。,。,。
分かっ	動詞,自立,*,*,五段・ラ行,連用タ接続,分かる,ワカッ,ワカッ
とる	動詞,非自立,*,*,五段・ラ行,基本形,とる,トル,トル
の	名詞,非自立,一般,*,*,*,の,ノ,ノ
か	助詞,副助詞／並立助詞／終助詞,*,*,*,*,か,カ,カ
？	記号,一般,*,*,*,*,？,？,？
本当に	副詞,一般,*,*,*,*,本当に,ホントウニ,ホントーニ
EOS
```

## rubyからMeCabを使う
インストールしたMeCabを使うことができましたので、rubyから呼び出してみたいと思います。
今回は、`natto`というgemを使用します。
https://github.com/buruzaemon/natto

```
$ gem install natto
```

```rb
# -*- coding: utf-8 -*-

require 'natto'

txt = '公務員だぞ、地方公務員。お前達が乗車しているのはグレートマジンガーか？ ダンガイオーか？ 自閉症児や不良少年が主人公のロボットアニメじゃないんだよ。分かっとるのか？ 本当に'

natto = Natto::MeCab.new
natto.parse(txt) do |n|
  puts "#{n.surface}: #{n.feature}"
end
```

実行時間は以下のとおりです。
real	0m0.478s
user	0m0.154s
sys	0m0.085s

# Juman++を使った形態素解析

## Juman++とは
言語モデルとして Recurrent Neural Network Language Model(RNNLM)を利用した高性能な形態素解析システムです。
http://nlp.ist.i.kyoto-u.ac.jp/index.php?JUMAN++

## ダウンロードとインストール
homebrewを使ってインストールしてみます。

```
$ brew install jumanpp
```

バージョンを確認してインストールできたか確認しましょう。

```
$ jumanpp -v
JUMAN++ 1.02 
```

## 形態素解析をしてみる

```
$ cat test.txt | jumanpp 
公務 こうむ 公務 名詞 6 普通名詞 1 * 0 * 0 "代表表記:公務/こうむ カテゴリ:抽象物 ドメイン:政治"
員 いん 員 名詞 6 普通名詞 1 * 0 * 0 "代表表記:員/いん 漢字読み:音 カテゴリ:人"
だ だ だ 判定詞 4 * 0 判定詞 25 基本形 2 NIL
ぞ ぞ ぞ 助詞 9 終助詞 4 * 0 * 0 NIL
、 、 、 特殊 1 読点 2 * 0 * 0 NIL
地方 ちほう 地方 名詞 6 普通名詞 1 * 0 * 0 "代表表記:地方/ちほう 地名末尾 カテゴリ:場所-その他"
公務 こうむ 公務 名詞 6 普通名詞 1 * 0 * 0 "代表表記:公務/こうむ カテゴリ:抽象物 ドメイン:政治"
員 いん 員 名詞 6 普通名詞 1 * 0 * 0 "代表表記:員/いん 漢字読み:音 カテゴリ:人"
。 。 。 特殊 1 句点 1 * 0 * 0 NIL
お前 おまえ お前 名詞 6 普通名詞 1 * 0 * 0 "代表表記:御前/おまえ カテゴリ:人"
達 たち 達 接尾辞 14 名詞性名詞接尾辞 2 * 0 * 0 "代表表記:達/たち"
が が が 助詞 9 格助詞 1 * 0 * 0 NIL
乗車 じょうしゃ 乗車 名詞 6 サ変名詞 2 * 0 * 0 "代表表記:乗車/じょうしゃ カテゴリ:抽象物 ドメイン:交通 反義:名詞-サ変名詞:下車/げしゃ;名詞-サ変名詞:降車/こうしゃ"
して して する 動詞 2 * 0 サ変動詞 16 タ系連用テ形 14 "代表表記:する/する 付属動詞候補（基本） 自他動詞:自:成る/なる"
いる いる いる 接尾辞 14 動詞性接尾辞 7 母音動詞 1 基本形 2 "代表表記:いる/いる"
の の の 名詞 6 形式名詞 8 * 0 * 0 NIL
は は は 助詞 9 副助詞 2 * 0 * 0 NIL
グレート グレート グレート 名詞 6 普通名詞 1 * 0 * 0 "自動獲得:Wikipedia Wikipedia多義"
マジンガー マジンガー マジンガー 名詞 6 普通名詞 1 * 0 * 0 "自動獲得:Wikipedia Wikipediaリダイレクト:マジンガーＺ"
か か か 助詞 9 接続助詞 3 * 0 * 0 NIL
？ ？ ？ 特殊 1 記号 5 * 0 * 0 NIL
\  \  \  特殊 1 空白 6 * 0 * 0 "代表表記: / "
ダンガイオー ダンガイオー ダンガイオー 名詞 6 普通名詞 1 * 0 * 0 "自動獲得:Wikipedia Wikipediaリダイレクト:破邪大星ダンガイオー"
か か か 助詞 9 接続助詞 3 * 0 * 0 NIL
？ ？ ？ 特殊 1 記号 5 * 0 * 0 NIL
\  \  \  特殊 1 空白 6 * 0 * 0 "代表表記: / "
自閉症 じへいしょう 自閉症 名詞 6 普通名詞 1 * 0 * 0 "代表表記:自閉症/じへいしょう カテゴリ:抽象物 ドメイン:健康・医学"
児 じ 児 名詞 6 普通名詞 1 * 0 * 0 "代表表記:児/じ 漢字読み:音 カテゴリ:人"
や や や 助詞 9 接続助詞 3 * 0 * 0 NIL
不良 ふりょう 不良だ 形容詞 3 * 0 ナ形容詞 21 語幹 1 "代表表記:不良だ/ふりょうだ"
少年 しょうねん 少年 名詞 6 普通名詞 1 * 0 * 0 "代表表記:少年/しょうねん カテゴリ:人"
が が が 助詞 9 格助詞 1 * 0 * 0 NIL
主人公 しゅじんこう 主人公 名詞 6 普通名詞 1 * 0 * 0 "代表表記:主人公/しゅじんこう 〜を〜に構成語 カテゴリ:人 ドメイン:文化・芸術"
の の の 助詞 9 接続助詞 3 * 0 * 0 NIL
ロボット ろぼっと ロボット 名詞 6 普通名詞 1 * 0 * 0 "代表表記:ロボット/ろぼっと カテゴリ:人工物-その他 ドメイン:科学・技術"
アニメ あにめ アニメ 名詞 6 普通名詞 1 * 0 * 0 "代表表記:アニメ/あにめ カテゴリ:抽象物 ドメイン:文化・芸術"
じゃ じゃ だ 判定詞 4 * 0 判定詞 25 ダ列タ系連用ジャ形 14 NIL
ない ない ない 接尾辞 14 形容詞性述語接尾辞 5 イ形容詞アウオ段 18 基本形 2 "代表表記:ない/ない"
んだ んだ んだ 助動詞 5 * 0 ナ形容詞 21 基本形 2 NIL
よ よ よ 助詞 9 終助詞 4 * 0 * 0 NIL
。 。 。 特殊 1 句点 1 * 0 * 0 NIL
分かっ わかっ 分かる 動詞 2 * 0 子音動詞ラ行 10 タ接連用形 9 "代表表記:分かる/わかる"
とる とる とる 動詞 2 * 0 子音動詞ラ行 10 基本形 2 "代表表記:盗る/とる"
@ とる とる とる 動詞 2 * 0 子音動詞ラ行 10 基本形 2 "代表表記:撮る/とる ドメイン:文化・芸術 自他動詞:自:撮れる/とれる"
@ とる とる とる 動詞 2 * 0 子音動詞ラ行 10 基本形 2 "代表表記:摂る/とる ドメイン:料理・食事 自他動詞:自:摂れる/とれる"
@ とる とる とる 動詞 2 * 0 子音動詞ラ行 10 基本形 2 "代表表記:採る/とる 自他動詞:自:採れる/とれる"
@ とる とる とる 動詞 2 * 0 子音動詞ラ行 10 基本形 2 "代表表記:捕る/とる 自他動詞:自:捕れる/とれる"
@ とる とる とる 動詞 2 * 0 子音動詞ラ行 10 基本形 2 "代表表記:執る/とる ドメイン:政治"
@ とる とる とる 動詞 2 * 0 子音動詞ラ行 10 基本形 2 "代表表記:取る/とる 自他動詞:自:取れる/とれる 付属動詞候補（基本）"
の の のだ 助動詞 5 * 0 ナ形容詞 21 語幹 1 NIL
か か か 助詞 9 接続助詞 3 * 0 * 0 NIL
？ ？ ？ 特殊 1 記号 5 * 0 * 0 NIL
\  \  \  特殊 1 空白 6 * 0 * 0 "代表表記: / "
本当に ほんとうに 本当だ 形容詞 3 * 0 ナノ形容詞 22 ダ列基本連用形 8 "代表表記:本当だ/ほんとうだ 反義:名詞-普通名詞:嘘/うそ"
EOS
```
## rubyからJuman++を使う
Juman++も使うことができましたので、rubyから呼び出してみたいと思います。
今回は、`jumanpp_ruby`というgemを使用します。
https://github.com/EastResident/jumanpp_ruby

```
$ gem install jumanpp_ruby
```


```rb
# -*- coding: utf-8 -*-
require 'jumanpp_ruby'

juman = JumanppRuby::Juman.new(force_single_path: :true)
p juman.parse('公務員だぞ、地方公務員。お前達が乗車しているのはグレートマジンガーか？ ダンガイオーか？ 自閉症児や不良少年が主人公のロボットアニメじゃないんだよ。分かっとるのか？ 本当に')
```

実行時間は以下のとおりです。
real	0m9.326s
user	0m0.145s
sys	0m0.123s

MeCabよりも実行時間がかかるようですね。インスタンスの生成に時間がかかっているようでしたので、キャッシュしてしまえばそれほど実行時間は長くなさそうです。
ただ、RNNを使っているため形態素解析のもっともらしさは、MeCabよりありますね。

# まとめ
`MeCab`と`Juman++`の2つを用いて形態素解析してみました。どちらも容易に使うことができ既存のアプリケーションへ組み込みやすそうでした。
実行時間については、MeCabに軍配が上がりましたので、ひとまず既存データでの形態素解析を行って頻出単語を調べるなどの場合には、こちらを使用するのが良いかもしれません。

しかし、精度的にはJuman++のほうが良さそうに思えましたので、高速化に期待したいところであります。