1年ほど前に、日本語アナライザーを比較する記事を書きました。
前回の記事:日本語アナライザーの比較(Kuromoji / Sudachi / MeCab / LLM の性能検証)
あれから1年がたち、日本語の検索まわりは少しずつ変わりました。 新しい選択肢も出てきましたし、考え方も少し変わりました。
そこで今回は、続編としてもう一度比較します。 ただし、前回とまったく同じことを繰り返すわけではなく、 2026年の今なら、こう測るともっと良い、というやり方で見直します。
今回の検証は「去年と同じ条件での定点観測」ではありません。そのため、今回の数値を前回のものとそのまま比較できません。
検証に使用したプログラム等は GitHub リポジトリで公開しています。同リポジトリ内の GLOSSARY.md にまとめた用語集を参照してください。
この記事で見ること、見ないこと
先に、ゴールを明確に整理します。 読みながら「結局なにを比べているの?」と迷わないためです。
この記事で見ること:
- それぞれのアナライザーが、日本語をどう単語に区切るか。
- 検索用のトークンとして、どれが使いやすいか。
- 専門用語・英数字・単位(例:NSAIDs、300〜500mg)が保たれるか。
- 実際の検索クエリで、目的の文書がちゃんとヒットするか。
この記事で深くは扱わないこと:
- 大規模なデータでの検索ランキング評価。
- 人手による「この結果は正しい」という関連度判定。
- LLM を Elasticsearch のアナライザーとして使う構成。
この1年で変わったこと
1. 既存のアナライザーは、ちゃんと進化していた
まず押さえたいのは、定番のツールは止まっていない、ということです。
- Kuromoji は、Elastic 公式の日本語アナライザーとして引き続き使えます。
- Sudachi は、外部プラグイン(Works Applications の elasticsearch-sudachi)として進化を続け、 新しい Elasticsearch にも対応してきました。
- Sudachi の辞書は、数か月おきに新語が追加されています。
- MeCab や Janome も、Python 前処理用の選択肢として現在も利用されています。
ここで大事なのは、Kuromoji と Sudachi の「立場」が違うことです。
– Kuromoji は Elastic 公式の Japanese analysis plugin です。
– Sudachi は 外部プラグインです。
つまり、Sudachi を使うときは、 使っている Elasticsearch のバージョンに対応しているかを必ず確認します。
2. 「Elasticsearch の中で使えるか」で整理すると分かりやすい
ここで混乱しやすいのが、「結局どのアナライザーを Elasticsearch で使えるの?」という点です。
実は、全部を同じようには使えません。 3つのグループに分けると分かりやすいです。
- Elasticsearch の中で動く(プラグイン):Kuromoji(公式)、Sudachi(外部)。
- Elasticsearch の中では動かない:MeCab、Janome、Lindera。 これらは Python などで先にトークン化し、その結果を Elasticsearch に入れて使います。
- 番外(参考枠):LLM。これはアナライザーとは目的が違います(あとで説明します)。
さらに、実行環境による違いもあります。
- Self-Managed(自前で運用):
Kuromoji などの公式プラグインは、各ノードにanalysis-kuromojiをインストールし、ノードを再起動して使います。外部プラグイン(Sudachi など)も入れられます。
- Elastic Cloud Serverless:
Kuromoji などの core analysis plugins は最初から利用できます。
一方で、外部プラグインの追加や、独自ファイルのアップロードはできません。
そのため、Sudachi などの外部プラグインや、ファイルとして配置する独自辞書(synonyms / stop words / language analyzer 用 dictionary files など)を前提にした構成は使えません。
ただし、同義語についてはファイルアップロードではなく、synonyms API を使って管理できます。
3. いちばん大きな変化:形態素解析に頼らない検索
一言でいうと、この1年で「検索のやり方そのもの」に選択肢が増えました。
これまでの日本語検索は、形態素解析で単語に区切り、その単語で探すのが基本でした。 これは今も有効で、なくなりません。
ただ、もう1つの道が実用的になりました。 意味で探す検索(セマンティック検索)です。
仕組みをシンプルにいうと、こうです。 文章を「意味のベクトル(数字の並び)」に変換し、意味が近いものを探します。 このとき、形態素解析で単語に区切る必要はありません。
Elasticsearch では、semantic_text という仕組みと、 EIS(Elastic Inference Service)経由の多言語の埋め込みモデルを使うことで、 日本語でもこの検索がぐっと手軽になりました(例として、EIS では Jina Embeddings v5 系や Microsoft Multilingual E5 Large などの embedding model が利用できます)。
実務で考えると、これは大きいです。 「ロキソニン」と入れなくても、「痛み止め」で関連文書を拾える、というような検索ができます。
本番環境では、既定の inference endpoint に依存せず、利用する埋め込みモデルの inference_id を明示するのが安全です。既定モデルはバージョンや環境によって変わる可能性があり、複数インデックスで異なる embedding model が混在するとランキングに影響するためです。
4. LLM は「アナライザー」ではなく「参考枠」
前回は LLM(当時は GPT-4o)も比較に入れました。 今回も LLM を見ますが、立ち位置をはっきり分けます。
なぜかというと、LLM はインデックス用のトークナイザーとは目的が違うからです。 LLM を、Kuromoji や Sudachi と横並びにして「どれが良いアナライザーか」と比べると、 かえって混乱します。
そこで今回は、LLM を別カテゴリ(参考枠)として、次の点だけ見ます。
- 専門語を「意味のまとまり」として拾えるか。
- 検索の補助(キーワード抽出や意味理解)に使えそうか。
ここで、混同しやすい点を1つ整理します。 「LLM によるトークン分割は再現性がない」という声もありますが、必ずしもそうとは限りません。
- LLM の tokenizer そのものは、同じ条件なら基本的に同じ結果になります。
- バラつくのは、「重要語を抜き出して」とお願いしたときの生成結果のほうです。
なので今回は、再現できるように、モデル名・プロンプト・temperature を記録します。
今回の改良点(前回との違い)
前回より良くした点を、正直に宣言します。 詳しくは METHODOLOGY.md を見てください。ここでは要点だけ。
- 正規化を「入口」でそろえる。 Python で NFKC 正規化を1回だけかけ、同じ入力を全アナライザーに渡します。
- 機能語の除去を「品詞ベース」に統一する。 手書きのストップワード一覧ではなく、助詞・記号などの品詞でそろえて除きます。
- Kuromoji を「正解」と決めつけない。 類似度を1つの数字で出すだけでなく、全アナライザー間の一致や、専門語の扱いも見ます。
- 実際の検索クエリで動作を確認する。 トークンが似ているかだけでなく、「探したい文書が見つかるか」を見ます。
- バージョンを記録する。 Elasticsearch・プラグイン・辞書・ライブラリ・LLM の情報を残し、来年また比べられるようにします。
再検証:実際に比べてみる
使うテキスト
医療系のテキストを2つ使います。
- Text 1:ロキソニンの説明文(前回と同じ、短めの文)。なじみのある例として。
- Text 2:アセトアミノフェンの説明文(今回のために書き下ろした、少し長い文)。 専門用語・カタカナの薬品名・英語の略語・数値を多く含みます。
比べるアナライザー
- Elasticsearch の中:Kuromoji(標準、Elasticsearch にもともとあるアナライザー)、Kuromoji_search(
kuromoji_tokenizerをmode: searchに設定して、この記事用に作ったアナライザーですのでElasticsearch にもともと入っている名前ではありません。)、Sudachi(A / B / C)。 - Python で前処理:MeCab、Janome。 (Lindera は Rust 製の新しい選択肢ですが、今回の環境では Python 版を導入できなかったため、 本文での紹介にとどめ、計測には含めていません。)
- 参考枠(LLM):openai-gpt-oss-120b(EIS 経由)。
結果:トークン数
クリーニング後の、ユニークなトークン数です(実測値)。
Text 1(ロキソニン、約137文字):
| アナライザー | ユニークなトークン数 |
|---|---|
| Kuromoji(標準) | 34 |
| Kuromoji(search) | 34 |
| Sudachi A | 36 |
| Sudachi B | 33 |
| Sudachi C | 33 |
| MeCab | 35 |
| Janome | 38 |
Text 2(アセトアミノフェン、約290文字):
| アナライザー | ユニークなトークン数 |
|---|---|
| Kuromoji(標準) | 64 |
| Kuromoji(search) | 65 |
| Sudachi A | 64 |
| Sudachi B | 58 |
| Sudachi C | 56 |
| MeCab | 70 |
| Janome | 72 |
ここで読み取れることを少しだけ。 細かく分割する MeCab や Janome はトークン数が多めです。 Sudachi は C(大きい単位)になるほどトークン数が減り、複合語をまとめていることが分かります。 ただし「数が多い=良い」ではありません。大事なのは、次に見る専門用語の扱いと検索のヒットです。
結果:専門用語の扱い
ここが検証の肝となる、興味深いポイントです。 特定の専門用語が、意図通りにひと塊のトークンとして保持されたかを確認します(○ = 単一語として検出)。
Text 1(ロキソニン):
| 用語 | kuromoji | kuromoji_search | sudachi_a | sudachi_b | sudachi_c | mecab | janome |
|---|---|---|---|---|---|---|---|
| ロキソニン | ○ | ○ | ○ | ○ | ○ | × | ○ |
| 解熱鎮痛 | × | × | × | × | × | × | × |
| 非ステロイド性抗炎症薬 | × | × | × | × | × | × | × |
| NSAIDs | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
| 炎症 | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
| 発熱 | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
Text 2(アセトアミノフェン):
| 用語 | kuromoji | kuromoji_search | sudachi_a | sudachi_b | sudachi_c | mecab | janome |
|---|---|---|---|---|---|---|---|
| アセトアミノフェン | ○ | ○ | ○ | ○ | ○ | × | ○ |
| 中枢神経系 | × | × | × | × | × | × | × |
| 解熱鎮痛薬 | × | × | × | × | ○ | × | × |
| 非ステロイド性抗炎症薬 | × | × | × | × | × | × | × |
| NSAIDs | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
| インフルエンザ | ○ | ○ | ○ | ○ | ○ | × | ○ |
| 300mg | × | × | × | × | × | × | × |
| 肝機能障害 | × | × | × | × | ○ | × | × |
| アナフィラキシーショック | × | × | × | × | × | × | × |
| スティーブンス・ジョンソン症候群 | × | × | × | × | × | × | × |
ここから読み取れることを、いくつか。
まず、カタカナの薬品名(ロキソニン、アセトアミノフェン)や インフルエンザ は、 ほとんどのアナライザーが1語のまま残しました。 ただし、今回の MeCab の構成だけは残しませんでした。
一点だけ補足します、 これは「MeCab はダメ」という話ではありません。 分割のされ方は、使う辞書(UniDic 系か IPAdic 系かなど)や設定の影響が大きいです。 今回の MeCab + 使用辞書(UniDic)の組み合わせでは、カタカナ語が細かく分割される傾向がありました。
次に、英字の略語 NSAIDs は、すべてのアナライザーが1語で保持しました。 英字のかたまりは、そのまま残りやすいです。
そして、長い複合語(非ステロイド性抗炎症薬、中枢神経系、アナフィラキシーショック、 スティーブンス・ジョンソン症候群)は、すべてのアナライザーが分割しました。 どれも、そのままでは1語になりません。
面白いのは、解熱鎮痛薬 と 肝機能障害 を、Sudachi の C モードだけが1語で残したことです。 C モードは大きい単位でまとめるため、こうした複合語をひとかたまりにできます。
数値+単位の 300mg は、どのアナライザーも1語にしませんでした (今回の元の文が 300〜500mg なので、300・500・mg に分かれます)。
ここで大事なのは、「1語で残る=良い」ではない、ということです。
- 細かく分割されると、部分一致で拾いやすくなります(再現率が上がる)。
- 1語でまとまると、完全一致やフレーズ検索でズレにくくなります(精度が上がる)。
つまり、どちらが良いかは「あなたの検索の目的」で決まります。 非ステロイド性抗炎症薬 のような長い語を1語で完全一致させたいなら、 ユーザー辞書への登録や、フレーズ検索の併用を検討します。
結果:アナライザー間の似ている度合い(Jaccard)
次に、アナライザーどうしがどれくらい似ているかを見ます。 前回は「Kuromoji にどれだけ似ているか」だけを見ましたが、 今回は Kuromoji を正解と決めつけず、全ペアを比べます(1.00 が完全一致)。
Text 1(ロキソニン):
| kuromoji | kuromoji_search | sudachi_a | sudachi_b | sudachi_c | mecab | janome | |
|---|---|---|---|---|---|---|---|
| kuromoji | 1.00 | 1.00 | 0.84 | 0.63 | 0.63 | 0.64 | 0.85 |
| kuromoji_search | 1.00 | 1.00 | 0.84 | 0.63 | 0.63 | 0.64 | 0.85 |
| sudachi_a | 0.84 | 0.84 | 1.00 | 0.64 | 0.64 | 0.58 | 0.72 |
| sudachi_b | 0.63 | 0.63 | 0.64 | 1.00 | 1.00 | 0.42 | 0.54 |
| sudachi_c | 0.63 | 0.63 | 0.64 | 1.00 | 1.00 | 0.42 | 0.54 |
| mecab | 0.64 | 0.64 | 0.58 | 0.42 | 0.42 | 1.00 | 0.62 |
| janome | 0.85 | 0.85 | 0.72 | 0.54 | 0.54 | 0.62 | 1.00 |
Text 2(アセトアミノフェン):
| kuromoji | kuromoji_search | sudachi_a | sudachi_b | sudachi_c | mecab | janome | |
|---|---|---|---|---|---|---|---|
| kuromoji | 1.00 | 0.98 | 0.85 | 0.67 | 0.62 | 0.59 | 0.74 |
| kuromoji_search | 0.98 | 1.00 | 0.84 | 0.69 | 0.64 | 0.59 | 0.76 |
| sudachi_a | 0.85 | 0.84 | 1.00 | 0.67 | 0.64 | 0.68 | 0.68 |
| sudachi_b | 0.67 | 0.69 | 0.67 | 1.00 | 0.87 | 0.44 | 0.58 |
| sudachi_c | 0.62 | 0.64 | 0.64 | 0.87 | 1.00 | 0.42 | 0.54 |
| mecab | 0.59 | 0.59 | 0.68 | 0.44 | 0.42 | 1.00 | 0.53 |
| janome | 0.74 | 0.76 | 0.68 | 0.58 | 0.54 | 0.53 | 1.00 |
数字が多いので、読み方をまとめます。
- kuromoji と kuromoji_search はほぼ同じでした(1.00〜0.98)。 今回のテキストでは、search モードの差はほとんど出ませんでした。 複合語の固有名詞(例:関西国際空港)が多い文では差が出やすくなります。 (この点は、次の検索クエリの結果で確認します。)
- kuromoji / janome / sudachi_a は互いに近い(細かく分割するグループ)。
- sudachi_b と sudachi_c は互いに近い(大きい単位でまとめるグループ)。
- mecab は、他と最も離れていました。 ただしこれは MeCab 固有の特徴というより、今回使用した辞書・設定による切り方の違いです。
この「グループ分け」は、そのまま選び方の指針になります。
- 細かく拾いたい → kuromoji / sudachi A / janome。
- まとめたい → sudachi B / sudachi C。
- mecab は独特なので、目的に合うかを個別に確認する。
結果:検索クエリでの動作
最後に、実際の検索で確かめます。 ここが、検索システムとして一番大事なところです。
少数の文書を登録し、クエリごとに「期待する文書が拾えるか」を見ます(○ = ヒット)。
| クエリ | 期待文書 | kuromoji | kuromoji_search | sudachi_a | sudachi_b | sudachi_c |
|---|---|---|---|---|---|---|
| 空港 | doc 1 | ○ | ○ | ○ | ○ | ○ |
| 関西空港 | doc 1 | ○ | ○ | ○ | ○ | × |
| NSAIDs | doc 2 | ○ | ○ | ○ | ○ | ○ |
| 300mg | doc 2 | ○ | ○ | ○ | ○ | ○ |
| アセトアミノフェン | doc 2 | ○ | ○ | ○ | ○ | ○ |
(doc 1 は「関西国際空港は大阪府にある国際空港です。」、 doc 2 はアセトアミノフェンの説明文です。)
結果を読み解きます。
まず、ほとんどのクエリは、すべてのアナライザーでヒットしました。 唯一の取りこぼしは、関西空港(略称)を Sudachi C で検索したときだけです。
なぜでしょうか。 Sudachi C は、大きい単位でまとめるため、関西国際空港 を1つのトークンにします。 そのため、略称の「関西空港」とはうまく一致せず、ヒットしませんでした。
これは、まさに精度と再現率のトレードオフです。
- 大きい単位(Sudachi C)は、正式名称での完全一致に強い。 ただし、略称や部分的なクエリは取りこぼすことがあります。
- 細かい単位(Kuromoji や Sudachi A)は、部分一致で拾いやすい。
一方で、うれしい結果もあります。 英字の略語 NSAIDs、数値+単位の 300mg、カタカナの専門語 アセトアミノフェン は、 すべてのアナライザーで検索できました。 300mg は1つのトークンではありませんでしたが、300 と mg が別々に索引されるため、検索では拾えます。
ただし、ここは設定に依存します。 今回の query 設定ではヒットしましたが、operator(and / or)、 クエリ側のアナライザー、フィールド側のアナライザーの設定によって結果は変わります。
ここでの学びは、最初に立てた問いそのものです。 トークンが1語できれいに残るかどうかと、検索で見つかるかどうかは、必ずしも一致しません。 最終的に大事なのは「ユーザーが探したい文書が見つかるか」です。
なお、今回の小さな例では、Kuromoji の標準と mode: search で差は出ませんでした。 mode: search の効果は、複合語の固有名詞がもっと多いデータで効いてきます。
参考:LLM は何を「キーワード」として拾ったか
最後に、参考枠の LLM(EIS 経由の gpt-oss-120b)を見ます。 くり返しになりますが、これはアナライザーの比較ではありません。 「意味のまとまりとして、専門語を拾えるか」を見るための参考です。
抽出されたキーワードは次の通りです。
Text 1(ロキソニン):
ロキソニン錠 / ロキソニン / 非ステロイド性抗炎症薬 / NSAIDs / 解熱鎮痛作用 / 関節リウマチ / 変形性関節症 / 腰痛症 / 肩こり / 歯痛 / 手術後 / 外傷後 / 炎症 / 痛み / 風邪 / 熱
Text 2(アセトアミノフェン):
アセトアミノフェン / 中枢神経系 / 解熱鎮痛薬 / 非ステロイド性抗炎症薬 / NSAIDs / 抗炎症作用 / 一般用医薬品 / 頭痛 / 歯痛 / 月経痛 / 関節痛 / インフルエンザ / 風邪 / 発熱 / 成人 / 1回300〜500mg / 1日3回 / 経口投与 / 肝機能障害 / 高齢者 / 用量調整 / 重篤な副作用 / 肝障害 / アナフィラキシーショック / スティーブンス・ジョンソン症候群
ここが、形態素解析との大きな違いです。
形態素解析がすべて分割してしまった長い専門語を、LLM は1つの意味のまとまりとして拾いました。 たとえば、非ステロイド性抗炎症薬、中枢神経系、アナフィラキシーショック、 スティーブンス・ジョンソン症候群 などです。 さらに、1回300〜500mg や 1日3回 のような、用量を表す「意味のかたまり」も拾っています。
一言でいうと、LLM は「索引用の最小単位」ではなく「意味のまとまり」を取り出します。
このため、LLM が向いているのは次のような場面です。
- クエリの意図を理解する(クエリ理解)。
- 文章から重要語を抜き出す(キーワード抽出)。
- 意味で探す検索(セマンティック検索)の補助。
逆に、インデックスのトークン化には向きません。 理由は3つあります。
- 生成結果は毎回まったく同じとは限らない(再現性が低い)。
- 大量の文書をすべて LLM に通すのはコストが高い。
- そもそも目的が、転置インデックス用の最小トークンを作ることではない。
今回使用した LLM の設定(再現性のため):
- 使用モデル:openai-gpt-oss-120b
- 実行環境:EIS(Elastic Inference Service)経由
- temperature:0
- プロンプト:付録(GitHub のリポジトリ)に掲載
アナライザーの選び方ガイド
ここまでをふまえて、用途別の選び方をまとめます。 「結局どれを使えばいいの?」への答えです。
- 部分一致や再現率を重視したい(広く拾いたい) → Kuromoji、または Sudachi A(細かく分割)。
- 完全一致・フレーズ検索を重視したい(複合語をまとめたい) → Sudachi C。
- バランスを取りたい → Sudachi B。
- 新語・製品名・固有名詞が多い → 辞書更新の速い Sudachi、または Kuromoji に辞書を足す構成。
- 意味で探したい(言い換えにも強くしたい) → 形態素解析ではなく、semantic_text + 多言語埋め込み(EIS)。
- Elasticsearch の中だけで完結させたい → 実質、Kuromoji か Sudachi(ほかは Python 前処理が必要)。
- LLM → インデックスのトークン化には向きません。 クエリ理解やキーワード抽出など、検索の「補助」に使うのが向いています。
まとめ
最後に、覚えておきたいことを1つだけ。
「いちばん良いアナライザー」は存在しません。用途で決まります。
この1年での大きな変化は、選択肢が増えたことです。 形態素解析は今も主役の1つですが、意味で探すセマンティック検索という道も、 日本語で手軽に使えるようになりました。
次の一歩としては、自分の検索でよく使うクエリをいくつか決めて、 この記事の方法で実際に試してみるのがおすすめです。 results/ に数値が出るので、自分のデータで「どれが合うか」を確かめられます。
※本記事の Python コードと検証環境は、Claude Codeを使って作成しました。
Links
- Kuromoji(analysis-kuromoji)プラグイン
- kuromoji analyzer
- kuromoji_tokenizer
- semantic_text フィールド
- semantic_text による意味検索
- Elastic Inference Service(EIS)
- EIS の対応モデル(gpt-oss-120b など)
- 自前クラスタから EIS を使う(Cloud Connect)
- カスタムプラグイン/バンドルのアップロード(Serverless の制約の出典)
- Hosted と Serverless の違い
- Synonyms API(Serverless で同義語を使う方法)

