SamuraiT / mecab-python3

:snake: mecab-python. you can find original version here:http://taku910.github.io/mecab/
https://pypi.python.org/pypi/mecab-python3
Other
541 stars 51 forks source link

parseするとNoneTypeが返ってくる #73

Closed cidrugHug8 closed 3 years ago

cidrugHug8 commented 3 years ago

大きなテキストを与えるとNoneTypeが返ってきてしまいます。 処理されるようにできないでしょうか。

Python 3.7.7 (default, Jun  9 2020, 18:08:39) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import MeCab
>>> tagger=MeCab.Tagger()
>>> tagger.parse('あ\t'*700000)[:10]
'あ\tア\tア\tア\t記号'
>>> tagger.parse('あ\t'*1000000)[:10]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable

Python 3.7.7

polm commented 3 years ago

--input-buffer-size (-b)に大きな数値を指定すると対応できますが入力文を事前に分割した方がいいです。

MeCabは文を入力単位として設計されています。作りには余裕があるので段落程度でも全く問題ないし、一般的な記事でもいけますが、長編小説とかをまるごと突っ込むと無理があります。

入力文には普段絶対単語内に起きない改行などの文字があるので、それをベースに事前に分割することをおすすめします。

polm commented 3 years ago

返事がないのでクローズしますが他に質問ありましたらお気軽に聞いてください。

cidrugHug8 commented 3 years ago

申し訳ありません、返信遅れました。

Python 3.7.7 (default, Jun  9 2020, 18:08:39) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import MeCab, ipadic
>>> tagger=MeCab.Tagger(ipadic.MECAB_ARGS+' -b 81920000')
>>> tagger.parse('あ\t'*1000000)[:10]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable
>>> tagger=MeCab.Tagger(ipadic.MECAB_ARGS+' -b 819200000')
>>> tagger.parse('あ\t'*1000000)[:10]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable

-bで与えるのはバイト数でよろしかったでしょうか?

よろしくお願いいたします。

polm commented 3 years ago

はい、バイト数です。

cidrugHug8 commented 3 years ago

\nでsplitすると何度もparseを呼ばなければならずそのオーバーヘッドが気になっています。 一気に与えればMeCabの方でうまく処理してくれるような気がして。。。

コマンドラインのMeCabにテキストファイルを入力すると処理できるのに、 mecab-python3を通すと処理できないのが不満です。

cidrugHug8 commented 3 years ago

上記のやり方で-bは効いていますでしょうか?

polm commented 3 years ago

\nでsplitすると何度もparseを呼ばなければならずそのオーバーヘッドが気になっています。 一気に与えればMeCabの方でうまく処理してくれるような気がして。。。

逆ですよ。長い文だと処理が難しくなります。

コマンドラインのMeCabにテキストファイルを入力すると処理できるのに、mecab-python3を通すと処理できないのが不満です。

CLIのMeCabは一行ごと処理していますのですでに改行で分割しています。ソースはこちら。

https://github.com/taku910/mecab/blob/046fa78b2ed56fbd4fac312040f6d62fc1bc31e3/mecab/src/tagger.cpp#L1245

「上記のやり方」とは具体的になにを指すか分かりませんがCLIでも -b は有効です。一行の長さが -b の値を超えるとエラーが発生します。それでも次の行から処理は続きますが。

cidrugHug8 commented 3 years ago

このような意味になります。

巨大なテキストを入れるとmecabでは処理できません。

# mecab < a.txt | head -1
too long sentence.

しかし-b オプションを付けると処理できます。

# mecab -b 10000000 < a.txt | head -1
# ...

mecab-pyでも-bオプションなしでは処理できません。

# mecab-py < a.txt | head -1
too long sentence.

-b オプションを付けても処理できません。

# mecab-py -b 10000000 < a.txt |head -1
ウィキペディア  ウィキペディア  ウィキペディア  ウィキペディア  名詞-固有名
詞-一般                 3
Traceback (most recent call last):
  File "/usr/local/bin/mecab-py", line 8, in <module>
    sys.exit(parse())
  File "/usr/local/lib/python3.7/site-packages/MeCab/cli.py", line 19, in parse
    print(tagger.parse(line.strip())[:-1])
BrokenPipeError: [Errno 32] Broken pipe

mecab-pyで-bオプションを付けることはpythonコードで-bオプションを付けてTaggerを生成することと同義でしょうか? mecab-pyで-bオプションを付けても処理できていませんが。

--input-buffer-size (-b)に大きな数値を指定すると対応できますが入力文を事前に分割した方がいいです。

とあるものの、-bを付けても対応できていないと思うのですが。。。

polm commented 3 years ago

なるほど、よく分かりました。これは意外だったので調べてみましたが、確かに-bはCLIでしか効果がないようです。

以前リンクしたところで-bの値は確かに使われています。しかしそれはCLIで処理する場合のみ利用されている部分で、ライブラリとして呼び出す場合に固定値の BUF_SIZE とかが代わりに使われているようです。

tagger が None を返した後に tagger.what() を確認すると、 too long sentence となっているので、この辺で問題が起きていることが分かります。ここの動作も複雑ですが、単純に文字列が長過ぎるというより、ラティス作成でなにか問題が起きてコストを計算できないみたいです。

メモリー確保自体が問題でなければ、BUF_SIZE を変更することでより長い文に対応できるかもしれませんが、再コンパイルしないと変更できませんので確認していません。また問題は文字列の長さよりラティスのノードの数とかの可能性も高いのでそもそもそれでも解決できないかもしれません。

どちらにしても、ライブラリの視点から調整できるパラメターがないので、より長い文を分割せずに対応する術がありません。これは mecab-python3 に限ったことではありません。少し調べたところ、別のライブラリでも基本的に事前分割していることを確認しました。

NMeCabの方ではエラーの原因についての少し細かい説明もあります。

-b について誤った情報を伝えてしまったことは申し訳ありませんが、結局最初に言った通り、入力分を事前に分割した方が良いです。