1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PR: データブリックス・ジャパン株式会社
Omnigentによるメタハーネス入門(1)基礎編

はじめに

前回の記事では、TypeSafe 社の Jev を LLM ジャッジとして使えるかを、紛らわしい誤答72件で確かめました。

その記事の最後で触れたとおり、Databricks のブログで、Jev と同じ使い方ができるオープンなモデルを Databricks 上にデプロイし、SQL の ai_query から呼ぶ方法が紹介されています。

今回はこの手順でモデル (SemIf-OpenJev) をデプロイし、前回と同じ72件で LLM ジャッジとして試しました。

先に結論を書きます。

  • ブログのノートブックを「すべて実行」するだけで、GPU のサービングエンドポイントまでできあがり、SQL の ai_query から呼べた
  • LLM ジャッジとしての一致率は81.9%で、Jev (88.9%) より低かった。見抜ける誤答と見逃す誤答の顔ぶれも Jev とは違った
  • Jev と違って、見逃した誤答の確率が0.97まで上がることがあり、確率だけでは正しい回答と見分けにくかった
  • 同じ入力なら、何度呼んでも確率はまったく同じだった
  • 自分のワークスペースの中で動き、テーブルの行ごとに SQL から判定できる

そのまま Jev の代わりに LLM ジャッジとして使うより、分類のように選択肢がはっきりした判定を、自分のデータに対して大量に回す用途に向いていると感じました。

SemIf-OpenJev とは

SemIf-OpenJev は、Jev と同じ使い方ができるように作られたオープンソースのプロジェクトです。MIT ライセンスで公開されています。リポジトリには「Jev や TypeSafe とは無関係」と書かれており、TypeSafe の Jev そのものではありません。

中身は Qwen/Qwen3.5-4B です。モデルを再学習せずにそのまま使い、選択肢ごとの確率を1回の処理で返します。文章は生成しません。

入力の形は、Jev と少し違います。

Jev (OpenRouter) SemIf-OpenJev
判定対象 state state
質問 questions に複数書ける question に1つ
答えの形 yes/no、選択肢、段階評価の3種類 選択肢だけ (2〜16個)。yes/no も選択肢2つとして渡す
返ってくるもの yes の確率、または選択肢ごとの確率 選ばれた選択肢と、選択肢ごとの確率

Databricks にデプロイする

ブログのノートブックを実行する

ブログからリンクされているノートブックを、ワークスペースにインポートして実行しました。

ノートブックは次の順に進みます。

  1. SemIf のリポジトリと Qwen の重みをダウンロードする
  2. ホテルのレビュー6件で、ノートブック上でモデルを動かして確かめる
  3. MLflow に記録し、Unity Catalog にモデルを登録する
  4. GPU のサービングエンドポイントを作る
  5. エンドポイントを呼んで、同じレビューで確かめる
  6. SQL の ai_query からエンドポイントを呼ぶ

GPU は2か所で使います。

  • ノートブックを動かすコンピュート: AI Runtime (サーバレス GPU) の A10 で、環境は AI v5 です。ノートブックの冒頭で、この条件を満たしていないと止まるようになっています
  • サービングエンドポイント: AWS では A10G (GPU_MEDIUM)、Azure では A100 (GPU_LARGE) です

AI Runtime は AWS と Azure で使えます。GCP では使えないので、このノートブックも動きません。

ウィジェットに4つの値を入れる

最初のセルを実行すると、ノートブックの上部にウィジェットが4つ出ます。

Screenshot 2026-09-26 at 13.28.44.png

  • カタログとスキーマ: モデルを登録する場所です。モデルを作成できる権限が必要です
  • モデル名: Unity Catalog に登録するモデルの名前です
  • エンドポイント名: 新しく作られるサービングエンドポイントの名前です

エンドポイント名には注意が必要です。ノートブックは、同じ名前のエンドポイントがすでにあると、新しく作らずにそのエンドポイントの設定を書き換えます。ワークスペースで使われていない名前を付けてください。

値を入れたら「すべて実行」です。

登録とエンドポイントの作成

モデルの登録では、env_pack="databricks_model_serving" を指定しています。登録するときに Python の環境と重みをまとめておく方法で、エンドポイントを作るときにコンテナを作り直さずに済みます。

version = mlflow.register_model(model_info.model_uri, UC_MODEL, env_pack="databricks_model_serving").version

エンドポイントは、GPU の Small サイズで作られます。使われないときは0台まで縮小する設定です。

Screenshot 2026-09-26 at 13.40.26.png

ログには、次のようなメッセージが出ていました。どれも処理は止まらず、そのまま最後まで進みました。

  • 重みを読み込むときの 'tuple' object has no attribute 'config'
  • causal_conv1d が入っていないので、遅い代わりの処理で動くという警告
  • Hugging Face の認証トークンが設定されていないという警告

ホテルレビューの判定

ノートブックの例題は、ホテルのレビューを good と bad に分けるものです。

レビューの内容 判定 good の確率
部屋も朝食も良く、また予約した good 1.00
エアコンが3日壊れたまま bad 0.00
立地は良いが、壁が薄くてうるさい bad 0.16
「駐車場に40ドル払えて最高」という皮肉 bad 0.01
普通だが清潔で静か good 1.00
チェックインで1時間待ったが、スイートに変更してもらえた good 0.98

皮肉を書いたレビューも、bad と判定しています。

SQL から呼ぶ

エンドポイントは、SQL の ai_query から呼べます。SemIf の入力は OpenAI 互換のチャット形式ではありませんが、ai_query はこうした独自の形式のエンドポイントにも、named_struct で組み立てた値をそのまま送れます。

SELECT
  decision.choice AS verdict,
  decision.options[0].probability AS p_good,  -- 選択肢は送った順に返るので、0番目が good
  review
FROM (
  SELECT
    review,
    ai_query(
      endpoint => 'taka-jev-endpoint',
      request => named_struct(
        'id', review_id,
        'state', review,
        'question', 'Was this a good or bad hotel stay?',
        'options', array(
          named_struct('id', 'good', 'description', 'The guest had a good stay and would recommend the hotel.'),
          named_struct('id', 'bad', 'description', 'The guest had a bad stay and would not recommend the hotel.')
        )
      ),
      returnType => 'STRUCT<choice: STRING, options: ARRAY<STRUCT<probability: DOUBLE>>>'
    ) AS decision
  FROM hotel_reviews
)

テーブルの行ごとに、判定と確率が列として返ってきます。結果は、Python からエンドポイントを呼んだ場合と同じでした。

前回の72件で LLM ジャッジとして試す

渡し方

前回の Jev の呼び出しに合わせて、次のように渡しました。

  • state: ドキュメントの抜粋、質問、回答をまとめた文
  • question: 前回と同じ評価基準の文
  • options: correct と incorrect の2つ。説明文は前回の Jev と同じ
CRITERION = (
    "Is the candidate answer factually and technically correct for the question, "
    "using the supplied documentation excerpt and respecting any version specified in the question?"
)
OPTIONS = [
    {"id": "correct", "description": "The answer is correct and consistent with the documentation excerpt."},
    {"id": "incorrect", "description": "The answer is wrong, contradicts the excerpt, or ignores a version constraint in the question."},
]


def make_request_row(r):
    state = (
        f"Documentation excerpt:\n{r['context']}\n\n"
        f"Question:\n{r['question']}\n\n"
        f"Candidate answer:\n{r['answer']}"
    )
    # id はリクエストの中で重複しないようにする
    return {"id": f"{r['aid']}-{r['lang']}", "state": state, "question": CRITERION, "options": OPTIONS}


def query(rows):
    body = {"dataframe_records": rows}
    return w.api_client.do("POST", f"/serving-endpoints/{ENDPOINT}/invocations", body=body)["predictions"]

「正しい」の確率が0.5 以上なら「正しい」と判定します。前回と同じ基準です。

一致率

人手で付けた正誤と、判定が一致した件数です。Jev は前回の記事の2回分の結果です。

英語 日本語 全体
SemIf-OpenJev 30/36 29/36 59/72 (81.9%)
Jev (1回目) 32/36 32/36 64/72 (88.9%)
Jev (2回目) 32/36 32/36 64/72 (88.9%)

SemIf の間違いは13件で、Jev の8件より多くなりました。

見抜ける誤答と見逃す誤答の顔ぶれが違う

件数だけでなく、どの回答を間違えるかも Jev とは違いました。

Jev が見逃した誤答のうち、2件は SemIf が見抜きました。

回答 SemIf Jev (1回目 / 2回目)
MERGE の句の名前は正しいが、対象の説明が逆 (英語版) 0.27 0.68 / 0.76
シークレットの作成に必要な権限が1つ抜けている (日本語版) 0.41 0.71 / 0.67

逆に、Jev が2回とも見抜いた誤答を、SemIf が見逃したものがありました。

回答 SemIf Jev (1回目 / 2回目)
「マテリアライズドビューはパイプラインからしか作成できない」(英語版) 0.85 0.09 / 0.09
同上 (日本語版) 0.97 0.34 / 0.38
VERSION AS OF に日付を渡す (日本語版) 0.88 0.08 / 0.09
スコアラーの引数名が expected_response になっている (英語版) 0.56 0.13 / 0.15
INSERT のほかの行は書き込まれるという補足 (日本語版) 0.59 0.21 / 0.17

正しい回答を「誤り」と判定したものが1件ありました。 ボリュームのパスを聞く問題 (英語版) で、回答は /Volumes/main/sales/raw/report.csv というパスだけです。確率は0.15 でした。Jev は、正しい回答を「誤り」と判定したことは一度もありませんでした。

一方、次の誤答は、SemIf も Jev も見逃しています。

  • 削除ベクトルで、後で実行するコマンドが違う回答 (英語版と日本語版)
  • 権限が1つ抜けている回答 (英語版)
  • CHECK 制約で、ほかの行は書き込まれるという補足 (英語版)
  • タイムトラベルで、日付だけの文字列はエラーになるという補足 (日本語版)

どれも、回答の前半は正しく、後半や補足の一部だけが誤っているものです。前回の記事で Jev が見逃しやすいと書いた種類の誤答は、SemIf にとっても見逃しやすいようです。

確率では見分けにくい

前回の記事では、Jev が返した確率が次のように分かれていました。

  • 正しい回答: 0.91以上
  • 見逃した誤答: 0.5〜0.8
  • 回答全体が誤っている誤答: 0.01〜0.04

見逃した誤答が「正しい」と判定されても、確率は正しい回答より明らかに低かったので、確率が中途半端な判定だけを LLM ジャッジに回せば見逃しを補えました。

SemIf では、確率が次のように分かれました。

回答の種類 件数 最小 中央値 最大
正しい回答 24 0.15 1.00 1.00
見抜いた誤答 36 0.00 0.03 0.47
見逃した誤答 12 0.53 0.81 0.97

見逃した誤答の確率が0.97まで上がり、正しい回答と重なっています。前回と同じく「確率が0.2〜0.8の判定だけを LLM ジャッジに回す」形で計算すると、13件の間違いのうち7件がこの範囲の外に残りました。前回、LLM ジャッジはこの72件をすべて正しく判定しているので、回した分は正しく直る前提での計算です。

ブログのノートブックにも、確率について次の注意書きがあります。

確率は、与えた選択肢の間での相対スコアであり、較正済みの信頼度ではありません。

しきい値で判定する前に、自分のデータで人手の正誤と照らし合わせ、temperature (確率に変換する前にスコアを割る値) を調整するように、と書かれています。今回は既定値の1.0 のままで試しています。

同じ入力なら、確率はまったく同じ

同じ入力で2回呼んだところ、72件すべてで確率が完全に一致しました。1件ずつ呼んでも、まとめて呼んでも、ai_query から呼んでも同じです。

Jev では、同じ入力でも呼ぶたびに確率が少し変わり、0.5付近の回答では判定が入れ替わることもありました。評価の結果を後から再現したい場合は、SemIf のほうが扱いやすいと思います。

時間

呼び方 1件あたりの時間
1件ずつ (中央値) 約104ms
36件ずつまとめて 約72ms
ai_query (72件で10.1秒) 約141ms

前回の Jev は、OpenRouter 経由で1件あたり約200ms でした。ただし、SemIf は自分専用の GPU エンドポイントで動いていて、Jev は外部の API です。条件が違うので、この数字だけでどちらが速いとは言えません。

料金の考え方も違います。Jev は呼び出した量に応じて料金がかかります。SemIf は、GPU のエンドポイントが動いている時間に応じて料金がかかります。エンドポイントは使われないと0台まで縮小しますが、縮小した後の最初の呼び出しは、GPU の起動に数分かかります。

まとめ

Databricks のブログの手順で SemIf-OpenJev をデプロイし、前回と同じ72件で LLM ジャッジとして試して、分かったことをまとめます。

  • ブログのノートブックを「すべて実行」するだけで、GPU のサービングエンドポイントまでできあがる。必要なのは AI Runtime (A10) と GPU のサービングエンドポイント
  • エンドポイント名に既存の名前を入れると、そのエンドポイントの設定が書き換わる
  • SQL の ai_query から、テーブルの行ごとに判定と確率を取り出せた
  • LLM ジャッジとしての一致率は81.9%で、Jev (88.9%) より低かった
  • Jev が見逃した誤答を見抜いたものが2件、Jev が見抜いた誤答を見逃したものが5件あった
  • 回答の一部だけが誤っている誤答は、SemIf も Jev も見逃しやすかった
  • 見逃した誤答の確率が0.97まで上がり、確率だけでは正しい回答と見分けにくかった
  • 同じ入力なら、何度呼んでも確率はまったく同じだった

一番の収穫は、「Jev と同じ使い方ができる」ことと「Jev と同じように判定できる」ことは別だと分かったことでした。入力と出力の形がそろっていても、中身のモデルが違えば、どの回答を見逃すかも、確率の分かれ方も変わります。一方で、自分のワークスペースの中で動き、SQL からテーブルの行ごとに判定でき、結果が毎回同じ、という点は SemIf ならではです。LLM ジャッジとしてそのまま置き換えるより、分類のように選択肢がはっきりした判定を、自分のデータに対して大量に回す用途で試してみると良さそうです。どちらの用途でも、自分のデータの一部に人手で正誤を付けて、確率がどう分かれるかを先に確かめておくのがおすすめです。

参考リンク

はじめてのDatabricks

はじめてのDatabricks

Databricks無料トライアル

Databricks無料トライアル

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?