1. はじめに
日本語の tokenize ライブラリの速度を比較している記事は沢山ありますが、「日本語の文章を単語のレンマごとにローマ字化したい」というニッチな要望に応えるものはなかったため、比較してみます。
2. 所要時間の測り方
処理を1000回行い、その平均所要時間を算出します。
from functools import wraps
from time import perf_counter
from statistics import mean
def timeit(func):
@wraps(func)
def _wrapper(*args, **kwargs):
es = []
for _ in range(1000):
t = perf_counter()
result = func(*args, **kwargs)
e = perf_counter() - t
es.append(e)
return result, mean(es)
return _wrapper
3. tokenize => lemmatize
3-1. 使用するライブラリ
下記の4種類のライブラリを比較します。
fugashi==1.2.1
ginza==5.1.2
Janome==0.4.2
SudachiPy==0.6.7
3-1-1. fugashi
from fugashi import Tagger
tagger_fugashi = Tagger('-Owakati')
@timeit
def lemmatize_by_fugashi(sent: str) -> list[str]:
return [word.feature.lemma for word in tagger_fugashi(sent)]
3-1-2. GiNZA
GiNZA
は tokenize 周りに SudachiPy
を使用しています。
import spacy
nlp = spacy.load('ja_ginza', enable=[''])
@timeit
def lemmatize_by_ginza(sent: str) -> list[str]:
return [token.lemma_ for token in nlp(sent)]
3-1-3. Janome
from janome.tokenizer import Tokenizer
t = Tokenizer()
@timeit
def lemmatize_by_janome(sent: str) -> list[str]:
return [token.base_form for token in t.tokenize(sent)]
3-1-4. mecab-python3
import MeCab
tagger_mecab = MeCab.Tagger()
@timeit
def lemmatize_by_mecab(sent: str) -> list[str]:
"""数詞の場合IndexErrorが起こるので、Noneを返すように修正"""
return [
None if len(row.split(',')) < 8 else row.split(',')[7]
for row in tagger_mecab.parse(sent).split('\n')[:-2]
]
3-1-5. SudachiPy
from typing import Literal
from sudachipy import tokenizer
from sudachipy import dictionary
tokenizer_obj = dictionary.Dictionary().create()
@timeit
def lemmatize_by_sudachipy(sent: str, mode: Literal['a', 'b', 'c']) -> list[str]:
match mode:
case 'a':
split_mode = tokenizer.Tokenizer.SplitMode.A
case 'b':
split_mode = tokenizer.Tokenizer.SplitMode.B
case 'c':
split_mode = tokenizer.Tokenizer.SplitMode.C
return [m.dictionary_form() for m in tokenizer_obj.tokenize(sent, split_mode)]
3-2. 検証結果
3-2-1. 文章①
日越外交関係樹立50周年記念ハイレベル経済セミナー
ライブラリ | 出力 | 速度 |
---|---|---|
fugashi | 日越 / 外交 / 関係 / 樹立 / None / 周年 / 記念 / ハイ-high / レベル-level / 経済 / セミナー-seminar | 0.00010 |
GiNZA | 日越 / 外交 / 関係 / 樹立 / 50 / 周年 / 記念 / ハイレベル / 経済 / セミナー | 0.00030 |
Janome | 日 / 越 / 外交 / 関係 / 樹立 / 50 / 周年 / 記念 / ハイレベル / 経済 / セミナー | 0.00104 |
mecab-python3 | 日越 / 外交 / 関係 / 樹立 / None / None / 記念 / ハイ-high / レベル-level / 経済 / セミナー-seminar | 0.00006 |
SudachiPy (A) | 日越 / 外交 / 関係 / 樹立 / 50 / 周年 / 記念 / ハイ / レベル / 経済 / セミナー | 0.00003 |
SudachiPy (B) | 日越 / 外交 / 関係 / 樹立 / 50 / 周年 / 記念 / ハイ / レベル / 経済 / セミナー | 0.00003 |
SudachiPy (C) | 日越 / 外交 / 関係 / 樹立 / 50 / 周年 / 記念 / ハイレベル / 経済 / セミナー | 0.00003 |
3-2-2. 文章②
両国関係は様々な分野で引き続き協力を促進するための大きな余地がある。
ライブラリ | 出力 | 速度 |
---|---|---|
fugashi | 両国 / 関係 / は / 様々 / だ / 分野 / で / 引き続く / 協力 / を / 促進 / 為る / 為 / の / 大きな / 余地 / が / 有る / 。 | 0.00015 |
GiNZA | 両国 / 関係 / は / 様々 / だ / 分野 / で / 引き続く / 協力 / を / 促進 / する / ため / の / 大きな / 余地 / が / ある / 。 | 0.00050 |
Janome | 両国 / 関係 / は / 様々 / だ / 分野 / で / 引き続き / 協力 / を / 促進 / する / ため / の / 大きな / 余地 / が / ある / 。 | 0.00132 |
mecab-python3 | 両国 / 関係 / は / 様々 / だ / 分野 / で / 引き続く / 協力 / を / 促進 / 為る / 為 / の / 大きな / 余地 / が / 有る / 。 | 0.00011 |
SudachiPy (A) | 両国 / 関係 / は / 様々 / だ / 分野 / で / 引く / 続く / 協力 / を / 促進 / する / ため / の / 大きな / 余地 / が / ある / 。 | 0.00004 |
SudachiPy (B) | 両国 / 関係 / は / 様々 / だ / 分野 / で / 引き続く / 協力 / を / 促進 / する / ため / の / 大きな / 余地 / が / ある / 。 | 0.00004 |
SudachiPy (C) | 両国 / 関係 / は / 様々 / だ / 分野 / で / 引き続く / 協力 / を / 促進 / する / ため / の / 大きな / 余地 / が / ある / 。 | 0.00004 |
3-2-3. 文章③
コミュニケーション不足により信頼関係を築くことができない。
ライブラリ | 出力 | 速度 |
---|---|---|
fugashi | コミュニケーション-communication / 不足 / に / 因る / 信頼 / 関係 / を / 築く / 事 / が / 出来る / ない / 。 | 0.00012 |
GiNZA | コミュニケーション / 不足 / に / よる / 信頼関係 / を / 築く / こと / が / できる / ない / 。 | 0.00040 |
Janome | コミュニケーション / 不足 / により / 信頼 / 関係 / を / 築く / こと / が / できる / ない / 。 | 0.00106 |
mecab-python3 | コミュニケーション-communication / 不足 / に / 因る / 信頼 / 関係 / を / 築く / 事 / が / 出来る / ない / 。 | 0.00009 |
SudachiPy (A) | コミュニケーション / 不足 / に / よる / 信頼 / 関係 / を / 築く / こと / が / できる / ない / 。 | 0.00004 |
SudachiPy (B) | コミュニケーション / 不足 / に / よる / 信頼 / 関係 / を / 築く / こと / が / できる / ない / 。 | 0.00004 |
SudachiPy (C) | コミュニケーション / 不足 / に / よる / 信頼関係 / を / 築く / こと / が / できる / ない / 。 | 0.00004 |
mecab-python3
は tokenize そのものはとても速いのですが、出力に手を加えないといけないので、予想していたより遅くなりました。
出力は GiNZA
と SudachiPy (C)
が単語を大きく取りがちという傾向がありました。
4. romanize
4-1. 使用するライブラリ
下記の2種類のライブラリを比較します。
cutlet==0.1.19
pykakasi==2.2.1
どちらのライブラリもローマ字化の方法を複数実装していますが、今回は訓令式を指定した結果を載せます。
また、どちらも内部で tokenize してから romanize しますが、今回は指定した単語を丸々 romanize したいので、無理やりくっつけます。
4-1-1. Pykakasi
import pykakasi
kks = pykakasi.kakasi()
@timeit
def romanize_by_pykakasi(lemma: str) -> str:
return ''.join([item['kunrei'] for item in kks.convert(lemma)]).lower()
4-1-2. cutlet
cutlet
は tokenize に fugashi
を使用しています。
import cutlet
katsu = cutlet.Cutlet(system='kunrei')
katsu.use_foreign_spelling = False
@timeit
def romanize_by_cutlet(lemma: str) -> str:
return katsu.romaji(lemma).replace(' ', '').lower()
4-2. 検証結果
単語 | Pykakasi | cutlet | Pykakasi速度 | cutlet速度 |
---|---|---|---|---|
ある | aru | aru | 0.000008 | 0.000047 |
が | ga | ga | 0.000006 | 0.000038 |
こと | koto | koto | 0.000009 | 0.000022 |
する | suru | suru | 0.000005 | 0.000021 |
ため | tame | tame | 0.000004 | 0.000011 |
だ | da | da | 0.000003 | 0.000011 |
で | de | de | 0.000003 | 0.000010 |
できる | dekiru | dekiru | 0.000006 | 0.000014 |
ない | nai | nai | 0.000004 | 0.000013 |
に | ni | ni | 0.000003 | 0.000010 |
により | niyori | niyori | 0.000006 | 0.000021 |
の | no | no | 0.000003 | 0.000010 |
は | ha | wa | 0.000003 | 0.000010 |
よる | yoru | yoru | 0.000004 | 0.000012 |
を | wo | o | 0.000003 | 0.000009 |
コミュニケーション | komyunikeesyon | komyunikeesyon | 0.000024 | 0.000028 |
セミナー | seminaa | seminaa | 0.000009 | 0.000020 |
ハイ | hai | hai | 0.000007 | 0.000013 |
ハイレベル | haireberu | haireberu | 0.000013 | 0.000028 |
レベル | reberu | reberu | 0.000009 | 0.000016 |
不足 | fusoku | busoku | 0.000005 | 0.000013 |
両国 | ryougoku | ryoukoku | 0.000005 | 0.000016 |
事 | koto | koto | 0.000005 | 0.000012 |
余地 | yoti | yoti | 0.000005 | 0.000012 |
促進 | sokusin | sokusin | 0.000005 | 0.000014 |
信頼 | sinrai | sinrai | 0.000005 | 0.000016 |
信頼関係 | sinraikankei | sinraikankei | 0.000005 | 0.000031 |
出来る | dekiru | dekiru | 0.000009 | 0.000024 |
分野 | bunya | bun'ya | 0.000008 | 0.000017 |
協力 | kyouryoku | kyouryoku | 0.000007 | 0.000022 |
周年 | syuunen | syuunen | 0.000005 | 0.000018 |
因る | yoru | yoru | 0.000005 | 0.000012 |
外交 | gaikou | gaikou | 0.000005 | 0.000014 |
大きな | ookina | ookina | 0.000007 | 0.000015 |
引き続き | hikituzuki | hikituzuki | 0.000005 | 0.000017 |
引き続く | hikituzuku | hikituzuku | 0.000005 | 0.000017 |
引く | hiku | hiku | 0.000005 | 0.000012 |
日 | niti | hi | 0.000006 | 0.000012 |
日越 | nitietu | nitietu | 0.000005 | 0.000017 |
有る | aru | aru | 0.000004 | 0.000012 |
様々 | samazama | samazama | 0.000005 | 0.000020 |
樹立 | juritu | zyuritu | 0.000005 | 0.000013 |
為 | tame | tame | 0.000005 | 0.000011 |
為る | suru | naru | 0.000005 | 0.000012 |
築く | kizuku | kizuku | 0.000006 | 0.000014 |
経済 | keizai | keizai | 0.000005 | 0.000015 |
続く | tuzuku | tuzuku | 0.000006 | 0.000014 |
記念 | kinen | kinen | 0.000006 | 0.000013 |
越 | etu | kosi | 0.000005 | 0.000012 |
関係 | kankei | kankei | 0.000005 | 0.000014 |
赤字でつけた差分を見ると Pykakasi
の方が欲しい結果が出ている感じがします。
速度も Pykakasi
の方が2-3倍速いようです。
5. 終わりに
今回に要望に対しては SudachiPy + Pykakasi
が有効だということが分かりました。
import pykakasi
from sudachipy import tokenizer
from sudachipy import dictionary
kks = pykakasi.kakasi()
tokenizer_obj = dictionary.Dictionary().create()
mode = tokenizer.Tokenizer.SplitMode.C
def romanize_each_lemma(sent: str) -> list[str]:
return [
''.join([item['kunrei'] for item in kks.convert(m.dictionary_form())]).lower()
for m in tokenizer_obj.tokenize(sent, mode)
if m.dictionary_form().isalpha()
]