エイエイレトリック

なぐりがき

mecabの制約付き解析(部分解析)のエラー

mecabの制約付き解析(部分解析)を使おうとしたら結構詰まったのでエラーとその解決策についてメモします。

Logo frwl uk

前提知識

mecab--partial (-p) オプションを使うと辞書に登録されてない単語でも解析できます。

$ mecab
例えばコ↑レ↓を名詞にしたいときがある

例えば   接続詞,*,*,*,*,*,例えば,タトエバ,タトエバ
コ 名詞,一般,*,*,*,*,*
↑ 記号,一般,*,*,*,*,↑,↑,↑
レ 名詞,一般,*,*,*,*,レ,レ,レ
↓ 記号,一般,*,*,*,*,↓,↓,↓
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
名詞  名詞,一般,*,*,*,*,名詞,メイシ,メイシ
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
たい  助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ
とき  名詞,非自立,副詞可能,*,*,*,とき,トキ,トキ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
ある  動詞,自立,*,*,五段・ラ行,基本形,ある,アル,アル
EOS

$ mecab -p
例えば
コ↑レ↓    名詞
を名詞にしたいときがある
EOS

例えば   接続詞,*,*,*,*,*,例えば,タトエバ,タトエバ
コ↑レ↓    名詞,サ変接続,*,*,*,*,*
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
名詞  名詞,一般,*,*,*,*,名詞,メイシ,メイシ
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
たい  助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ
とき  名詞,非自立,副詞可能,*,*,*,とき,トキ,トキ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
ある  動詞,自立,*,*,五段・ラ行,基本形,ある,アル,アル
EOS

形態素を指定したい部分 (形態素断片) について 表層\t素性パターン の形で入力し、指定しない部分 (文断片) は改行をはさんでそのまま入力することで欲しい出力に合わせることができます。

詳しい使い方は mecabの公式ドキュメント (https://taku910.github.io/mecab/partial.html) を参考にしてください。

部分解析は、記号や数字のような正規表現で取得できる単語であれば辞書登録しなくても解析できるので便利です。

natto-pyでは boundary_constraints の引数があり、証券コードに対して制約付き解析を使う例が紹介されています。 (URL)

tokenizer.cpp(368) エラー

ところで、mecabの解析結果では基本的に半角スペースが無視されます。

$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd

007 ロシアより愛をこめての原題は "From Russia with Love"
007 名詞,固有名詞,人名,一般,*,*,007,ゼロゼロセブン,ゼロゼロセブン
ロシアより愛をこめて  名詞,固有名詞,一般,*,*,*,ロシアより愛をこめて,ロシアヨリアイヲコメテ,ロシアヨリアイオコメテ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
原題  名詞,一般,*,*,*,*,原題,ゲンダイ,ゲンダイ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
"   記号,一般,*,*,*,*,*
From    名詞,固有名詞,組織,*,*,*,*
Russia  名詞,固有名詞,一般,*,*,*,Russia,ロシア,ロシア
with    名詞,固有名詞,人名,一般,*,*,WITH,ウィズ,ウィズ
Love    名詞,固有名詞,人名,一般,*,*,LOVE,ラブ,ラブ
"   記号,一般,*,*,*,*,*
EOS

"From Russia with Love" の部分が半角スペースごと分割されました。 この場合、解析結果を元の文字列に復元するのが少々面倒ですし、英単語部分を細かく分割したくない気がします。 そこで、" で囲まれた英語部分を部分解析で1つの名詞扱い (形態素断片) にしてみます。

$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd -p

007 ロシアより愛をこめての原題は 
"From Russia with Love" 名詞
EOS

tokenizer.cpp(368) [new_node->feature]

エラーになってしまいました。 コードをみると解析中に新しいnodeを参照できないことが原因と思われます。 (tokenizer.cpp)

今回の入力の場合、原因は文断片 (制約のない部分) 007 ロシアより愛をこめての原題は の最後にある 半角スペース 。 これを削除すると問題なく実行できます。

$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd -p

007 ロシアより愛をこめての原題は
"From Russia with Love" 名詞
EOS

007 名詞,固有名詞,人名,一般,*,*,007,ゼロゼロセブン,ゼロゼロセブン
ロシアより愛をこめて  名詞,固有名詞,一般,*,*,*,ロシアより愛をこめて,ロシアヨリアイヲコメテ,ロシアヨリアイオコメテ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
原題  名詞,一般,*,*,*,*,原題,ゲンダイ,ゲンダイ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
"From Russia with Love" 名詞
EOS

実装の詳細までみてないので想像ですが、文断片(制約なし部分)→形態素断片(制約ありテキスト部分) の順に解析するとき、半角スペースを含んでいると、形態素断片を正しく取得できなくなってるんじゃないかなと思います。

形態素断片→文断片の順番であれば、文断片に半角スペースを含んでいても問題ないです。

制約ありテキスト列の 直前半角スペース があるときだけ起こるエラーです。

# 後ろの文字列の最初に半角スペース -> OK
"From Russia with Love" 名詞
 が原題
EOS

"From Russia with Love" 名詞
が 接続詞,*,*,*,*,*,が,ガ,ガ
原題  名詞,一般,*,*,*,*,原題,ゲンダイ,ゲンダイ
EOS

# 後ろの文字列の最後に半角スペース -> OK
"From Russia with Love" 名詞
が原題 
EOS

# 前の文字列の最後にタブ -> OK
原題は   
"From Russia with Love" 名詞
EOS

# 前の文字列の最後に全角スペース -> OK (※解析結果に `記号,空白` が含まれる)
原題は 
"From Russia with Love" 名詞
EOS

原題  名詞,一般,*,*,*,*,原題,ゲンダイ,ゲンダイ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
  記号,空白,*,*,*,*, , ,
"From Russia with Love" 名詞
EOS

英単語のほか、URLは活性化するため前後に半角スペースを含むことが多いので注意が必要です。 pythonstr.strip のように半角スペースを除去する処理を挟んで入力しましょう。

$ mecab

このブログのURLは https://eieito.hatenablog.com/ です。 
この  連体詞,*,*,*,*,*,この,コノ,コノ
ブログ   名詞,一般,*,*,*,*,*
の 助詞,連体化,*,*,*,*,の,ノ,ノ
URL 名詞,一般,*,*,*,*,*
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
https   名詞,固有名詞,組織,*,*,*,*
:// 名詞,サ変接続,*,*,*,*,*
eieito  名詞,一般,*,*,*,*,*
.   名詞,サ変接続,*,*,*,*,*
hatenablog  名詞,一般,*,*,*,*,*
.   名詞,サ変接続,*,*,*,*,*
com 名詞,一般,*,*,*,*,*
/   名詞,サ変接続,*,*,*,*,*
です  助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。 記号,句点,*,*,*,*,。,。,。
EOS

$ mecab -p
このブログのURLは
https://eieito.hatenablog.com/  名詞
です。 

この  連体詞,*,*,*,*,*,この,コノ,コノ
ブログ   名詞,一般,*,*,*,*,*
の 助詞,連体化,*,*,*,*,の,ノ,ノ
URL 名詞,一般,*,*,*,*,*
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
https://eieito.hatenablog.com/  名詞,サ変接続,*,*,*,*,*
です  助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。 記号,句点,*,*,*,*,。,。,。
EOS

そもそも --partial は使わず、前処理で別の記号に置換してもいいと思います。 e.g. このブログのURLはURLです。

mecab-python3の入力エラー

(mecab-python3==1.0.4 で検証)

mecab-python3 では taggerの初期化のとき引数で渡す (MeCab.Tagger("-p") ) か、tagger.set_partial(True)--partial と同じ設定を使うことができます。

デフォルトの設定とは異なり、入力の最後に改行 (+EOS) がないとsegmentation faultのエラーになります。 mecab-python3のバグではなくmecabの入力がそのように定義されていることが原因です。

import MeCab

tagger = MeCab.Tagger("-p")
#NG: エラーになる
tagger.parse('007 ロシアより愛をこめての原題は "From Russia with Love"')
zsh: segmentation fault  ipython

# 以下はOK
tagger.parse('007 ロシアより愛をこめての原題は "From Russia with Love"\nEOS')
tagger.parse('007 ロシアより愛をこめての原題は "From Russia with Love"\n')

tagger.parse('007 ロシアより愛をこめての原題は\n"From Russia with Love"\t名詞\nEOS')

mecab-python3は本家と違い、tagger.set_partial(False) で設定を無効化できるので意外と便利です。 部分解析が設定されているかどうかは tagger.partial() で確認できます。

tagger.partial()
>> True

mecab-python3を使ってもっと細かく設定したい場合は PythonでMeCabの制約付き解析を使う - Qiita のようにLattice設定を使えばよさそうです。

ja.wikipedia.org