昔、形態素解析にかかる時間を短縮するために調べた内容のメモです。
以前の記事で、mecab-python3 の使い方を書いたとき、tagger = MeCab.Tagger()
という処理を関数の外側で行なっていました。
実は初めてmecab-python3を使った頃、僕は次のように書いてました。
def mecab_tokenizer(text):
# 関数の中で、MeCab.Tagger()を呼び出す。これが遅い
tagger = MeCab.Tagger()
parsed_text = tagger.parse(text)
parsed_lines = parsed_text.split("\n")[:-2]
surfaces = [l.split('\t')[0] for l in parsed_lines]
features = [l.split('\t')[1] for l in parsed_lines]
bases = [f.split(',')[6] for f in features]
# ここに、必要な品詞の単語だけ選抜する処理を入れることもある
result = [b if b != '*' else s for s, b in zip(surfaces, bases)]
return result
1個や2個のテキストを処理する分にはこの書き方で問題なかったのですが、
数十万件のテキストを処理するとこの関数がとても遅いという問題があり、調査をしていました。
結果わかったことは、タイトルの通り、MeCab.Tagger()
が遅いということです。
jupyter で コードの前に %timeit とつけると時間を測れるのでやってみます。
%timeit tagger=MeCab.Tagger()
```
結果:
217 µs ± 6.17 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
```
ちなみに、形態素解析自体(parse)の実行時間はこちら
# 100文字のテキストを事前に用意しておきます
print(len(text))
```
100
```
%timeit parsed_text = tagger.parse(text)
```
結果:
26.9 µs ± 151 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
```
テキストがもっと長くなると話も変わるのですが、100文字くらいのテキストであれば、
parseにかかる時間よりも、Taggerのオブジェクトを作るのにかかる時間の方が8くらいかかっています。
対象のテキスト数(=関数が呼び出される回数)が数十万〜数百万件になってくると、
体感スピードがかなり違うので、
tagger = MeCab.Tagger()
は関数の中ではなく、事前に行うようにしておきます。
名前空間を汚染したりすることが気になる場合は、 class化するなどの対応をとりましょう。
また、形態素解析するテキストの数が少ない場合はあまり気にしなくても大丈夫です。
完全に余談ですが、この記事を書くために私物のMacで時間を計測したとき、職場のMacよりはるかに速いので感動しました。
職場の端末だとMeCab.Tagger()に 1.2ms (6倍!)かかります。
端末が5年物とそこそこ古いだけでなく、辞書指定などの問題もあるかもしれません。