1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

日本語の tokenize, lemmatize, romanize の速度比較

Last updated at Posted at 2023-03-09

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 そのものはとても速いのですが、出力に手を加えないといけないので、予想していたより遅くなりました。
出力は GiNZASudachiPy (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()
    ]
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?