――LLM生成 × 有識者評価 × Fine-tuningで検証する実験計画
はじめに
LLMをFine-tuningすれば、本当に回答品質は向上するのでしょうか。
Fine-tuningの解説では、
- データセットを用意する
- モデルを学習する
- 学習後のモデルを評価する
という流れがよく紹介されます。
しかし、実際に試そうとすると、もっと手前に大きな問題があるのではと感じるようになりました。
実際lossが下がっても、全然イケてないことが多いです。
これは掲載中のなろう系小説の第一話を書かせるためにSFT+LoRAでチューニングしても全く精度が
上がらなかったり、有名どころのマーケティングデータセットをSFT+LoRAでチューニングしてもやっぱり精度が上がらないという現象がありました。 結果最新モデルにすると精度が上がるというありきたりの結果になっています。
このことから
その「教師データ」は、本当に正しいのか?
** データセットの構成は適切なのか? **
さらに、人間による評価を加える場合には、
評価している人の判断は信用できるのか?
という問題も発生します。
そこで今回の実験計画では、単にFine-tuningを実行するだけではなく、
この記事を読むために最低限知っておけばよいこと
Fine-tuning:既存LLMを追加データで調整すること
SFT:正解例を見せて学習させる方法
LoRA:モデル全体ではなく一部だけ効率よく学習する方法
Dataset:モデルに学ばせる例題集
Baseline:改善前の比較基準
この記事の位置付け
今回のシリーズ全体では、次の流れで検証を進める予定です。
[今回]
実験計画
↓
Baseline評価
↓
30件の小規模データセット
↓
評価者・評価基準の検証
↓
Fine-tuning①
↓
100件へ拡張
↓
Fine-tuning②
↓
完全未学習データによる最終評価
↓
品質・コスト・効果の考察
なぜ最初に評価方法を決めるのか
OpenAIのModel Optimizationの公式ドキュメントでは、モデル改善の最初のステップとして次のように説明されています。
“Write evals that measure model output, establishing a baseline for performance and accuracy.”
つまり、
まず評価方法を作り、現在の性能を測る
ことから始めます。
その後、
評価
↓
改善
↓
再評価
↓
改善
というサイクルを繰り返します。
今回の実験もこの考え方を基本とします。
ただし今回は、ここからさらに一歩踏み込みます。
だけではなく、
ところまで検証します。
これが今回の実験の中心テーマの一つです。
実験全体像
今回のデータセットは、次のように段階的に作ります。
| Dataset | 件数目安 | 内容 | 主な用途 |
|---|---|---|---|
baseline_dataset_v0.5 |
30前後 | 同一問題に複数LLMが回答 | 現状性能比較 |
dataset_v1.0 |
約30 | LLM生成による候補データ | 小規模PoC |
dataset_v1.5 |
約30 | 有識者によるA/B/C評価済み | Fine-tuning① |
dataset_v2.0 |
約100 | LLMによる拡張データ | 規模拡張 |
dataset_v2.5 |
約100 | 有識者評価済みデータ | Fine-tuning② |
eval_dataset_v3.0 |
50〜100以上 | 学習に使わない完全Holdout | 最終評価 |
モデルについては、Baselineでは例として以下を比較します。
Fable
GPT-5.6 Sol
Gemini
Swallow
重要なのは、
同じ問題、同じ条件で比較すること
です。
STEP 0:仮説と「成功」の定義
最初に、Fine-tuningを実行する前に仮説を決めます。
今回の中心仮説は次です。
LLMが生成したデータをそのまま学習するより、複数の有識者によって選別・修正されたデータを使用した方が、未知データに対する回答品質が高くなるのではないか。
さらに副仮説を設定します。
仮説A
30件程度の高品質データでも、Baselineより改善が確認できる。
仮説B
30件から100件へ増やすことで、さらに性能が改善する。
仮説C
単純なLLM生成データより、有識者評価を通したデータの方が改善効果が高い。
仮説D
評価者によって判断には一定のばらつきが存在する。
仮説E
評価基準を改善すると、評価者間の一致度が上昇する。
測定するもの
Fine-tuning後のスコアだけではなく、以下を計測します。
モデル品質
評価者一致度
データ作成時間
人間の作業時間
APIコスト
有識者への支払額
1データあたり作成コスト
Fine-tuningコスト
最終的には、
品質改善量
─────────────
データ作成コスト
という観点でも比較します。
STEP 1:baseline_dataset_v0.5を作る
まず、Fine-tuningを行う前の状態を測定します。
例えば以下のような構造になるように30問を用意します。
{
"product": "軽量ランニングシューズ",
"target": "週末にランニングする30代会社員",
"feature": "従来モデルより20%軽量",
"benefit": "長距離でも疲れにくい",
"tone": "シンプル・都会的",
"constraint": "15文字程度",
"copy_a": "軽さは、走りを変える。"
"copy_b": "軽さは正義"
"chosen": "...",
"rejected": "...",
"preference": "copy_a",
"chose_reason": "具体性が高く...",
"rejected_reason": "..."
}
つまり、
商品 → ターゲット → 特徴 → ベネフィット → トーン → 制約 → コピー候補 -> 有識者評価
という因果関係をデータに持たせます。
それぞれについて、
Fable
GPT-5.6 Sol
Gemini
Swallow
に回答してもらいます。
各自のモデルに回答してもらう際のjson構造ではモデル名は記載しますが、
最終的に人が判断するときにはcopy_a,copy_bのようにモデル名を隠します。
つまり4択+全部だめな場合有識者作の5つになります。
保存する情報
単に回答本文だけを保存するのではなく、可能な限り以下を記録します。
{
"question_id": "Q001",
"prompt": "...",
"model": "...",
"model_version": "...",
"system_prompt_version": "...",
"generation_parameters": {},
"response": "...",
"created_at": "..."
}
LLMはモデルやバージョンによって挙動が変わるため、再現性確保のためにモデル情報も記録します。
OpenAIもModel Optimizationの説明で、LLM出力は非決定的であり、モデルファミリーやsnapshotによって挙動が変化し得ることを説明しています。
MLflowへ保存
今回の検証では、可能なものはMLflowへ記録します。
例えば、
experiment
├─ baseline-fable
├─ baseline-gpt
├─ baseline-gemini
└─ baseline-swallow
とします。
MLflow TrackingはRun単位でmetadataやartifactを記録できます。
さらにMLflowのEvaluation Datasetは、同じデータを使って異なるモデルやプロンプトを比較する用途にも使えます。
STEP 2:dataset_v1.0を約30件作る
次にFine-tuning用データセット候補をLLMで生成します。
ただし、ここでいきなり100件、1000件とは作りません。
まず約30件です。
理由は、この段階では、
データ生成方法そのものが正しいか分からない
からです。
STEP 3:評価者そのものを評価する
ここが今回追加した重要な実験です。
通常は、
LLM回答
↓
有識者
↓
正解
と考えてしまいます。
しかし、
有識者なら誰でも同じ結論になるのでしょうか?
これを実験します。
評価者候補を複数集める
例えば、
候補者:10〜20人
を募集します。
クラウドソーシングや専門家ネットワークなどを利用することを想定します。
最初から100件を発注するのではなく、
5〜10問
程度のテスト評価をお願いします。
A/B/C方式
評価画面ではモデル名を隠します。
Question
Answer A
...
Answer B
...
どちらが良いですか?
[ ] A
[ ] B
[ ] 同等
[ ] 両方問題あり
さらに、
評価理由
を書いてもらいます。
そしてA/B両方に問題がある場合には、
Answer C
として有識者による修正版を作ってもらいます。
なぜC案を残すのか
A/Bしか保存しない場合、
Aの方がマシ
というだけで、
本当に良い回答
がDatasetに存在しない可能性があります。
そこで、
A
B
Preference
理由
C
まで保存します。
これにより、後から複数のFine-tuning方式に変換できます。
評価者間一致度を測る
例えば3人が10問を評価します。
Q1 Q2 Q3 Q4 Q5
Expert1 A B A B B
Expert2 A B A B A
Expert3 B B A B B
まず単純な一致率を計算します。
さらに複数評価者の一致度を扱う指標として、Fleiss' κも候補とします。
statsmodels公式ドキュメントにも、
“Fleiss’s kappa statistic for inter rater agreement.”
として実装されています。
重要なのは、
一致率が低かったら評価者を責めるのではない
ことです。
原因として、
評価基準が曖昧
問題文が曖昧
A/Bの差が小さい
評価者の専門分野が違う
そもそも正解が一意ではない
可能性があります。
評価者の専門分野も分ける
例えば文章生成タスクの場合、
Evaluator A
文章・コピーの専門家
Evaluator B
マーケティング実務者
Evaluator C
営業・事業側の実務者
という構成も試します。
すると、
文章品質:5
マーケティング:4
営業観点:2
のような多面的なDatasetを作れる可能性があります。
STEP 4:評価基準を改善する
例えば評価者一致率が低かったとします。
Agreement = 45%
この時点でFine-tuningには進みません。
原因分析を行う
ChatGPT Workなどを使って、
不一致だった問題
を抽出します。
例えば、
問題の曖昧性
回答の長さ
専門用語
評価軸の曖昧さ
などを分類します。
その結果をもとにRubricを修正します。
同じデータで再評価する
例えば、
Evaluation Rubric v0.1
↓
Agreement 45%
Rubric修正
Evaluation Rubric v0.2
↓
Agreement 78%
となれば、
モデルの問題ではなく、
評価設計が原因だった
可能性が高くなります。
STEP 5:dataset_v1.5を作る
評価方法と評価者がある程度固まったら、
dataset_v1.0
を複数有識者で評価し、
dataset_v1.5
を作ります。
Raw Datasetを残す
この段階で重要なのは、
SFT用Datasetだけを作らないことです。
Raw形式として、例えば次を保存します。
{
"id": "Q001",
"prompt": "...",
"response_a": "...",
"response_b": "...",
"preferred": "B",
"scores": {
"accuracy": 5,
"usefulness": 4,
"clarity": 5
},
"reason": "...",
"expert_answer_c": "...",
"evaluator_id": "...",
"evaluation_seconds": 320
}
こうしておけば、
後からFine-tuning方式を変更できます。
SFT用Datasetへ変換
SFTの場合は、
prompt
↓
expert_answer_c
などに変換できます。
Hugging Face TRL公式ドキュメントでは、
“SFT supports both language modeling and prompt-completion datasets.”
とされています。
DPO用Datasetへ変換
A/B評価はDPOなどのPreference Learningにも利用できます。
TRLではPreference Datasetについて、
“A preference dataset is used for tasks where the model is trained to choose between two or more possible completions”
と説明しています。
代表的には、
prompt
chosen
rejected
形式です。
つまり今回のA/B/C Datasetは、
SFT
DPO
その他のPreference Optimization
へ発展させられます。
STEP 6:Fine-tuning①
ここで初めてFine-tuningを行います。
対象は、
dataset_v1.5
約30件
です。
この時点で学習方式を確定します。
候補は、
SFT
SFT + LoRA
DPO
DPO + LoRA
などです。
ただし最初から複数手法を同時に比較すると変数が増えすぎます。
そのため最初は、
1つのFine-tuning方式
を選択し、
教師データの改善だけによる変化
を確認する予定です。
model_v1を評価
Fine-tuning後のモデルを、
Baseline
vs
model_v1
で比較します。
ただし、この時点では最終Holdout Datasetは使いません。
ここではDevelopment用Datasetで改善傾向を見るだけです。
STEP 7:dataset_v2.0を約100件へ拡張する
30件で問題がなければ、次に100件程度まで拡張します。
ただし単純に、
30 → 100
と増やすわけではありません。
v1で見つかった弱点を追加する
model_v1の評価結果から、
苦手カテゴリ
失敗パターン
Edge Case
評価者不一致が多かったパターン
を抽出します。
それをdataset_v2.0へ意図的に含めます。
つまりDatasetはランダムに増やすのではなく、
モデルの失敗
↓
次のDataset
というFeedback Loopで成長させます。
OpenAIもモデル改善について、評価結果に基づきPromptやFine-tuning Datasetを修正して反復する流れを説明しています。
STEP 8:dataset_v2.5を作る
100件のdataset_v2.0に対して、再び有識者評価を行います。
ただし、すべてにC案を書いてもらう必要はないと考えています。
基本は、
全100件
A/B評価
↓
評価スコア
↓
理由
を取得します。
そして、
A/Bともに不十分
なものだけC案を作ってもらいます。
有識者コストも測る
例えば、
評価者人数
評価時間
報酬
1問あたり時間
1問あたり費用
C案作成率
を記録します。
仮説段階の予算としては、
dataset_v1.5
10〜25万円程度
dataset_v2.5
20〜50万円程度
を想定し、実際のPoCで実測値に置き換えます。
この金額そのものも実験結果です。
STEP 9:Fine-tuning②
dataset_v2.5を使って再度Fine-tuningします。
Base Model
↓
model_v1
30件
↓
model_v2
100件
として比較します。
確認するのは、
30件 → 100件
と増やしたことで、
どの程度改善したかです。
単なるスコアだけではなく、
改善量
人件費
GPU時間
API費用
Dataset作成時間
も比較します。
STEP 10:eval_dataset_v3.0で最終評価する
ここが最も重要です。
dataset_v2.5を最終評価には使用しません。
OpenAIのFine-tuning Best Practicesには、
“split the dataset into training and test portions. The training set is for fine-tuning jobs, and the test set is for evals.”
と明記されています。
つまり、
Training Dataset
と、
Evaluation Dataset
は分離します。
eval_dataset_v3.0は学習に使わない
最終評価専用Datasetを作ります。
eval_dataset_v3.0
50〜100件以上
このDatasetは、
dataset_v1.5
dataset_v2.5
Fine-tuning
Prompt改善
には使用しません。
実は「最後に作らない」
名前はv3.0ですが、問題候補そのものは実験初期に作ってLockしておく方が安全です。
実験開始
↓
Holdout候補を作成
↓
封印
↓
Fine-tuning①
↓
Fine-tuning②
↓
最後に開封
とします。
つまり、
v3.0は作成Versionではなく、使用Stageを示すVersion
と考えます。
これにより、無意識に最終評価問題へ合わせてDatasetやPromptを修正してしまうことを防ぎます。
OpenAIもTest Setを早期に構築することが、学習後のBenchmark比較に役立つと説明しています。
STEP 11:最終比較
最終的に、
eval_dataset_v3.0
に対して複数モデルを評価します。
例えば、
| Model | Accuracy系 | Human Preference | Rubric Score | Cost |
|---|---|---|---|---|
| Baseline | - | - | - | - |
| model_v1 | - | - | - | - |
| model_v2 | - | - | - | - |
さらに、
Fable
GPT-5.6 Sol
Gemini
Swallow
などBaselineで使用したモデルも参考値として残せます。
見たい結果
例えば、
Baseline 3.1
model_v1 3.6
model_v2 4.2
となるのか。
あるいは、
Baseline 3.1
model_v1 3.7
model_v2 3.7
となるのか。
後者なら、
30件から100件に増やしても効果がなかった
という結果になります。
それも重要な実験結果です。
うまくいかなかったら追加検証する
今回の検証では、
想定した改善が確認できなくても終了しません。
例えば、
model_v2
≒
Baseline
だった場合、
原因候補として、
Dataset品質
Dataset数
Dataset分布
評価基準
Fine-tuning方式
Hyperparameter
Base Model
Prompt
を分析します。
そして最も可能性の高い仮説から追加実験します。
失敗
↓
原因仮説
↓
追加実験
↓
再評価
を繰り返します。
「Fine-tuningが失敗した」で終わらず、
なぜ改善しなかったのか
まで追うことを、この検証シリーズの重要な方針とします。
MLflowで何を記録するか
MLflowでは最低限、以下を記録します。
Experiment
├─ Dataset Version
├─ Model
├─ Model Version
├─ Prompt Version
├─ Fine-tuning Method
├─ Hyperparameters
├─ Metrics
├─ Evaluator Agreement
├─ Evaluation Cost
└─ Artifacts
MLflowはDatasetのlineageやversion管理、Experimentとの関連付けにも対応しています。
また、現在のMLflow GenAI Evaluationでは、人間やDomain ExpertからのFeedbackを記録する仕組みも提供されています。
今回の実験では、これを人間評価結果の管理にも利用できるか検証します。
今回の実験全体での意義
1. 「Fine-tuning」ではなく「教師データ製造工程」を実験する
よくある検証は、
Dataset
↓
Fine-tuning
↓
Accuracy
です。
今回は、
問題設計
↓
LLM生成
↓
評価者選定
↓
評価基準検証
↓
人間レビュー
↓
Dataset
↓
Fine-tuning
↓
未知データ評価
まで扱います。
つまり、
教師データがどう作られたか
をブラックボックスにしません。
2. 人間をGround Truthとして盲信しない
今回特に検証したいのがここです。
LLMは間違える
のは当然ですが、
人間の評価も一致しない
可能性があります。
そのため、
Inter-rater Agreement
まで測定します。
さらに、
評価基準を変えると
人間同士の一致度がどう変わるか
まで検証します。
3. 「品質 × コスト」でFine-tuningを評価する
最終的に知りたいのは、
「Fine-tuningすると何点上がったか」
だけではありません。
例えば、
30件Dataset
費用 15万円
Score +0.5
100件Dataset
費用 40万円
Score +0.55
だったら、
100件作る価値は低いかもしれません。
逆に、
100件
Score +1.5
なら投資する価値があります。
最終的には、
Dataset品質
×
人間工数
×
金額
×
モデル改善量
の関係を明らかにしたいと考えています。
今回の実験で知りたいこと
当初の5つの問いを答えるために必要な質問は次です。
- LLMだけで作ったDatasetでもFine-tuning効果はあるのか?
- 有識者レビューを入れるとどの程度改善するのか?
- 有識者同士の評価はどの程度一致するのか?
- 良い評価者を事前テストで選別できるのか?
- 30件と100件ではどの程度差が出るのか?
- A/B PreferenceはFine-tuningに利用できるのか?
- 有識者C案はどの程度必要になるのか?
- Datasetを100件作ると実際いくらかかるのか?
- Dataset品質とFine-tuning性能にはどの程度関係があるのか?
- Fine-tuning後のモデルは、完全未知データでも改善するのか?
まとめ
今回の実験計画を作る過程で、
Fine-tuningで最も重要な問いは、
「どう学習させるか?」
より前に、
「何を正解として学習させるか?」
なのではないかと考えるようになりました。
さらに、その「正解」を人間が決めるのであれば、
その人間の判断をどのように信用するのか?
という問題も避けられません。
そこで今回のシリーズでは、
LLM
↓
Dataset
↓
Human Evaluation
↓
Evaluation Quality
↓
Fine-tuning
↓
Holdout Evaluation
↓
Cost / Quality Analysis
までを一つの実験として扱います。
次回はまず、
baseline_dataset_v0.5を実際に設計・作成し、複数LLMの現状性能を測定する
ところから始めます。