Kuromoji・Sudachi・MeCab・Janome・LLM・semantic search の使い分け【2026】

Tech blog cover: a laptop with a burst of colorful shapes and the title text overlaid in white letters. BLOG

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 を見てください。ここでは要点だけ。

  1. 正規化を「入口」でそろえる。 Python で NFKC 正規化を1回だけかけ、同じ入力を全アナライザーに渡します。
  2. 機能語の除去を「品詞ベース」に統一する。 手書きのストップワード一覧ではなく、助詞・記号などの品詞でそろえて除きます。
  3. Kuromoji を「正解」と決めつけない。 類似度を1つの数字で出すだけでなく、全アナライザー間の一致や、専門語の扱いも見ます。
  4. 実際の検索クエリで動作を確認する。 トークンが似ているかだけでなく、「探したい文書が見つかるか」を見ます。
  5. バージョンを記録する。 Elasticsearch・プラグイン・辞書・ライブラリ・LLM の情報を残し、来年また比べられるようにします。

再検証:実際に比べてみる

使うテキスト

医療系のテキストを2つ使います。

  • Text 1:ロキソニンの説明文(前回と同じ、短めの文)。なじみのある例として。
  • Text 2:アセトアミノフェンの説明文(今回のために書き下ろした、少し長い文)。 専門用語・カタカナの薬品名・英語の略語・数値を多く含みます。

比べるアナライザー

  • Elasticsearch の中:Kuromoji(標準、Elasticsearch にもともとあるアナライザー)、Kuromoji_search(kuromoji_tokenizermode: search に設定して、この記事用に作ったアナライザーですのでElasticsearch にもともと入っている名前ではありません。)、Sudachi(A / B / C)。
  • Python で前処理:MeCab、Janome。 (Lindera は Rust 製の新しい選択肢ですが、今回の環境では Python 版を導入できなかったため、 本文での紹介にとどめ、計測には含めていません。)
  • 参考枠(LLM):openai-gpt-oss-120b(EIS 経由)。

結果:トークン数

クリーニング後の、ユニークなトークン数です(実測値)。

アナライザーユニークなトークン数
Kuromoji(標準)34
Kuromoji(search)34
Sudachi A36
Sudachi B33
Sudachi C33
MeCab35
Janome38
アナライザーユニークなトークン数
Kuromoji(標準)64
Kuromoji(search)65
Sudachi A64
Sudachi B58
Sudachi C56
MeCab70
Janome72

ここで読み取れることを少しだけ。 細かく分割する MeCab や Janome はトークン数が多めです。 Sudachi は C(大きい単位)になるほどトークン数が減り、複合語をまとめていることが分かります。 ただし「数が多い=良い」ではありません。大事なのは、次に見る専門用語の扱いと検索のヒットです。

結果:専門用語の扱い

ここが検証の肝となる、興味深いポイントです。 特定の専門用語が、意図通りにひと塊のトークンとして保持されたかを確認します(○ = 単一語として検出)。

用語kuromojikuromoji_searchsudachi_asudachi_bsudachi_cmecabjanome
ロキソニン×
解熱鎮痛×××××××
非ステロイド性抗炎症薬×××××××
NSAIDs
炎症
発熱
用語kuromojikuromoji_searchsudachi_asudachi_bsudachi_cmecabjanome
アセトアミノフェン×
中枢神経系×××××××
解熱鎮痛薬××××××
非ステロイド性抗炎症薬×××××××
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(ロキソニン):

kuromojikuromoji_searchsudachi_asudachi_bsudachi_cmecabjanome
kuromoji1.001.000.840.630.630.640.85
kuromoji_search1.001.000.840.630.630.640.85
sudachi_a0.840.841.000.640.640.580.72
sudachi_b0.630.630.641.001.000.420.54
sudachi_c0.630.630.641.001.000.420.54
mecab0.640.640.580.420.421.000.62
janome0.850.850.720.540.540.621.00

Text 2(アセトアミノフェン):

kuromojikuromoji_searchsudachi_asudachi_bsudachi_cmecabjanome
kuromoji1.000.980.850.670.620.590.74
kuromoji_search0.981.000.840.690.640.590.76
sudachi_a0.850.841.000.670.640.680.68
sudachi_b0.670.690.671.000.870.440.58
sudachi_c0.620.640.640.871.000.420.54
mecab0.590.590.680.440.421.000.53
janome0.740.760.680.580.540.531.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 は独特なので、目的に合うかを個別に確認する。

結果:検索クエリでの動作

最後に、実際の検索で確かめます。 ここが、検索システムとして一番大事なところです。

少数の文書を登録し、クエリごとに「期待する文書が拾えるか」を見ます(○ = ヒット)。

クエリ期待文書kuromojikuromoji_searchsudachi_asudachi_bsudachi_c
空港doc 1
関西空港doc 1×
NSAIDsdoc 2
300mgdoc 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)を見ます。 くり返しになりますが、これはアナライザーの比較ではありません。 「意味のまとまりとして、専門語を拾えるか」を見るための参考です。

抽出されたキーワードは次の通りです。

ロキソニン錠 / ロキソニン / 非ステロイド性抗炎症薬 / NSAIDs / 解熱鎮痛作用 / 関節リウマチ / 変形性関節症 / 腰痛症 / 肩こり / 歯痛 / 手術後 / 外傷後 / 炎症 / 痛み / 風邪 / 熱

アセトアミノフェン / 中枢神経系 / 解熱鎮痛薬 / 非ステロイド性抗炎症薬 / 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