公開ベンチマークだけでLLMを選んでいませんか? 判別力のある評価テストを自作する方法
この記事で分かること
- なぜ公開ベンチマークのスコアだけでモデルを選ぶと失敗しやすいのか
- 「自分の業務で使えるモデル」を見極める評価テストの作り方(考え方の8ステップ)
- 多くの人がハマる落とし穴(候補モデルが全員満点になる/ローカルLLMの隠れた罠)
- 実際にやってみた結果
コードは最小限にとどめ、「どう実装するか」より「どう考えるか」が伝わることを重視しています。細かい実装を追わなくても、評価の全体像がつかめるように書きました。
はじめに
「このタスクにはどのLLMを使うべきか」を決めるとき、多くの場合は公開ベンチマークのスコアやリーダーボードを参考にします。ですが、実際に自社のユースケース(データ分析・要約・コードレビューなど)で候補モデルを比べようとすると、次の壁にぶつかります。
- 公開ベンチマークは汎用タスク向けで、自社の具体的な業務とは条件が違う
- 自分で比較テストを組んでも、候補モデルが軒並み満点になってしまい、優劣が分からない
Amanityでも、ローカルLLM(Ollama経由)とクラウドAPI(Gemini)のどちらを業務に使うべきかを検証する中で、まさにこの壁にぶつかりました。この記事では、そこから作り上げた「 ちゃんと差がつく(=判別力のある)評価テスト 」の作り方を、実際に踏んだ失敗とあわせて紹介します。
この記事でいう「評価テスト」とは?
モデルに解かせる問題(入力データと質問)と、その正解、そして採点方法をワンセットにした自作の小さなテストのことです。学校の小テストを、LLM比較用に自分で作るイメージです。
まず全体像。判別力のある評価テストを自作する8ステップ
先に結論(全体像)を示します。自作の評価テストは、データ分析・要約・コードレビューに限らず、分類・抽出・翻訳など「正解が定義できる」タスクであれば、次の8ステップに一般化できます。細部は後で説明するので、まずは流れをつかんでください。
- 評価したい業務を1つ、具体的に決める。「月次売上CSVから異常値を検出する」のように、任せたい業務を1つに絞ります。
- その業務の「何が正解か」を、人間が判定できる状態にする。正解を定義できないタスクは、そもそも自動採点ができません。
- 正解は手計算せず、別のロジックで計算・検算する。詳しくは後述しますが、「答えを別の方法で求め直す」のがポイントです。
- 候補モデル全部に、同じ問題・同じ条件で解かせる。プロンプト・入力データ・設定をモデル間でそろえます。
- 採点は「通読して雰囲気で」ではなく、項目ごとに機械的に○×をつける。正解の項目数だけチェックを回します。
- 候補が軒並み満点で並んだら「天井効果」と判断する。後述しますが、テストが簡単すぎて差がつかない状態のことです。
- モデル間で差が出るまで、テストの難易度を上げて4〜6を繰り返す。難しくする方向にはコツがあります(後述)。
- 結果をもとに「どの業務にどのモデルを使うか」を判断する。全項目で最強の1台を探すのではなく、「この業務にはこのモデルで十分」という単位で判断します。
なお、Step1(評価したい業務を1つに絞る)は上のとおりシンプルなので、独立した節は設けません。ここからは、つまずきやすいStep2以降のポイントを、実際に使った具体例とあわせて掘り下げていきます。
ポイント1:正解付きのテストデータを「自作」する(Step2〜3)
なぜ、わざわざ自作するのか
手元にある実データをテストに使うと、「モデルの答えが合っているか」の判定基準を毎回人間が作らなければならず、採点が属人的になります。そこで、テストデータそのものを、正解つきで自作する方針にしました。自作にはこんな利点があります。
- 難易度を自由に調整できる(あとで出てくる「難易度を上げる」に必須)
- 作った側が正解を保証できるので、採点基準がぶれない
- 「モデルが事実にないことを口から出まかせで言っていないか(捏造)」も機械的にチェックできる
いちばん大事なコツ:正解は「手計算しない」
ここが最重要です。 正解データを手計算してはいけません。
このプロジェクトの目的は「LLMの計算ミスを見つけること」なので、比較の物差しである正解データ自体に手計算ミスがあっては本末転倒です。そこで、こういう手順にします。
- まず、架空のデータを自動生成する(例:2年分・24ヶ月の売上データ)。このとき、「本物の異常値」「季節性による見せかけの増加」「値上げイベント」などの“仕掛け”を意図的に混ぜておく。
- 正解は、生成に使った設定値を転記するのではなく、「できあがったデータ」を別ロジックで数え直して求める。
言い換えると、「答えを作る計算」と「データを作る計算」をわざと別々にするということです。こうすると、データ生成にバグがあっても、それがそのまま正解のバグになる事故を防げます。
イメージだけコードで示すと、核心は次の一点です(データ生成の全文は本筋ではないので省略します)。
# 正解は「生成時の設定値」ではなく、「生成済みデータ(rows)」を数え直して求める
def revenue_of(product=None, month_prefix=None):
return sum(
r["units"] * r["unit_price"] for r in rows # ← rows = 生成済みデータ
if (product is None or r["product"] == product)
and (month_prefix is None or r["month"].startswith(month_prefix))
)
answer_key = {"total_revenue_jpy": revenue_of()} # 独立ロジックで検算した正解ポイントは、revenue_of() が生成時のパラメータ(成長率などの設定値)を一切使わず、できあがったデータを独立に集計し直していることだけです。細部は分からなくても「答えは別ルートで求め直す」という考え方さえ掴めればOKです。
ポイント2:みんな満点になったら難易度を上げる(Step6〜7)
「天井効果」とは
天井効果(ceiling effect)とは、テストが簡単すぎて、優秀なモデル同士が揃って満点を取ってしまい、実力差を測れなくなる現象のことです。全員が100点の小テストでは、誰が本当に優秀かは分かりませんよね。それと同じです。
実際、クラウドAPIの2モデル(Gemini 2.5 Flash / Pro)で最初のテスト(v1)を組んだところ、3ドメインすべてで両モデルが満点付近になり、この天井効果が起きました。差がつかない=比較テストとして機能していない、ということです。
難易度を上げる「3方向」
天井効果が起きたら、テストを難しくした次のバージョンを作ります。難しくする方向は、大きく3つです。
- ノイズを増やす。正解に紛らわしい要素を混ぜ、単純なパターン認識では解けないようにする
- トラップを混ぜる。「一見正しそうだが実は誤り」「一見異常だが実は正常」を仕込み、額面通りに受け取ると間違えるようにする
- 複数ステップの推論を要求する。単一の事実確認では答えが出ず、複数のデータや手順を組み合わせないと正解にたどり着けないようにする
ドメインごとの具体例はこんなイメージです。
| ドメイン | 難易度を上げる方向の例 |
|---|---|
| データ分析 | 異常値の倍率を小さくしてノイズと紛らわしくする/季節性など「異常に見えて実は毎年の再現パターン」を混ぜる/「売上増の主因は需要増ではなく値上げ」のような複数列にまたがる推論を仕込む |
| 要約 | 文章量とノイズを増やす/文字数制限で優先順位の判断を強制する/後で覆される「ひっかけの決定」を混ぜ、最終結論だけを拾えるか試す |
| コードレビュー | 複数の関数をまたいで初めて気づけるバグにする/数値を追わないと気づけない業務ロジックのバグにする/「一見怪しいが実は正しい」関数を混ぜ、誤検知率も見る |
補足:コードレビューは特に注意が必要です。教科書的な既知バグ(アンチパターン)は学習データに大量に含まれるため、主要モデルはほぼ満点になりがちです。差をつけたいなら、最初から③(業務ロジックの推論・数値トレースが必要なバグ)のレベルで設計するのが効率的です。
実際にこの方針で難易度を上げていくと、次のようにきれいに差がつき始めました。
| バージョン | 設計 | 結果 |
|---|---|---|
| v1(教科書的) | 既知バグ・単純な集計・平易な要約 | 候補が軒並み満点でタイ → 判別不能 |
| v2(トラップ導入) | ノイズ拡大・トラップ混入・文字数制限 | モデル間で差が出始める |
| v3(業務ロジック推論) | 数値トレース必須のバグ設計 | 上位モデルと下位モデルがはっきり分離 |
なお、古いバージョン(v1)は消さずに残しておきます。「最低ラインの品質確認(=これすら落とすモデルは論外)」として使えるからです。
難易度アップのアイデアはLLMに出させてもよい
「具体的にどう難しくするか」で悩んだら、次のようなプロンプトで次バージョンの案自体をLLMに考えてもらうと効率的です(コピペで使えます)。
以下のテストケースで、候補モデルが全て満点(横並びの高スコア)になり、
判別力がありませんでした。
## 現在のテストケース概要
{{データの内容・質問内容・現在の正解}}
## 依頼
このテストの難易度を、次の3方向それぞれで具体的に上げる案を1つずつ、計3つ提案してください。
1. ノイズ方向: 正解に近い紛らわしい要素を増やす
2. トラップ方向: 「一見正しそうだが実は誤り」等を混ぜ、額面通りだと誤答するようにする
3. 推論方向: 複数のデータ・手順を組み合わせないと正解にたどり着けないようにする
各案には「変更後も正解が一意に定まり、別ロジックで検算できる」ことの説明も添えてください。出てきた案のうち「正解を別ロジックで検算できる 」ものを選んで実装すれば、ポイント1の原則も守れます。
ポイント3:採点をLLMに任せる(LLM-as-judge)ときの注意(Step5)
採点役をLLM自身にやらせる方法を LLM-as-judge(LLMを審判役に使う) と呼びます。便利ですが、運用中に一度失敗しました。
失敗の内容:モデルの出力を「ざっと読んで大丈夫そうか」で採点した結果、事実にない内容の捏造を見落としかけました。通読による判定は「なんとなく良さそう」という印象に引っ張られやすく、細部の事実誤認を見逃しがちなのです。
そこで、採点を「正解の項目を1つずつ機械的に○×する」運用に改めました。考え方はこれだけです。
正解キーの項目を1つずつ取り出し、モデルの出力と照合して Yes/No をつける
→ Yes の数を数えて合計スコアにする
(通読しての印象では判定しない)これを実際にLLMへ投げるときは、次のようなプロンプトが使えます(そのままコピペ可)。
あなたは公平な採点者です。以下の「モデルの出力」を「正解キーの項目」と1つずつ照合し、
各項目について Yes/No と根拠を判定してください。一読しての印象ではなく、項目ごとに判定すること。
## 正解キーの項目
{{正解キーの項目}}
## モデルの出力
{{model_output}}
## 出力形式
- 項目1: Yes/No — 根拠(出力のどの記述を根拠にしたか引用)
- 項目2: Yes/No — 根拠
(項目数だけ繰り返す)
## 注意
- 正解キーにない情報(捏造)があれば該当箇所を指摘すること
- 判断が曖昧な項目は「Yes」に寄せず、根拠が弱ければ素直に「No」にすること
- 最後に「合計スコア: X/Y」を出力することもう1つ重要な注意点があります。 採点役には、評価対象とは別系統の高性能モデルを使い、候補モデル自身に自己採点させないこと です(自分のテストに自分で丸をつけるようなもので、甘くなりがちです)。本記事では採点役にClaudeを使いました。
ポイント4:ローカルLLM(Ollama)の隠れた罠 num_ctx
ローカルLLMを使う場合、Step4(同じ条件で実行する)で見落としやすい罠があります。
症状:Ollamaで動かすローカルLLMに、長めのプロンプト(CSVやコードを含む)を渡すと、モデルが古い情報を無視したような回答を返すことがありました。
原因:ollama show で表示されるコンテキスト長(例:qwen2.5なら32768)は、あくまでモデルが対応できる最大値にすぎません。実行時のデフォルトは、VRAMから自動算出されたもっと小さい値(検証環境では4096)で動いていました。実際にはこうなります。
たとえるなら:スペック表に「最大32,768トークン」と書いてあっても、実際に動いている作業机の広さは4,096トークン分しかなく、はみ出した古い部分は黙って捨てられていた、という状態です。
対策はシンプルです。実行時に作業机の広さ(num_ctx)を明示的に指定し、実行後に「実際に何トークン読んだか(prompt_eval_count)」を確認して、切り捨てが起きていないかを裏取りします。
# 要点だけ:
# 1) 呼び出し時に num_ctx を明示指定する(自動算出の小さい値に任せない)
options = {"num_ctx": 8192} # 実測プロンプトに余裕を持たせた値
# 2) 応答の prompt_eval_count が num_ctx を超えていたら、切り捨てを疑う
if prompt_eval_count > num_ctx:
print("WARN: 切り捨てが起きた可能性あり")「スペック表を見て安心していたら、実際はもっと狭い枠で動いていた」というのは起きやすい誤解です。長いプロンプトを扱うときは、 実行前に枠を明示、実行後に消費トークンで裏取り を習慣にすると安全です。
実際にやってみた結果(Step8)
上記の考え方を、実際にGemini 2.5 Flash / Proの比較に適用した結果です。
| ドメイン | v1(易しい版) | 難易度を上げた後 | 結果 |
|---|---|---|---|
| データ分析 | Flash/Proともに高スコアで判別不能 | 異常値の倍率を縮小+季節性トラップ+値上げ推論を追加 | Proのみ真の異常値を検出。Flashは見逃し |
| 要約 | Flash/Proともにほぼ網羅で判別不能 | 400字制限+ひっかけの決定を追加 | Proが必須項目の網羅率でFlashを明確に上回る |
| コードレビュー | Flash/Proともに既知バグを完全検出 | 業務ロジック推論・数値トレース必須のバグに変更 | Proのみ全問正解。Flashは一部見逃し |
各テストを1回ずつ実行した一次検証(n=1)なので、統計的に確定した結論ではありません。それでも、 「差がつくように意図して設計しないと、モデル比較は簡単にタイになって何も分からない」 という本記事の主張を裏付ける結果になりました。
応用:同じテストをローカルLLMにも当ててみると
このv1〜v3のテストを、後日そのままローカルLLM(qwen2.5:7b / llama3.1:8b / gemma3)にも適用しました。結果は「v1の易しい段階から、すでにクラウドAPIに明確に負けている」というものでした。
- データ分析:v1の総売上集計の時点で、llama3.1:8bは桁違いの誤差、qwen2.5:7bは-44%、gemma3は-18%。天井効果でタイになる余地すらなし
- 要約:v1では健闘するモデルもあったが、v2(400字制限+ひっかけ)で大きく差が開いた
- コードレビュー:v2まではクラウドAPIに匹敵したが、v3(業務ロジック推論)で全ローカルモデルが崩壊
ここから分かるのは、 天井効果と難易度アップの設計が効くのは「拮抗した2モデルを比べる場面」 だということです。実力差が大きいモデル同士なら、易しいテスト(v1)の時点ですでに差が見えることもあります。
まとめ・次のアクション
最後に、冒頭の8ステップをチェックリストにしました。自分の業務でそのまま使ってみてください。
- Step1: 評価したい業務を1つ、具体的に決めたか
- Step2: その業務の「正解」を人間が判定できる状態にしたか(正解つきテストを設計したか)
- Step3: 正解は手計算せず、別ロジックで計算・検算しているか
- Step4: 候補モデル全部に、同じ問題・同じ条件で解かせたか
- Step5: 採点は通読ではなく、正解の項目ごとに機械的に○×しているか
- Step6: 候補が軒並み満点になる「天井効果」が起きていないか確認したか
- Step7: 天井効果が起きたら、ノイズ・トラップ・複数ステップ推論で難易度を上げ、差が出るまで繰り返したか
- Step8: 結果をもとに、どの業務にどのモデルを使うか判断できたか
- (Ollama利用時)
num_ctxを明示し、prompt_eval_countで切り捨てを確認したか
公開ベンチマークのスコアだけでなく、自社のユースケースに合わせた「差がつくテスト」を自分で設計できれば、モデル選定の納得感は大きく変わります。まずは1つの業務から、小さく試してみてください。
関連記事
この検証を行ったのは 株式会社Amanity です。 自社で手を動かして技術を確かめたうえで、モバイルアプリ・LINEアプリ・Webアプリの受託開発を行っています。
開発について相談する株式会社Amanity
福岡を拠点に、AIを活用したモバイル・Web・LINEアプリの受託開発を行っています。
