【開発日誌】AIに「オタクの気持ちになって」と言ったら「ツンデレ」でベンチマークし始めた話 (Embedding選定)

この記事は筆者のいい加減な指示のもと、生成AIが自分で調べて盛り盛りして作成しています。

はじめに:AIの「分かりすぎている」提案

現在構築中のフィギュア写真投稿サイト「FigureFan」では、57言語以上に対応したクロス言語検索の実装を進めています。

私はアシスタントAIに、検索エンジンの核となるEmbedding(ベクトル化)モデルの選定を依頼しました。
AIは最初、真面目に「平和 (Peace)」という単語を TranslateGemma の仕様に基づく 59の言語・方言エントリ で翻訳してベクトルの距離を測り、「このモデルが優秀です!」と報告してきました。

しかし、本サービスは世界中のファンが交流する場所です。
私は真面目に問いかけました。
「今回の検証だけで十分なのか? アニメやオタク文化を重視したサービスの目的として、その辞書的な評価だけで本当に合致していると言えるのか?」

私は具体的なキーワードの指示は一切していません。
しかし、AIが「承知しました。ドメインに特化した検証を行います」と言って持ってきたテスト計画を見て、私は思わず吹き出しました。

AIが提案した検証用データセット(抜粋):
・初音ミク (Hatsune Miku)
・エヴァンゲリオン (Evangelion)
・ケモノ耳 (Kemonomimi)
ツンデレ (Tsundere)

ツンデレwww
まさかAIからその単語が出てくるとは。
「お前、分かってるな」ということで、AIが自ら構築したこのベンチマークを採用することにしました。


1. 検証環境と使用モデル

1.1 環境 (GPU Offload)

  • CPU: Legacy (AVX非対応)
  • GPU: NVIDIA GeForce GTX 1080 Ti
  • Runtime: Ollama (Docker, version 0.14.3)

注意: AVX非対応CPU環境のため、DockerでGPUパススルー設定を行い、Ollamaの推論を完全にGPUで実行させています。

1.2 検証モデル詳細 (再現性情報)

本検証で使用したモデルのOllama上のID(Short)と、完全なDigestは以下の通りです。

モデル名Ollama TagModel ID (Short)Dim
embeddinggemmaembeddinggemma:latest85462619ee72768
paraphrase-multilingualparaphrase-multilingual:latestba13c2e06707768
bge-m3bge-m3:latest7907646426071024
Full Digests (SHA256):
  • embeddinggemma: sha256:0800cbac9c2064dde519420e75e512a83cb360de3ad5df176185dc69652fc515
  • paraphrase-multilingual: sha256:aa99ebfc77f41e752c215353c08bdb22a75d7858a629934fa04f587d3cdee165
  • bge-m3: sha256:daec91ffb5dd0c27411bd71f29932917c49cf529a641d0168496c3a501e3062c
※Digestは ollama show <model> --modelfile コマンドの FROM 行から取得した、モデルの実体(Blob)を指すハッシュ値です。これにより、latestタグが更新されてもベースモデルの同一性を追跡可能です。完全な再現には、パラメータ設定を含む Modelfile 全体の同一性も必要となります。

2. 検証結果

検証1: 基礎的な多言語対応能力

目的: 一般的な単語レベルでの言語間距離を測定する。
対象言語: TranslateGemmaの仕様に基づいた59の言語・方言エントリ
方法: 「平和 (Peace)」という単語のベクトルを59言語で生成し、日本語ベクトルとのコサイン類似度を算出。その単純平均値(日本語の自己類似度1.0を含む)をスコアとする。

結果(対日本語類似度・平均)

  • paraphrase-multilingual: 0.7322 (1位)
  • embeddinggemma: 0.6445
  • bge-m3: 0.5133
使用した59言語の翻訳リスト(再現用・修正版)
ja: 平和, en: Peace, zh-CN: 和平, zh-TW: 和平, ko: 평화, vi: Hòa bình, th: สันติภาพ, id: Perdamaian, fil: Kapayapaan, fr: Paix, de: Frieden, es: Paz, it: Pace, pt: Paz, ru: Мир, nl: Vrede, tr: Barış, ar: سلام, hi: शांति, bn: শান্তি, pa: ਸ਼ਾਂਤੀ, gu: શાંતિ, or: ଶାନ୍ତି, ta: அமைதி, te: శాంతి, kn: ಶಾಂತಿ, ml: സമാധാനം, my: ငြိမ်းချမ်းရေး, km: សន្តិភាព, lo: ສັນຕິພາບ, ms: Keamanan, uk: Мир, pl: Pokój, ro: Pace, hu: Béke, el: Ειρήνη, cs: Mír, sv: Fred, no: Fred, da: Fred, fi: Rauha, bg: Мир, sr: Мир, hr: Mir, sk: Mier, sl: Mir, et: Rahu, lv: Miers, lt: Taikos, fa: صلح, he: שלום, hy: Խաղաղություն, ka: მშვიდობა, az: Sülh, kk: Бейбітшілік, uz: Tinchlik, ky: Тынчтык, tk: Parahatçylyk, tg: Сулҳ

※注意: コサイン類似度の絶対値はモデルごとのベクトル空間の分布に依存するため、異なるモデル間で数値を直接比較しても優劣の証明にはなりません。あくまで日本語ベクトルとの相対的な距離感の参考値です。

検証2: オタクコンテキストでの検索性能

AIが作成したデータセット(N=10)を使用し、実践的な検索テストを行いました。
※本テストはサニティチェック(健全性確認)を目的とした小規模なものであり、統計的な有意性を保証するものではありません。

検証条件

  • プレフィックス: 実装のシンプルさと公平性を保つため、全てのモデルで素のテキストを入力しました(モデル推奨のprefixは不使用)。
  • 評価手順: クエリと投稿の双方をL2正規化した後、内積(コサイン類似度)でソートし、Top 3を取得して評価。

テストデータセット全文 (再現用)

[Posts]
1. (JA) 初音ミクの1/7スケールフィギュア。ツインテールが美しい。
2. (EN) Hatsune Miku Nendoroid, very cute and chibi!
3. (ES) Una figura de Hatsune Miku cantando en el escenario.
4. (EN) EVA Unit-01 Test Type. The purple paint is perfect.
5. (JA) 汎用ヒト型決戦兵器 人造人間エヴァンゲリオン初号機
6. (EN) Rei Ayanami - Bandages version. Quiet personality.
7. (JA) このアニメキャラは典型的なツンデレだね。素直になれないところが可愛い。
8. (EN) She is a classic Tsundere character. 'It's not like I like you or anything!'
9. (EN) Kemonomimi girl with fluffy fox ears and tail.
10. (JA) 獣耳の女の子。狐の尻尾がモフモフ。

[Queries & Targets]
Q1: "初音ミク" -> Targets: [1, 2, 3] (3件)
Q2: "Mecha robot" -> Targets: [4, 5] (2件)
Q3: "ツンデレ" -> Targets: [7, 8] (2件)
Q4: "Fox ears" -> Targets: [9, 10] (2件)
Q5: "Evangelion" -> Targets: [4, 5, 6] (3件)

詳細スコア内訳 (Recall @ 3)

Avg Score = (Top3に含まれた正解数 / クエリごとの全正解数) の5クエリ算術平均

モデル名 Q1
(Miku)
Q2
(Mecha)
Q3
(Tsundere)
Q4
(Fox)
Q5
(EVA)
Avg
embeddinggemma 1.001.001.001.001.00 1.00
bge-m3 0.670.501.001.001.00 0.83
paraphrase-multilingual 0.670.500.501.000.67 0.67

embeddinggemma、まさかの満点。
GoogleのGemmaアーキテクチャをベースとしたこのモデルは、「ツンデレ」のニュアンスを言語を超えて完全に理解しているようです。


3. 最終的な構成

本番環境でも「L2正規化+内積(Cosine)」による検索を採用します。

Search Flow (Read) Indexing Flow (Write) User Post Search API (FastAPI) Job Worker (Async) Ollama (Embed) Meilisearch Keyword Qdrant Vector

実装のポイント

  1. Indexing Flow (非同期):
    • Job Workerが Ollama (embeddinggemma) でベクトル化します。
    • ベクトルデータはVector DBであるQdrantへ保存します。
    • 検索用テキストとメタデータはSearch EngineであるMeilisearchへ保存します。
  2. Search Flow (同期):
    • Search APIは Keyword検索 (Meilisearch)Vector検索 (Qdrant) を並列実行。
    • 双方から Top-60 件を取得し、RRF (Reciprocal Rank Fusion, k=60) アルゴリズムで統合して最終ランキングを生成します。同一ドキュメントIDはマージされます。
      ※Top-60およびk=60の設定は、RRFにおける標準的な初期パラメータとしての例です。

まとめ

私の「オタク文化を重視したい」という真面目な問いかけに対し、AIは「ツンデレ」という最適解を自律的に導き出し、さらにそれを完璧に理解するモデルを見つけ出してきました。

AIのコンテキスト理解力と、意外なユーモアセンス(?)に感心させられた一日でした。

結論:Embeddingモデルに迷ったら、とりあえずツンデレでテストしろ。