今一番熱い形態素解析エンジンといえば、やっぱりJUMAN++ですよね!
(参考1)JUMAN++ - 黒橋・河原研究室 - 京都大学
(参考2)新形態素解析器JUMAN++を触ってみたけど思ったより高精度でMeCabから乗り換えようかと思った話
少しだけ使ってみましたが、状況次第ではmecab + NEologdを凌駕する精度が出そうだと感じました。
JUMAN++にはpythonラッパーが提供されていますが、今回はrubyから利用する方法を試してみました。
[17年1月24日追記]Gemとして公開してみました。
GitHub:EastResident/jumanpp_ruby
環境
OS: Mac OS X EI Capitan
Ruby: 2.3.1
前提
前提として、当然ではありますがJUMAN++が入っている環境である必要があります。Macならhomebrewで一発なので、それが一番簡単かと思います。
brew install jumanpp
jumanpp
コマンドが実行できる環境なら問題ありません。
ラッパーモジュールの定義
方法は色々あるかと思いますが、今回はごくシンプルな方法で実装しました。
# coding: utf-8
require 'open3'
module Jumanpp
def parse(sentents)
o, = Open3.capture2("echo \"#{sentents}\" \| jumanpp --force-single-path")
sep_words = o.split("\n").map { |r| r.delete("\\").delete("\"")}
if block_given?
sep_words.each do |word|
yield word.split
end
else
sep_words.map { |word| word.split.first }
end
end
module_function :parse
end
使用例
Jumanpp.parse
メソッドは、引数に与えた文字列を形態素に分解し、表層形のみを一次元配列の形で返します。ざっくり言うと、分かち書きして配列で返すだけです。
p Jumanpp.parse('モビルスーツの性能の違いが、戦力の決定的差でないということを教えてやる')
出力結果
["モビルスーツ", "の", "性能", "の", "違い", "が", "、", "戦力", "の", "決定", "的", "差", "で", "ない", "と", "いう", "こと", "を", "教えて", "やる", "EOS"]
さらに、Jumanpp.parse
メソッドにブロックを与えることで、構文解析結果を形態素ごとにイテレートして処理することも可能です。
Jumanpp.parse('ララァ…私を導いてくれ!') do |word|
p word
end
出力結果
["ララァ", "ララァ", "ララァ", "名詞", "6", "普通名詞", "1", "*", "0", "*", "0", "自動獲得:Wikipedia", "Wikipediaリダイレクト:ララァ・スン"]
["…", "…", "…", "特殊", "1", "記号", "5", "*", "0", "*", "0", "NIL"]
["私", "わたし", "私", "名詞", "6", "普通名詞", "1", "*", "0", "*", "0", "代表表記:私/わたし", "漢字読み:訓", "カテゴリ:人"]
["を", "を", "を", "助詞", "9", "格助詞", "1", "*", "0", "*", "0", "NIL"]
["導いて", "みちびいて", "導く", "動詞", "2", "*", "0", "子音動詞カ行", "2", "タ系連用テ形", "14", "代表表記:導く/みちびく"]
["くれ", "くれ", "くれる", "接尾辞", "14", "動詞性接尾辞", "7", "母音動詞", "1", "基本連用形", "8", "代表表記:くれる/くれる"]
["!", "!", "!", "特殊", "1", "記号", "5", "*", "0", "*", "0", "NIL"]
["EOS"]
ひらがなだけの文章でも試してみました。
# 認めたくないものだな、自分自身の若さ故の過ちというものを
Jumanpp.parse('みとめたくないものだな、じぶんじしんのわかさゆえのあやまちというものを') do |word|
p word
end
出力結果
["みとめ", "みとめ", "みとめる", "動詞", "2", "*", "0", "母音動詞", "1", "基本連用形", "8", "代表表記:認める/みとめる", "補文ト"]
["たく", "たく", "たい", "接尾辞", "14", "形容詞性述語接尾辞", "5", "イ形容詞アウオ段", "18", "基本連用形", "7", "代表表記:たい/たい"]
["ない", "ない", "ない", "接尾辞", "14", "形容詞性述語接尾辞", "5", "イ形容詞アウオ段", "18", "基本形", "2", "代表表記:ない/ない"]
["もの", "もの", "もの", "名詞", "6", "形式名詞", "8", "*", "0", "*", "0", "NIL"]
["だ", "だ", "だ", "判定詞", "4", "*", "0", "判定詞", "25", "基本形", "2", "NIL"]
["な", "な", "な", "助詞", "9", "終助詞", "4", "*", "0", "*", "0", "NIL"]
["、", "、", "、", "特殊", "1", "読点", "2", "*", "0", "*", "0", "NIL"]
["じぶん", "じぶん", "じぶん", "名詞", "6", "時相名詞", "10", "*", "0", "*", "0", "代表表記:時分/じぶん", "カテゴリ:時間"]
["じしん", "じしん", "じしん", "名詞", "6", "普通名詞", "1", "*", "0", "*", "0", "代表表記:自身/じしん", "カテゴリ:人"]
["の", "の", "の", "助詞", "9", "接続助詞", "3", "*", "0", "*", "0", "NIL"]
["わか", "わか", "わかい", "形容詞", "3", "*", "0", "イ形容詞アウオ段", "18", "語幹", "1", "代表表記:若い/わかい"]
["さ", "さ", "さ", "接尾辞", "14", "名詞性述語接尾辞", "1", "*", "0", "*", "0", "代表表記:さ/さ", "準内容語", "カテゴリ:抽象物;数量"]
["ゆえ", "ゆえ", "ゆえ", "名詞", "6", "副詞的名詞", "9", "*", "0", "*", "0", "代表表記:故/ゆえ"]
["の", "の", "の", "助詞", "9", "接続助詞", "3", "*", "0", "*", "0", "NIL"]
["あやまち", "あやまち", "あやまち", "名詞", "6", "普通名詞", "1", "*", "0", "*", "0", "代表表記:過ち/あやまちv", "連用形名詞化:形態素解析"]
["と", "と", "と", "助詞", "9", "格助詞", "1", "*", "0", "*", "0", "NIL"]
["いう", "いう", "いう", "動詞", "2", "*", "0", "子音動詞ワ行", "12", "基本形", "2", "代表表記:言う/いう", "補文ト"]
["もの", "もの", "もの", "名詞", "6", "形式名詞", "8", "*", "0", "*", "0", "NIL"]
["を", "を", "を", "助詞", "9", "格助詞", "1", "*", "0", "*", "0", "NIL"]
["EOS"]
最後に
とりあえずRubyから処理できるようにしてみました。mecabと性能比較して、自分の目的に合うようなら本格的に使ってみたいですねー。