Ahogrammer

Deep Dive Into NLP, ML and Cloud

日本語に対応した埋め込みモデルの検索性能を比較する

本記事では、軽量な検索ベンチマークであるNano-BEIRを用いて、日本語に対応した埋め込みモデルの検索性能を比較します。日本語埋め込みモデルの代表的なベンチマークとしては、JMTEB v2.0 があります[1]。JMTEBにも検索タスクは含まれていますが、ベンチマークの実行に時間がかかるため、今回はより高速に評価可能なNano-BEIRを用います。

Nano-BEIRとは

Nano-BEIRは、BEIRを小規模化したベンチマークです。BEIRは、18のデータセットを9種類の検索タスクにまたがって収集した情報検索用のベンチマークです[2]。タスクとして質問応答、事実検証、重複質問検索、議論検索、ニュース検索、ツイート検索、バイオメディカル検索、エンティティ検索、引用予測を含みます。以下にBEIRに含まれるタスクとデータセットを示します。多様なタスクとドメイン横断での評価ができる点が強みです。

BEIRに含まれるタスクとデータセット。画像は`[2]`より引用。

続きを読む

Text Embeddings InferenceをBlackwell GPUで動かす

Hugging Faceが提供するText Embeddings Inferenceは、テキスト埋め込みモデルで高速な推論を提供するためのツール。久しぶりに使おうとしたところ、執筆時点では手元のBlackwellアーキテクチャGPUには正式対応していないことに気がついた。幸い、プルリクエストで対応が進んでいるのを見つけたため、試してみた。

イメージのビルド

まず、リポジトリをクローンしてプルリクエストのブランチをフェッチし、CUDAのcompute capabilityをBlackwell(12.0)向けに設定してDockerイメージをビルドする。

git clone https://github.com/huggingface/text-embeddings-inference.git  
git fetch origin pull/735/head:pr-735
git checkout pr-735

runtime_compute_cap=120                                                
docker build . -f Dockerfile-cuda --build-arg CUDA_COMPUTE_CAP=$runtime_compute_cap

コンテナの起動

ビルドしたイメージを使用して、Text Embeddings Inferenceサーバーを起動する。ビルド時にイメージ名を付け忘れたので、以下ではイメージIDを直接指定している。

model=Qwen/Qwen3-Embedding-0.6B
volume=$PWD/data

docker run --gpus all -p 8080:80 -v $volume:/data [イメージID] --model-id $model --auto-truncate

動作確認

正常に実行できているか確認するために、curlコマンドでリクエストを送る。

curl 127.0.0.1:8080/embed \
    -X POST \
    -d '{"inputs":"こんにちは"}' \            
    -H 'Content-Type: application/json'

正常に動作していれば、埋め込みが返される。

[[-0.015902974,-0.04719861,-0.012466182,-0.05194763,...

Pythonから利用する場合は、huggingface_hubやOpenAIのSDK経由でエンドポイントにアクセスできる。以下はhuggingface_hubを使った例。

from huggingface_hub import InferenceClient

client = InferenceClient()
embedding = client.feature_extraction(
    "こんにちは",
    model="http://localhost:8080/embed"
)
print(len(embedding[0]))
# 1024

バッチ推論をしたい場合は、文字列のリストを渡すことでできる。ただし、feature_extractionのドキュメント上は文字列を渡すことになっているので、将来的に変化する可能性があることに注意が必要。

embedding = client.feature_extraction(
    ["こんにちは", "こんばんは"],
    model="http://localhost:8080/embed"
)
print(len(embedding))
# 2

参考資料

Gemini APIの構成的関数呼び出しでマルチホップQAを試す

Gemini APIのドキュメントを読んでいたところ、「構成的関数呼び出し(compositional function calling)」という機能があることに気がついた。この機能を使うと、LLMが関数を順番に呼び出しながら、段階的に複雑な要求を処理できる。たとえば「現在地の気温」を尋ねられた場合、まず現在地を取得する関数を呼び出し、その結果を用いて天気を取得する関数を実行する、といった流れを自動で組み立ててくれる。

似たようなことはResponses APIでも実現できるが、Geminiの構成的関数呼び出しでは呼び出しの連鎖をより自動的かつシンプルに扱えるように見える。 つまり、アプリ側で明示的なループ制御や状態管理を書く必要がほとんどなく、少ないコードでマルチステップの推論を実現できる。

そこで今回は、この機能を使ってマルチホップQAを解くことを試みる。マルチホップQAとは、複数の情報源や文脈を段階的に推論して答えを導く質問応答のことである。たとえば「ルーヴル美術館が所在する都市の市長の名前」という質問なら、

  1. まず「ルーヴル美術館がある都市=パリ」であることを特定し、
  2. 次に「パリの市長」を調べる、

という二段階の推論が必要になる。ちなみに、この例は日本語のマルチホップ QA データセットであるJEMHopQAの論文から取っている。

この記事では、Geminiと検索API(Tavily)を組み合わせて、このようなマルチホップQAを簡単に実装できるかを試してみる。

準備

まず、必要なパッケージをインストールする。検索にはTavilyを使用する。

pip install -q google-genai tavily-python

実装

次に実装を示す。特別な設定はなく、search関数をツールとして登録しているだけだが、Geminiが自動的に関数呼び出しを行ってくれる。

import os

from google import genai
from google.genai import types
from tavily import TavilyClient

# TavilyClientの用意
tavily_client = TavilyClient(api_key="")


def search(query: str) -> str:
    """Perform a web search and return the relevant context."""
    print(f"クエリ: {query}")
    context = tavily_client.get_search_context(query=query)
    return context


# Geminiのクライアントを用意
client = genai.Client(api_key="")
config = types.GenerateContentConfig(
    tools=[search],
)

# リクエスト
content = "ルーヴル美術館が所在する都市の市長の名前は?"
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=content,
    config=config,
)

# 出力の表示
print(f"回答 : {response.text}")

ルーヴル美術館が所在する都市の市長の名前は?」という質問を実行すると、Geminiは次のように関数を順に呼び出した。

クエリ: 都市 ルーヴル美術館
クエリ: パリ市長の名前
回答 : ルーヴル美術館が所在する都市、パリの市長はアンヌ・イダルゴ氏です。

最初に「ルーヴル美術館がある都市」を検索し、その結果「パリ」であると判断。次に「パリ市長の名前」を検索し、最終的に「アンヌ・イダルゴ」と導いている。Mayors of Europeによると、実行時点ではこの回答は正確だった。

まとめ

この記事では、構成的関数呼び出しを使ってマルチホップQAを解けるかを試した。今回の例のようにWeb検索をするだけなら、Responses API組み込みのweb_searchツールを使って実現することもできるが、自前の関数を呼び出すときに簡潔に実装できそうな点が魅力といえる。

参考資料

  1. コンポジション関数呼び出し | Gemini API
  2. JEMHopQA | GitHub
  3. JEMHopQA: 日本語マルチホップ QA データセットの改良
  4. tavily-python | GitHub