MeCab
イベント
該当するコンテンツが見つかりませんでした
マガジン
該当するコンテンツが見つかりませんでした
技術ブログ
こんにちは、AIテクノロ ジー グループのエンジニアの吉田です。 本記事は Enigmo Advent Calendar 2025 の 18日目の記事です。 普段は検索システム全般、 機械学習 システムのMLOps、AI関連の機能開発を担当しております。 この記事では「AIでさがす」サービスのリニューアルについて紹介します。 「AIでさがす」サービスとは 「AIでさがす」サービスは、 BUYMA のWebサイトおよびアプリで提供している、AIを活用した商品提案サービスです。 実際の機能は以下からご利用頂けます。( BUYMA アカウントでのログインが必要となります。) 「AIでさがす」サービス ユーザーが文章で質問すると、AIが質問内容を理解し、おすすめの商品を提案します。例えば「春のデートにぴったりなワンピースを教えて」といった質問に対して、AIが回答文とともに具体的な商品を紹介します。 従来のキーワード検索では見つけにくかった商品や、ユーザー自身が気づいていなかった新しい商品との出会いを提供することで、 BUYMA でのショッピング体験をより豊かにすることを目指しています。 ※商品画像はモザイク加工しております。 リニューアルの背景 旧システムは、ChatGPT API を活用した商品提案サービスでしたが、主な課題が3点ありました。 BUYMA の知識不足 ChatGPT が一般的な知識で回答を生成するため、 BUYMA ならではのトレンドや商品特性を反映できない。 根拠の不明確さ ChatGPT の回答に 参照元 がない。 検索キーワード生成の精度 形態素解析 ツールの MeCab を併用していましたが、文脈や意味を理解した検索キーワード生成ができない。 また、リリースから2年が経過し、本格的にバージョンアップが必要なタイミングでもありました。 ※旧システムの詳細は こちらの記事 で紹介しております。 ちょうどチームメンバーが社内ドキュメントのAI検索システムを開発しており、この仕組みを BUYMA の多数の記事コンテンツに適用すれば、より BUYMA らしい商品提案が可能になると考えました。 そこで、今回のリニューアルでは、 BUYMA 内の記事コンテンツ群をベースに会話するエージェントを作成しました。これにより、 BUYMA ならではの知識を持ったAIが、より BUYMA でおすすめしたい商品を提案できるようになりました。 システム変更前後の比較 旧システムと新システムの違いは以下の通りです。 旧システムでは、ChatGPT が一般的な知識で回答を生成し、 MeCab による単純な 形態素解析 で検索キーワードを生成していました。そのため、 BUYMA ならではの文脈を理解した商品提案が難しい状況でした。 新システムでは、 BUYMA 内記事コンテンツを参照した Vertex AI Search が回答文を生成し、Gemini が文脈を理解した検索キーワードを生成します。その結果、より BUYMA らしい商品提案が可能になりました。 それぞれの処理フローは以下の通りです。 旧システム処理フロー BUYMA 基幹システムから「AIでさがす」 API にリクエスト 「AIでさがす」 API がユーザーの質問を ChatGPT API に送信 ChatGPT が回答文とおすすめアイテムリストを生成 アイテム名を MeCab ( 形態素解析 )で解析し、検索キーワードを生成 検索 API で商品情報を取得し、ユーザーに表示 新システム処理フロー BUYMA 基幹システムから「AIでさがす」 API にリクエスト 「AIでさがす」 API がユーザーの質問を Vertex AI Search に送信 Vertex AI Search (事前に BUYMA 内記事コンテンツをインポート済み)が回答文を生成 質問文と回答文を Gemini に送信し、検索キーワードを生成 検索 API で商品情報を取得し、ユーザーに表示 アーキテクチャ ー特徴 1. Vertex AI Search Vertex AI Search を利用して、インポートした BUYMA 内記事コンテンツをベースに会話を行うエージェントを構築しました。 BUYMA 内記事コンテンツのインポート 約4000件の記事をデータストアにインポート プロンプト設計 「ファッション ECサイト BUYMA のショッピングアドバイザー」として定義し、ユーザーの質問に対して最適な商品を提案する形で回答を生成 2. Gemini Gemini を活用する事により、会話内容から商品検索キーワードを生成する機能を作成しました。 プロンプト設計 「 ECサイト の検索キーワードを生成する専門家」として定義し、会話の文脈を理解して検索キーワードを生成 MeCab との違い MeCab は単語の分解のみだが、Gemini は文脈を理解してブランド名・カテゴリ名・モデル名を組み合わせた検索キーワードを生成 実装時の課題・解決策・工夫した点 Vertex AI Search の幻覚への対応 初回質問時に Vertex AI Search が過去から質問が続いているような幻覚を見る場合がありました。当初は初回と2回目以降の会話を共通のプロンプトで行っており、「ユーザーの過去の質問履歴」の項目に入っている文言の有無から初回なのか、2回目以降の会話なのかを判断する指示を出していました。ところが、「過去」という文言に引きずられてなのか、初回なのに過去の質問をAI側が捏造して、その続きとして回答する場合が稀にありました。 プロンプトテンプレートを初回用と2回目以降用の2種類に分け、初回用のプロンプトからは「ユーザーの過去の質問履歴」の文言自体を削除する事によって対応しました。 敵対的クエリへの対応 敵対的クエリ(不適切な質問)の場合、Vertex AI Search の API からのレスポンスフォーマットが通常とは異なるものになり、要約が生成できないにもかかわらず、無理やり商品紹介を行ってしまいました。 敵対的クエリーのフォーマットを検知した場合は、要約失敗として扱い、商品紹介を行わないように修正しました。 この場合以外でも稀に異なるフォーマットのレスポンスになる場合があり、サービス継続に支障が出ないように都度改善を行いました。 Gemini のライブラリ移行 もともと使用していたライブラリがサポート終了を迎えるため、社内では実績がない新しいライブラリに移行する必要がありました。移行後、従来使用していた Gemini モデルが初期設定では使用できず、次世代のモデルを試したところレスポンスタイムが大幅に遅くなってしまいました。新しいライブラリという事もあり、AIツールではなかなか解決できず、最終的には Google サポートに問い合わせして解決に至りました。 得られた学びとノウハウ AIツールの活用と限界 「AIでさがす」のバックエンド API の リポジトリ は、ほぼ全部作り直したのですが、AIツールを活用する事によって、 工数 を節約する事ができました。Terraform 関連のリソース修正、テストケース作成やMOCK用のフロントエンド実装等においてもAIツールにより大幅な 工数 削減ができました。 一方で、Gemini のライブラリ移行など、ドキュメントの記載やインターネット上での知見が少ない領域ではAIツールでは解決できず、結果的に公式サポートへの問い合わせが必要でした。 AIの不確定な挙動への対応 Vertex AI Search の幻覚や部分的な失敗など、AIサービス特有の不確実性に対して、初回用と2回目以降用でテンプレートを分けるなど、細かな調整が重要でした。また、プロンプトだけではどうする事もできない場合があり、そのような場合は後処理でルールベースのロジックを追加する必要がありました。 効果測定 リニューアル後、以下のような指標が上昇しました。 1スレッドあたりの質問数の平均 会話の継続性が向上し、ユーザーが複数回質問を続けるようになりました。 1ユーザー1日あたりの質問数 利用頻度が向上し、ユーザーがより積極的に機能を活用するようになりました。 検索URLに遷移された回数 商品検索への誘導効果が向上し、実際の商品閲覧につながるケースが増加しました。 これらの結果から、 BUYMA 内記事コンテンツを根拠とした回答の提供と、文脈を理解した検索キーワード生成により、ユーザーの満足度と利用価値が向上したと考えられます。 直近の対応/今後の展開・課題 金額絞り込み機能の追加(今月対応) ユーザーからの要望が多い金額絞り込み機能の対応をしました。価格帯に関する質問に対して適切な商品提案ができていない課題があったため、Gemini で検索 API 用の金額フィルタークエリを生成することで対応しました。 コンテンツの拡充 現在は BUYMA 内記事コンテンツのみを Vertex AI Search にインポートしていますが、今後は YouTube での発信内容も追加する予定です。記事以外のコンテンツも活用することで、より幅広い情報をユーザーに提供できるようになります。 継続的なメンテナンス AIのライブラリやモデルは随時更新されていくため、継続的なメンテナンスが課題となります。特に Gemini や Vertex AI Search などのサービスは進化が早く、新しいモデルへの対応や非推奨ライブラリ/バージョンから移行など、定期的な見直しが必要です。 まとめ 本記事では、「AIでさがす」サービスのリニューアルについて紹介しました。 旧システムでは ChatGPT と MeCab を使用していましたが、 BUYMA 特有の知識不足や根拠の不明確さなどの課題がありました。リニューアルでは Vertex AI Search と Gemini を採用し、 BUYMA 内記事コンテンツを根拠とした回答生成と文脈を理解した検索キーワード生成を実現しました。 実装時には敵対的クエリへの対応やAIサービス特有の不確実性への対処など様々な課題に直面しましたが、ロジックでの細かい制御やAIツールの活用により解決できました。リニューアル後は会話継続性や利用頻度、商品検索への誘導効果が明らかに向上しています。 明日の記事は同じAIテクノロ ジー グループの髙橋さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
はじめまして!一橋大学SDS研究科 修士1年の佐藤祥太 ( @Shota_Sato01 ) です。今回私は8月のCA Tech JOBインターンに参加させていただきました! この記事では、配属先のAI Shiftでの取り組みについてご紹介させていただきます! 配 属部署について 今回のインターンでは、AI Shiftに配属になりました。「人とAIの協働を実現し人類に生産性革命をもたらす」というMISSIONのもと、AIエージェントやVoiceBotの開発に取り組んでいます。 ビジネスサイドとエンジニアサイドが議論しながら、組織で一体となってプロダクトの改善に取り組んでいるのが印象的でした。 タスク 概要と背景 AI Shiftが提供しているVoiceBotは、電話応対業務を自動化するサービスです。 その中で、お客様の発話が「何について言及しているのか」を正しく特定するのは会話の破綻を防ぐために非常に重要なファクターになります。 今回のインターンで私が取り組んだのは主にこの部分で、お客様の発話内容が何を示しているのかを膨大なリストの中から特定する、「エンティティリンキング」というタスクに取り組みました。 このタスクの課題としては 入力が音声認識結果であるため、認識誤りがあった場合正解となるエンティティと表記が一致しない 対象のリストが膨大であるとき、発話内容と正解となるエンティティを紐付けることが困難 という点があげられます。 そこで、私は、 音声認識誤りに頑健 効率的に候補を絞り込める (Nを小さくできる) という特徴を持つようなロジックについて検討を行い、提案した手法の検証を行いました。 手法 今回は、以下に示す計7つの手法 (BaseLine×3, SoftMatcha, MeCab+部分文字列検索×3) について検証を行い、定量的、定性的に比較を行いました。 BaseLine BaseLineの基本的なアイデアは、入力として、transcription (音声入力をテキストにしたもの) とエンティティのリスト全件分をLLMに渡し、出力として、transcriptionがリストの中のどのエンティティを指しているかを返します。 今回は、以下の3種類のプロンプトを作成しました。 Zero-Shot: transcriptionとエンティティリストをプロンプトの中に埋め込み応答を生成 Few-Shot: Zero-Shotに、入出力例を追加 CoT: Zero-Shotに、段階的に候補を絞り込むという指示を追加 候補集合の絞り込み BaseLineではLLMが数万件の候補から1つの正解を抽出する必要があり、正解率やプロンプト長の増加による実行速度への悪影響が懸念されます。 そこで、「LLMに入力する際の候補集合を事前に絞り込むことで、精度と実行時間を向上させられるのではないか」と考え、絞り込みの手法を複数提案し、検証を行いました。 SoftMatcha SoftMatcha [1]は高速かつ柔らかいパターンマッチ検索ツールです(余談になりますが、 2025年の言語処理学会@長崎 で実際に聴講させていただいた中で印象的な発表の一つでした)。 エンティティリスト全件をソース、transcriptを入力として、この手法で検索をかけてヒットしたものを候補集合に選定しました。 MeCabによる形態素解析 + 部分文字列検索 MeCab [2]は形態素解析のツールで、意味を持つ最小の表現単位(形態素)に分解することができます。 分解後の各形態素をキーワードとして、エンティティリストの全件に対し部分一致となるかどうかを判定し、絞り込みを行うのが基本的なアイデアになります。 今回は、このアイデアをベースに3種類の絞り込みのロジックを検討しました。 MeCab: transcriptionの各形態素に対し、得られた検索結果の和集合を候補集合にする MeCab + Cutdown: 上記の各検索結果のうち、サイズが1000以下のもののみを選択し、それらの和集合を候補集合にする MeCab + Comb: transcriptionからn個の形態素を選択し、それらをすべて含むエンティティを検索。これを全ての組み合わせに対し実行し、得られた検索結果の和集合を候補集合にする MeCab + Combについて補足させていただきます。上記の例であれば、n=2のとき、検索対象となる形態素の組み合わせは {(株式, 会社) | (株式, A) | (株式, えー) | (株式, A) | (会社, A) | (会社, えー) | (会社, A) | (A, えー) | (A, A) | (えー, A)} となります。 これらの各組み合わせに対し、すべての形態素に対し部分一致となるエンティティを検索し、それらの和集合を取ります。このような操作をn=1〜(transcriptionの形態素数)まで再帰的に行い、各試行で候補集合のサイズが1000を下回った時を最終的な候補集合として選択します。 ※なお、本検証においてはMeCabの辞書として unidic-lite を使用しています。 実験設定 今回は、エンティティリストとして、約2万件の固有名詞を対象にしました。 これらに対し、固有名詞のテキストをTTSを用いて合成音声を作り、それらを音声認識にかけることにより擬似的な音声認識結果を100件生成し、それらに対し、各手法の正解率と実行時間を評価します。 なお、実験で用いたLLMはGemini-2.0-Flashとし、今回扱うデータは、アルファベットの小文字化や空白除去等の正規化処理を事前に施しました。 実験結果 候補集合の絞り込みについて ここでは、BaseLine以外の手法についての候補集合の絞り込みについて議論します。 各手法毎の絞り込みを行った際の候補集合のサイズ ave_size:候補集合に含まれるエンティティ件数の平均値 LLM_miss:LLMが誤答したときのave_size included: 正解が含まれている候補集合の数 左側が音声認識がうまくいったとき、すなわち、音声の入力を正しくテキストに変換できたケースでの絞り込み結果になります(実用上では、このような場合は完全一致となるエンティティの検索を行えば良いと思いますが、参考のために併記させていただきます)。 右側は音声認識誤りが含まれるときの絞り込み結果です。 SoftMatchaは、認識誤りのない場合には、ほぼ一意に決定できるまでに候補集合を絞り込めています。一方で、少しでも認識誤りが含まれてしまうと検索結果にほとんどヒットせず、候補集合自体を作ることができなくなってしまう、という結果になりました。 一方、MeCabでは、認識誤りの有無にかかわらず、ほぼ全ての入力に対して、絞り込んだ集合の中に正解を含むことができています。しかし、最終的な候補集合のサイズの削減効果は限定的で、Nを小さくするという目標はあまり達成できていません。 MeCab + αの2手法は、いずれもMeCabと比較し効果的に候補を絞れていることが確認できます。元は約2万件あったデータを数百のオーダーにまで絞り込みを行えており、かつ、認識誤りを含む場合にも、およそ65%の割合で正解を含むことができています。 正解率と実行時間の評価 ここでは、全手法について、正解率と実行時間について議論します。 各手法毎の正解率と実行時間。実行時間は100回分の処理時間を1回あたりに換算したもの BaseLineの3手法については、正解率に多少変動は見られるものの、大幅な性能の改善は見られませんでしたが、認識誤りのない場合に対し、Few-Shotで正解率が大きく低下しているのが気になりました(おそらく例の与え方が良くなかった...🤔)。 SoftMatchaについて、音声認識がうまく行っている場合には100%の正解率を誇っています。一方で、認識誤りがある場合に関しては、もっとも正解率が低くなっています。これは今回のような実験設定に対しては、意味の近さを用いた柔らかな検索手法では対応できず、絞り込みの時点で多くが失敗しているためだと考えられます。また、事前の絞り込みの段階で時間がかかってしまい、今回の全手法の中で最も実行時間が長いという結果になりました。 MeCabについて、こちらはBaseLine手法とあまり性能の変化が見られませんでした。 これは、絞り込み後の候補集合のサイズがあまり小さくならなかったということを反映していると考えられます。 MeCab + αの2手法について、こちらは効果的に絞り込みを行えたこともあり、正解率、実行時間ともに顕著な性能の向上が確認できます。ロジック的には単純ですが、有効な手法が提案できたのではないかと思います。 定性分析 ここでは、これまでの実験結果を踏まえて、BaseLine以外のそれぞれの手法のメリット・デメリットを分析します。 SoftMatcha この手法のメリットは、意味的に変化しない誤字に対して強いという点です。 例えば、「株式会社第一」というエンティティが正解のときに、「株式会社第1」というtranscriptionが得られたケースを考えます。音声認識的には誤りが存在しますが、この誤りは意味的には変化していません。このような事例に対しては正確に候補集合を抽出できました。同様に、文字の全角・半角の違いなどにも頑健です。 デメリットとしては、削除誤りや意味的に異なるフレーズに置換されるような誤りが生じるケースに弱いという点です。 MeCab この手法のメリットは、一部でも形態素が正しければ候補として拾えるという点です。 上記のような「株式会社AえーA」のような場合でも、「株式」や「会社」といった部分的に正解している形態素が含まれていれば候補集合に正解を含むことができます。 デメリットとしては、すべての形態素に対して検索を行うだけでは候補の絞り込みがほとんどできていいない点です。 MeCab + Cutdown この手法のメリットは、候補集合を小さくできることにあります。先ほどのMeCabの致命的すぎるデメリットを克服するべく、リストに頻出する形態素は機械的に除外するという制約をかけました。結果として、うまく候補を絞れました。 デメリットとしては、頻出形態素のみ、もしくは頻出形態素+音声認識誤りからなるようなエンティティを取りこぼしてしまうということです。 例として正解が「株式会社AI Shift」の時にtranscriptionが「株式会社AIシフト」となった場合を考えてみます。 このとき、「株式」「会社」「AI」のいずれも一般的なキーワードかと思います。このようなケースでは、結果として「シフト」のみが検索対象の形態素となり、正解を候補集合に含めることはできなくなります。頻出する形態素を決める基準を何件以上とするかの閾値の調整が重要になりそうです。 MeCab + Comb この手法のメリットは、MeCab + Cutdownと同様に、候補集合を小さくできることにあります。また、機械的に頻出形態素を除外するという操作をしないので、MeCab + Cutdownのデメリットを補うことができます。 デメリットとしては、制約を厳しくする、すなわち、nの数が大きくなると、正解となるエンティティを最終的に取りこぼしてしまう可能性が高くなる点です。例えば、n = transcriptionの形態素数のときは、すべての形態素に誤りが含まれない場合、もしくは削除誤りしか存在しないケースでしか正解を候補集合に含めることができなくなってしまいます。nの数をどこまで大きくするべきかは事前に調整が必要だと感じました。 まとめと展望 ここまで色々と実験についてお話ししましたが、結論としては以下になります。 MeCab + αの手法が効果的っぽい Cutdown、Combのどちらが良いかは検索対象となるエンティティの集合の特性に依存しそう Cutdown:表層が似ていないとき(特徴的な形態素があるとき)に強い Comb:表層が似ているとき(頻出する形態素が存在するとき)に強い 今回の検証は人工的に生成した認識結果をもとにしたものですが、実際の音声対話では言い淀みや、エンティティに直接関係しない発話が含まれるケースが存在します。今後の展望としては、そのような多様な発話に対する頑健性の評価や対処方法の検討、またエンティティの特性に応じたロジックの使い分けなどが考えられます。 また、提案手法ではユーザーの発話に対し、一回の処理で候補集合の絞り込みを行うことを主眼に置きましたが、発話内容によっては十分に絞りきれていないケースが見受けられました。そのような場合に対する、更なる絞り込みのロジックやインタラクションの検討も行う必要がありそうです。 インターンを振り返って 今回、インターンに参加するにあたって自分の中での目標が二つありました。 一つは、「(機械学習)エンジニアとして働くとはどういうことかを体感する」ということ。もう一つは、「技術的に成長する」ということです。 一つ目に関しては、確実に達成することができました。 プロダクトの開発に関連した各種ミーティングに同席させていただいて、実際にプロダクトをよりよくするための議論に参加することができました。 もちろん、技術にまつわる全ての議論についていけたわけではないですが、それでも、プロダクトの問題点や改善点を見つけ出すプロセスやそれに対するアプローチ・考え方は、とても勉強になりました。 二つ目に関しては、達成率は50%程度だと感じています。 今回のインターンでは、今まで学んでいた知識や技術を生かすことはできたと思っています。この経験は自分のこれまでの学びに対して自信を持てる良いきっかけになったと思います。 しかし、インターン期間中に技術的に新しいスキルを獲得したり、新たな分野の知見を獲得するようなことはもっともっとやりたかったと感じています。 最後になりますが、インターン期間中はAI Shiftの皆さんがとても親切にしてくださって、楽しくインターン期間を過ごすことができました。 ここまで読んでいただきありがとうございました!! 参考 [1] https://softmatcha.github.io/ [2] https://aclanthology.org/W04-3230/ 投稿 エンティティリンキングの性能改善のための効果的な絞り込み手法の検証 は 株式会社AI Shift に最初に表示されました。
Elasticsearchの標準アナライザーは Kuromoji ですが、他にも日本語向けのアナライザーが存在します。本記事では Sudachi や MeCab 、およびPythonライブラリの Janome 、そして LLM(GPT-4) といった選択肢を比較し、どんな場面でどれを使うべきかを検討しました。 なお、Elasticsearch 9.xではSudachiやMeCabの公式対応プラグインはまだリリースされていません。そのため今回は Python環境で事前に形態素解析してからElasticsearchにインデックスする方法 を採用しています。一方、Janomeは純Python実装であるため、追加プラグインなしで使用可能です。 目次 比較対象アナライザー ステップ0:事前準備 Python環境とテキスト設定 トークン正規化・クリーニング関数 比較戦略 トークン化関数の定義(共通フォーマット出力) 包括的トークナイザー比較分析 📊 Kuromoji(ベースライン)出力 📊 Sudachi A/B/C、MeCab、Janome、GPT-4 出力 分析結果サマリー・統計表 1. トークン数比較表 2. ユニークトークン分析 考察 まとめ 比較対象アナライザー 1. Elasticsearchプラグイン系 Kuromoji :Elasticsearch標準の日本語アナライザー 2. Pythonライブラリ系(アプリ側で事前解析) SudachiPy :3種類の粒度(A/B/C)に対応 MeCab :高速かつ実績豊富な形態素解析器 Janome :Pure Pythonで導入が簡単 3. LLM系(外部API) OpenAI GPT-4o :文脈を考慮した柔軟な分割が可能 ステップ0:事前準備 1. 仮想環境の作成(uvを使用) curl -LsSf https://astral.sh/uv/install.sh | sh mkdir analyzer-project && cd analyzer-project uv venv source .venv/bin/activate 2. 必要なPythonライブラリのインストール uv pip install elasticsearch janome openai sudachipy sudachidict_core mecab-python3 3. Elasticsearchプラグインの確認(Kuromoji) bin/elasticsearch-plugin install analysis-kuromoji 4. MeCab本体のインストール(macOS向け) brew install mecab mecab-ipadic 5. OpenAI APIキーの設定 export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" Python環境とテキスト設定 import os import sys import platform import re from dotenv import load_dotenv # Import the working elasticsearch connection utility from elastic_conection import es, test_connection # Japanese text analysis libraries import MeCab from janome.tokenizer import Tokenizer from sudachipy import tokenizer as sudachi_tokenizer from sudachipy import dictionary as sudachi_dictionary # OpenAI for LLM comparison from openai import OpenAI # Environment and target text setup print("Python環境情報") print("="*30) print(f"Python Version: {sys.version.split()[0]}") print(f"Platform: {platform.platform()}") print(f"Architecture: {platform.machine()}") # Check if in virtual environment if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): print("仮想環境で実行中") else: print("仮想環境ではありません") print(f"実行環境: {sys.executable}") # Define the target text for analysis TARGET_TEXT = """ロキソニン錠の説明書ですね。ロキソニンは、解熱鎮痛作用のある非ステロイド性抗炎症薬(NSAIDs)で、 痛みや発熱、炎症を抑える効果があります。具体的には、関節リウマチ、変形性関節症、腰痛症、肩こり、 歯痛、手術後や外傷後の炎症や痛み、風邪による熱や痛みなどに用いられます。""" print(f"\n分析対象テキスト: {TARGET_TEXT}") print(f"文字数: {len(TARGET_TEXT)}") print() Python環境情報 ============================== Python Version: 3.13.3 Platform: macOS-15.5-arm64-arm-64bit-Mach-O Architecture: arm64 仮想環境で実行中 実行環境: /Users/*****/es-analyzer-project/.venv/bin/python 分析対象テキスト: ロキソニン錠の説明書ですね。ロキソニンは、解熱鎮痛作用のある非ステロイド性抗炎症薬(NSAIDs)で、 痛みや発熱、炎症を抑える効果があります。具体的には、関節リウマチ、変形性関節症、腰痛症、肩こり、 歯痛、手術後や外傷後の炎症や痛み、風邪による熱や痛みなどに用いられます。 文字数: 137 トークン正規化・クリーニング関数 Kuromoji を基準(ベースライン)として使用 し、他のアナライザーの結果をKuromojiレベルまでクリーニングして比較します。 比較戦略 1. Kuromoji = ベースライン クリーニングなし : Kuromojiの生出力をそのまま使用 理由 : Elasticsearchに最適化済み、自然にストップワード除去済み 役割 : 他のアナライザーの目標レベルとして機能 2. 他のアナライザー = Kuromojiレベルまでクリーニング MeCab, SudachiPy, Janome, OpenAI : クリーニング関数を適用 目標 : Kuromojiと同等の品質レベルに調整 比較 : クリーニング後にKuromojiとの類似度を測定 クリーニング処理内容(Kuromoji以外) 1. 助詞・助動詞の除去 : 「は」「を」「に」「が」など機能語の削除 2. 句読点・記号の除去 : 「、」「。」「(」「)」などの除去 3. ストップワードの除去 : 検索で意味の薄い語の削除 4. 空白・数字の除去 : 純粋な数字や空白文字の除去 期待される効果 公平な比較 : 全アナライザーが同じ品質レベルで比較される 実用性評価 : 検索エンジンでの実際の使用場面を想定 最適化効果 : 各アナライザーのクリーニング後の性能向上を確認 Kuromoji優位性 : Elasticsearchプラグインとしての最適化効果を確認 def clean_tokens_like_kuromoji(tokens): """ Clean tokens to match Kuromoji's behavior by removing stop words and punctuation. Args: tokens (list): List of token strings Returns: list: Cleaned tokens with stop words and punctuation removed """ # Japanese stop words and particles commonly filtered out stop_words = { 'は', 'の', 'を', 'に', 'が', 'で', 'と', 'から', 'まで', 'より', 'へ', 'や', 'か', 'も', 'て', 'た', 'だ', 'である', 'です', 'ます', 'した', 'する', 'ある', 'いる', 'なる', 'この', 'その', 'あの', 'どの', 'これ', 'それ', 'あれ', 'どれ', 'ここ', 'そこ', 'あそこ', 'どこ', 'こう', 'そう', 'ああ', 'どう', 'という', 'といった', 'による', 'において', 'について', 'に関して', 'に対して', 'に関する', 'について', 'さん', 'ちゃん', 'くん', 'さま', 'さあ', 'まあ', 'ああ', 'いや', 'はい', 'いいえ', 'うん', 'ううん', 'えー', 'あー', 'うー', 'おー' } # Punctuation and symbols to remove punctuation_patterns = [ r'^[、。!?.,!?;:()()\[\]「」『』【】〈〉《》〔〕…‥・]+$', # Pure punctuation r'^[ー\-~〜]+$', # Long vowel marks and dashes r'^[ \s]+$', # Whitespace (including full-width) r'^\d+$', # Pure numbers r'^[a-zA-Z]+$', # Pure alphabet r'^[0-9]+$', # Full-width numbers r'^[a-zA-Z]+$' # Full-width alphabet ] cleaned = [] for token in tokens: if not token or not token.strip(): continue # Remove stop words if token in stop_words: continue # Remove punctuation and unwanted patterns is_punctuation = False for pattern in punctuation_patterns: if re.match(pattern, token): is_punctuation = True break if not is_punctuation: cleaned.append(token) return cleaned print("Token cleaning function defined") トークナイザー初期化・接続確認 各トークナイザーを初期化し、動作確認を行います。 初期化対象 : 1. Elasticsearch + Kuromoji ローカルElasticsearchサーバー(localhost:9200)への接続 Kuromojiアナライザーの利用可能性確認 2. MeCab Homebrew環境のIPA辞書パス設定 分かち書きモード(-O wakati)での初期化 3. SudachiPy 標準辞書の読み込み A/B/Cモード対応トークナイザーオブジェクト作成 4. Janome 純Python実装のトークナイザー初期化 依存関係なしの簡単セットアップ 5. OpenAI GPT-4o 環境変数からAPIキー取得 GPT-4モデルへの接続確認 注意 : 各ツールが正常に初期化されない場合、エラーメッセージが表示されます。 # === システム接続確認とトークナイザー初期化 === print("=== システム接続確認 ===") # Elasticsearch接続と初期化 print("Elasticsearchに接続中...") try: # elastic_conection.pyからESクライアントをインポート from elastic_conection import es, test_connection # 接続テスト if test_connection(): es_available = True print("Elasticsearch接続成功") else: es_available = False print("Elasticsearch接続失敗") print("解決方法: Elasticsearchサーバーを起動してください") print(" brew services start elasticsearch") except Exception as e: es_available = False print(f"Elasticsearch接続失敗: {e}") print("解決方法: Elasticsearchサーバーを起動してください") print(" brew services start elasticsearch") # 各トークナイザーの初期化 print("\nトークナイザーを初期化中...") # MeCab初期化 (Homebrew環境対応) tagger = None try: import MeCab # Homebrew環境用の設定リスト(正しいmecabrcパスを含む) configs_to_try = [ "-r /opt/homebrew/etc/mecabrc -Owakati", # Homebrew mecabrc + wakati mode "-r /opt/homebrew/etc/mecabrc", # Homebrew mecabrc only "-Owakati", # wakati mode only "", # デフォルト設定 "-d /opt/homebrew/lib/mecab/dic/ipadic", # ipadic辞書パス (Homebrew) "-d /usr/local/lib/mecab/dic/ipadic", # ipadic辞書パス (従来のパス) ] for config in configs_to_try: try: print(f" MeCab設定を試行中: '{config if config else 'デフォルト'}'") tagger = MeCab.Tagger(config) # テスト実行して動作確認 test_result = tagger.parse("テスト") if test_result and len(test_result.strip()) > 0: mecab_config = config if config else "デフォルト設定" print(f"MeCab初期化成功 (設定: {mecab_config})") print(f" テスト結果: {test_result.strip()}") break except Exception as e: print(f" 設定失敗: {e}") continue if not tagger: print("MeCab初期化失敗: MeCab could not be initialized with any configuration") print("解決方法: brew install mecab mecab-ipadic") except ImportError: print("MeCab初期化失敗: MeCabがインストールされていません") print("解決方法: brew install mecab mecab-ipadic") # SudachiPy初期化 tokenizer_obj = None try: from sudachipy import tokenizer from sudachipy import dictionary tokenizer_obj = dictionary.Dictionary().create() print("SudachiPy初期化成功") except Exception as e: print(f"SudachiPy初期化失敗: {e}") # Janome初期化 janome_tokenizer = None try: from janome.tokenizer import Tokenizer janome_tokenizer = Tokenizer() print("Janome初期化成功") except Exception as e: print(f"Janome初期化失敗: {e}") # OpenAI API初期化 openai_client = None try: import openai from dotenv import load_dotenv import os load_dotenv() api_key = os.getenv("OPENAI_API_KEY") if api_key: openai_client = openai.OpenAI(api_key=api_key) print("OpenAI API初期化成功") else: print("OpenAI API初期化失敗: APIキーが設定されていません") print("解決方法: .envファイルにOPENAI_API_KEYを設定してください") except Exception as e: print(f"OpenAI API初期化失敗: {e}") # 初期化サマリー print("\n初期化サマリー:") print(f" Elasticsearch: {'OK' if es_available else 'NG'}") print(f" MeCab: {'OK' if tagger else 'NG'}") print(f" SudachiPy: {'OK' if tokenizer_obj else 'NG'}") print(f" Janome: {'OK' if janome_tokenizer else 'NG'}") print(f" OpenAI: {'OK' if openai_client else 'NG'}") トークン化関数の定義(共通フォーマット出力) tokenize_with_kuromoji(text) tokenize_with_mecab(text) tokenize_with_sudachi(text, mode) tokenize_with_janome(text) tokenize_with_openai(text) すべての関数でエラーハンドリングを実装し、失敗時は空リストを返します。 def tokenize_with_kuromoji(text): """Tokenize text using Elasticsearch Kuromoji analyzer""" if not es_available: print("Kuromoji tokenization skipped: Elasticsearch not available") return [] try: response = es.indices.analyze( body={ "analyzer": "kuromoji", "text": text } ) return [token['token'] for token in response['tokens']] except Exception as e: print(f"Kuromoji tokenization error: {e}") return [] def tokenize_with_mecab(text): """Tokenize text using MeCab""" if not tagger: print("MeCab tokenization skipped: MeCab not available") return [] try: result = tagger.parse(text).strip().split() return [token for token in result if token] except Exception as e: print(f"MeCab tokenization error: {e}") return [] def tokenize_with_sudachi(text, mode='C'): """Tokenize text using SudachiPy with specified mode (A, B, or C)""" if not tokenizer_obj: print("SudachiPy tokenization skipped: SudachiPy not available") return [] try: mode_map = {'A': sudachi_tokenizer.Tokenizer.SplitMode.A, 'B': sudachi_tokenizer.Tokenizer.SplitMode.B, 'C': sudachi_tokenizer.Tokenizer.SplitMode.C} tokens = tokenizer_obj.tokenize(text, mode_map[mode]) return [token.surface() for token in tokens] except Exception as e: print(f"SudachiPy tokenization error: {e}") return [] def tokenize_with_janome(text): """Tokenize text using Janome""" if not janome_tokenizer: print("Janome tokenization skipped: Janome not available") return [] try: tokens = janome_tokenizer.tokenize(text, wakati=True) return list(tokens) except Exception as e: print(f"Janome tokenization error: {e}") return [] def tokenize_with_openai(text): """Tokenize text using OpenAI GPT-4""" if not openai_client: print("OpenAI tokenization skipped: OpenAI client not available") return [] try: prompt = f""" Please tokenize the following Japanese text into meaningful segments. Return only a comma-separated list of tokens, no explanations. Text: {text} Tokens:""" response = openai_client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": prompt}], max_tokens=200, temperature=0 ) result = response.choices[0].message.content.strip() tokens = [token.strip() for token in result.split(',')] return [token for token in tokens if token] except Exception as e: print(f"OpenAI tokenization error: {e}") return [] print("All tokenization functions defined (with availability checks)") 包括的トークナイザー比較分析 def compare_all_tokenizers(text): """Compare all tokenizers on the given text - using Kuromoji as baseline (no cleaning)""" print(f"分析対象テキスト: {text}") print("=" * 80) results = {} # 1. Kuromoji (Elasticsearch) - BASELINE (no cleaning applied) print("\n1. Kuromoji (Elasticsearch) - ベースライン") kuromoji_tokens = tokenize_with_kuromoji(text) results['kuromoji'] = { 'raw': kuromoji_tokens, 'cleaned': kuromoji_tokens # No cleaning - use as baseline } print(f"Raw/Baseline ({len(kuromoji_tokens)}): {kuromoji_tokens}") print("Kuromojiはベースラインとして使用(クリーニングなし)") # 2. SudachiPy (all modes) - cleaned to match Kuromoji behavior for mode in ['A', 'B', 'C']: print(f"\n2. SudachiPy Mode {mode} - Kuromojiベース調整済み") sudachi_tokens = tokenize_with_sudachi(text, mode) sudachi_cleaned = clean_tokens_like_kuromoji(sudachi_tokens) results[f'sudachi_{mode}'] = { 'raw': sudachi_tokens, 'cleaned': sudachi_cleaned } print(f"Raw ({len(sudachi_tokens)}): {sudachi_tokens}") print(f"Cleaned ({len(sudachi_cleaned)}): {sudachi_cleaned}") # 3. MeCab - cleaned to match Kuromoji behavior print(f"\n3. MeCab - Kuromojiベース調整済み") mecab_tokens = tokenize_with_mecab(text) mecab_cleaned = clean_tokens_like_kuromoji(mecab_tokens) results['mecab'] = { 'raw': mecab_tokens, 'cleaned': mecab_cleaned } print(f"Raw ({len(mecab_tokens)}): {mecab_tokens}") print(f"Cleaned ({len(mecab_cleaned)}): {mecab_cleaned}") # 4. Janome - cleaned to match Kuromoji behavior print(f"\n4. Janome - Kuromojiベース調整済み") janome_tokens = tokenize_with_janome(text) janome_cleaned = clean_tokens_like_kuromoji(janome_tokens) results['janome'] = { 'raw': janome_tokens, 'cleaned': janome_cleaned } print(f"Raw ({len(janome_tokens)}): {janome_tokens}") print(f"Cleaned ({len(janome_cleaned)}): {janome_cleaned}") # 5. OpenAI GPT-4 - cleaned to match Kuromoji behavior if openai_client: print(f"\n5. OpenAI GPT-4 - Kuromojiベース調整済み") openai_tokens = tokenize_with_openai(text) openai_cleaned = clean_tokens_like_kuromoji(openai_tokens) results['openai'] = { 'raw': openai_tokens, 'cleaned': openai_cleaned } print(f"Raw ({len(openai_tokens)}): {openai_tokens}") print(f"Cleaned ({len(openai_cleaned)}): {openai_cleaned}") else: print(f"\n5. OpenAI GPT-4 (スキップ - API key not available)") # Comparison summary with Kuromoji as baseline print(f"\nKuromojiベースライン比較:") kuromoji_baseline = set(results['kuromoji']['cleaned']) for name, data in results.items(): if name != 'kuromoji': cleaned_tokens = set(data['cleaned']) overlap = len(kuromoji_baseline & cleaned_tokens) total_unique = len(kuromoji_baseline | cleaned_tokens) similarity = (overlap / total_unique * 100) if total_unique > 0 else 0 print(f" {name:<12}: {similarity:.1f}% similarity to Kuromoji baseline") return results # Run the comparison analysis_results = compare_all_tokenizers(TARGET_TEXT) 📊 Kuromoji(ベースライン)出力 トークン数:42 特徴:分かち書きが細かく、Elasticsearchでのインデックスに最適 📊 Sudachi A/B/C、MeCab、Janome、GPT-4 出力 Sudachiモードごとに粒度が変化 GPT-4oは語彙的まとまり重視で分割が異なる MeCab・Janomeは細かく分かれ、類似度が高い 1. Kuromoji (Elasticsearch) - ベースライン Raw/Baseline (42): ['ロキソニン', '錠', '説明', '書', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', 'nsaids', '痛み', '発熱', '炎症', '抑える', '効果', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛む', '風邪', '熱', '痛み', '用いる'] Kuromojiはベースラインとして使用(クリーニングなし) 2. SudachiPy Mode A Raw (86): ['ロキソニン', '錠', 'の', '説明', '書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛', 'み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体', '的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形', '性', '関節', '症', '、', '腰痛', '症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛', 'み', '、', '風邪', 'に', 'よる', '熱', 'や', '痛', 'み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (49): ['ロキソニン', '錠', '説明', '書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛', 'み', '発熱', '炎症', '抑える', '効果', 'あり', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛', 'み', '風邪', 'よる', '熱', '痛', 'み', 'など', '用い', 'られ'] 2. SudachiPy Mode B Raw (78): ['ロキソニン', '錠', 'の', '説明書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形性', '関節症', '、', '腰痛症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'に', 'よる', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (41): ['ロキソニン', '錠', '説明書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体的', '関節', 'リウマチ', '変形性', '関節症', '腰痛症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', 'よる', '熱', '痛み', 'など', '用い', 'られ'] 2. SudachiPy Mode C Raw (78): ['ロキソニン', '錠', 'の', '説明書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形性', '関節症', '、', '腰痛症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'に', 'よる', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (41): ['ロキソニン', '錠', '説明書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体的', '関節', 'リウマチ', '変形性', '関節症', '腰痛症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', 'よる', '熱', '痛み', 'など', '用い', 'られ'] 3. MeCab Raw (80): ['ロキソニン', '錠', 'の', '説明', '書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体', '的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形', '性', '関節', '症', '、', '腰痛', '症', '、', '肩こり', '、', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'による', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (45): ['ロキソニン', '錠', '説明', '書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', '熱', '痛み', 'など', '用い', 'られ'] 4. Janome Raw (82): ['ロキソニン', '錠', 'の', '説明', '書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱', '鎮痛', '作用', 'の', 'ある', '非', 'ステロイド', '性', '抗', '炎症', '薬', '(', 'NSAIDs', ')', 'で', '、', '\n', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あり', 'ます', '。', '具体', '的', 'に', 'は', '、', '関節', 'リウマチ', '、', '変形', '性', '関節', '症', '、', '腰痛', '症', '、', '肩こり', '、', '\n', '歯痛', '、', '手術', '後', 'や', '外傷', '後', 'の', '炎症', 'や', '痛み', '、', '風邪', 'による', '熱', 'や', '痛み', 'など', 'に', '用い', 'られ', 'ます', '。'] Cleaned (45): ['ロキソニン', '錠', '説明', '書', 'ね', 'ロキソニン', '解熱', '鎮痛', '作用', '非', 'ステロイド', '性', '抗', '炎症', '薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あり', '具体', '的', '関節', 'リウマチ', '変形', '性', '関節', '症', '腰痛', '症', '肩こり', '歯痛', '手術', '後', '外傷', '後', '炎症', '痛み', '風邪', '熱', '痛み', 'など', '用い', 'られ'] 5. OpenAI GPT-4o Raw (40): ['ロキソニン錠', 'の', '説明書', 'です', 'ね', '。', 'ロキソニン', 'は', '、', '解熱鎮痛作用', 'の', 'ある', '非ステロイド性抗炎症薬', '(', 'NSAIDs', ')', 'で', '、', '痛み', 'や', '発熱', '、', '炎症', 'を', '抑える', '効果', 'が', 'あります', '。', '具体的', 'に', 'は', '、', '関節リウマチ', '、', '変形性関節症', '、', '腰痛症', '、', '肩こり'] Cleaned (17): ['ロキソニン錠', '説明書', 'ね', 'ロキソニン', '解熱鎮痛作用', '非ステロイド性抗炎症薬', '痛み', '発熱', '炎症', '抑える', '効果', 'あります', '具体的', '関節リウマチ', '変形性関節症', '腰痛症', '肩こり'] 分析結果サマリー・統計表 トークン化結果を定量的に分析し、各アナライザーの特性を明確にします。 サマリーテーブル内容 : 1. トークン数比較表 Raw Tokens : 各アナライザーの生トークン数 Cleaned Tokens : クリーニング後のトークン数 Effectiveness : クリーニング効果率(ノイズ除去率) 2. ユニークトークン分析 各アナライザー固有のトークン : 他では検出されない独自トークン 全アナライザー共通トークン : すべてで一致する基本トークン トークン多様性 : 全体での語彙カバレッジ 分析指標 : トークン総数 : 各手法の分割粒度の違い 共通度 : アナライザー間の一致率 独自性 : 各手法の特徴的な分割パターン 活用方法 : 検索システム : 共通トークンは検索精度向上に寄与 NLP処理 : 用途に応じた最適アナライザー選択 品質評価 : トークン化の一貫性・信頼性評価 Tokenization Results Summary - Kuromoji Baseline ========================================================================================== Tokenizer Raw Tokens Final Tokens vs Kuromoji Note ------------------------------------------------------------------------------------------ kuromoji 42 42 100.0% ベースライン sudachi_A 86 49 71.4% クリーニング済み sudachi_B 78 41 53.3% クリーニング済み sudachi_C 78 41 53.3% クリーニング済み mecab 80 45 79.5% クリーニング済み janome 82 45 79.5% クリーニング済み openai 40 17 15.9% クリーニング済み 上の表の読み解き方; Raw Tokens (生トークン数) アナライザーがテキストを最初に分割した直後のトークン(単語)の総数です。この数値が大きいほど、より細かく単語を分割していることを示します。 Final Tokens (最終トークン数) 「生トークン」から助詞(「は」「が」など)、句読点、記号といった検索ノイズになりやすい不要なトークンを取り除いた(クリーニングした)後の数です。 このクリーニング処理により、各アナライザーを公平な土俵で比較できるようになります。 vs Kuromoji (Kuromojiとの類似度) クリーニング後のトークンセットが、基準であるKuromojiのトークンセットとどれだけ似ているかを示す割合(Jaccard係数)です。 計算式 : (両者に共通するトークン数) ÷ (どちらか一方にでも存在するユニークなトークン総数) このパーセンテージが高いほど、そのアナライザーの分割結果がKuromojiと似ていることを意味します。例えば、MeCabとJanomeはクリーニング後に80%の類似度となり、Kuromojiと非常に近い結果を出していることがわかります。 🔍 Unique Tokens Analysis (Cleaned Results vs Kuromoji Baseline) ====================================================================== sudachi_A: ['あり', 'など', 'ね', 'み', 'よる', 'られ', '用い', '痛'] sudachi_B: ['あり', 'など', 'ね', 'よる', 'られ', '具体的', '変形性', '用い', '腰痛症', '説明書', '関節症'] sudachi_C: ['あり', 'など', 'ね', 'よる', 'られ', '具体的', '変形性', '用い', '腰痛症', '説明書', '関節症'] mecab: ['あり', 'など', 'ね', 'られ', '用い'] janome: ['あり', 'など', 'ね', 'られ', '用い'] openai: ['あります', 'ね', 'ロキソニン錠', '具体的', '変形性関節症', '腰痛症', '解熱鎮痛作用', '説明書', '関節リウマチ', '非ステロイド性抗炎症薬'] Kuromoji unique tokens (not found in cleaned versions of others): kuromoji: ['nsaids', '用いる', '痛む'] Common tokens across all tokenizers (after cleaning): ['ロキソニン', '効果', '抑える', '炎症', '発熱', '肩こり'] 📊 統計サマリー: 📊 Kuromojiベースライン総トークン数: 34 📊 全アナライザー共通トークン数: 6 📊 共通度: 17.6% 上記は「各アナライザーのクリーニング後トークン」から「Kuromojiのトークン」を引いた残りのリストです。つまり、 Kuromojiにはないが、そのアナライザーだけが生成したユニークなトークン です。各ツールの辞書や分割ルールの違いが見てとれます。 Kuromojiだけが生成したトークン の後に、クリーニング後に すべてのアナライザーが共通して生成したトークン です。これらは、どのツールを使っても分割結果が変わらない、文章の核となる重要な単語と言えます。 考察 Kuromoji / MeCab / Janome 「専門職」→「専門」「職」、「看護師」→「看護」「師」のように、単語を細かく分割します。 これにより検索ヒット率が向上し、部分一致検索や強調表示に適しています。 Sudachi 「専門職」や「看護師」などの複合語を1トークンとして保持します。 意味のまとまりを重視する分析に向いており、モード切り替え(A/B/C)で粒度を調整できます。 Cモード : 「より良い検索体験を提供したい」が1語扱いになるため、特定の複合語での検索には不向きな場合があります。 Bモード : 実用面でバランスの取れた粒度を提供します。 LLM(GPT-4o) 文脈理解に基づいた分かち書きが可能です。 「介護福祉士」や「認知症」など、語彙として自然なまとまりで出力されます。 トークンの一貫性がないため、Elasticsearchのインデックス用途には不向きですが、意味理解や質問応答に最適です。 まとめ 日本語検索において、どのアナライザーを使うかは「検索したい内容」と「求める粒度」によって変わります。 細かく一致させたいなら Kuromoji 検索文をそのまま一致させたいなら Sudachi Cモード バランス重視なら Sudachi Bモード The post 日本語アナライザーの比較:Kuromoji・Sudachi・MeCab・Janome・LLMの性能検証 first appeared on Elastic Portal .
動画
該当するコンテンツが見つかりませんでした











