はじめに
松尾研LLM講座2025の最終課題アドバンスドコンペに参加しました。約3週間の期間で100以上のモデルを作成し、50以上のフェーズに分けて系統的に実験を回しました。
この記事では、大量の実験を通じて得られた知見を共有します。「何が効いたか」よりも「何が効かなかったか」の方が、次回参加者にとって価値があると思うので、失敗談を中心に書きます。
コンペの概要
アドバンスドコンペはAgentBenchというベンチマークを使います。学習指定モデル(Qwen2.5-7B-InstructまたはQwen3-4B-Instruct-2507)をSFT等でファインチューニングし、2つのタスクで評価されます。
- ALFWorld: テキストベースの家事ロボットエージェント。「石鹸をゴミ箱に置け」のような指示を受けて、部屋を探索して目的を達成する(50問)
- DBBench: SQLを使ってデータベースを操作し、質問に答える(150問)
この2つのスコアの合計で順位が決まります。
実験の進め方
フェーズ管理
実験はすべて「フェーズ」として管理しました。Phase A, B, C, ... と進め、途中から分岐が増えてK-seriesやM-seriesのようにサブフェーズも生まれました。最終的にA〜Zを超え、AA〜AEまで到達。
各フェーズで変えるのは1つの変数だけ。データを変えるならハイパーパラメータは固定、LoRA rankを変えるなら他は固定。これを徹底したおかげで「何が原因で良くなった/悪くなったか」を切り分けられました。
評価環境の信頼性検証
コンペでは複数の評価環境が使えましたが、環境ごとにスコアが微妙にずれます。早い段階で以下を確認しました:
- GCP L4でのDBBench評価 → 公式スコアと完全一致(最も信頼できる)
- Colab A100でのDBBench評価 → +1.7%の過大評価(信用してはいけない)
- Omnicampus ALFWorld事前評価 → ±2%程度のブレ(概ね信頼できる)
これを知らずにColab A100の数字を信じていたら、提出判断を間違えていたと思います。
効いたこと
1. フォーマット不一致の修正
最初の大きなブレークスルーは地味でした。学習データでは Think:/Act: というフォーマットを使っていたのですが、評価環境は THOUGHT:/ACTION: (大文字)を期待していました。
この修正だけでALFWorldの無効アクション率が14%→10%に減少。学習データと評価環境のフォーマットが一致しているかは真っ先に確認すべきです。
2. ハイパーパラメータ探索(Phase K)
20以上のデータ改善フェーズがすべて失敗した後、ようやくハイパーパラメータに目を向けました。それまでずっと r=8, epochs=0.3 で固定していたのですが、グリッドサーチをした結果:
| パラメータ | 変更前 | 変更後 |
|---|---|---|
| LoRA rank | r=8 | r=32 |
| エポック数 | 0.3 | 1.0 |
| 学習率 | 1e-5 | 5e-6 |
これだけでDBBenchが大幅に改善しました。データの前にハイパーパラメータを試せというのが最大の教訓です。1回の学習は十数分で終わるのに、データ作成には何時間もかかります。
3. スコア公式の逆算
公式のスコア計算方法は非公開でしたが、複数の提出結果からスコア公式を逆算しました:
score ≈ 0.03845 × ALFWorld% + 0.03856 × DBBench%
ALFWorldとDBBenchは1%あたりほぼ等価の貢献(約0.0385)。この発見により、「ALFWorldが2%上がるけどDBBenchが3%下がる」変更が得か損か、提出前に判断できるようになりました。
4. Instruction Masking
終盤で発見した技法です。通常のSFTでは会話全体(システムプロンプト+ユーザー入力+アシスタント応答)に対してlossを計算しますが、アシスタント応答部分だけにlossを限定する方法です。
これによりALFWorldのスコアが大きく向上しました。エージェントが「考えてから行動する」パターンをより正確に学習できたのだと考えています。
5. Seed選択
すべてのハイパーパラメータとデータを固定しても、乱数シードの違いだけでDBBenchが±2%、ALFWorldが±6%動きます。最適なseedを見つけるために18バリエーションを学習しました(いわゆる「Seedガチャ」)。
他にやることがなくなったときの最終手段ですが、無視できない効果がありました。
効かなかったこと(失敗の記録)
ここからが本題です。100+モデルの大半は「効かなかった」実験です。
1. eval_lossを信じるな
最も重要な教訓です。 LoRA rank=48のモデルは、eval_lossが全モデル中最低(最良)でした。普通なら「最もよく学習できている」と判断するところです。
しかしALFWorldの成功率は全モデル中最悪でした。
| モデル | eval_loss | ALFWorld |
|---|---|---|
| r=32 (ベスト) | 0.583 | 64% |
| r=48 (eval_loss最低) | 0.474 | 56% |
eval_lossの改善はタスク性能を予測しません。これはbf16実験でも再確認され、eval_lossが最低のモデルが一貫して最悪の結果を出しました。
2. LoRA rankは「大きいほど良い」わけじゃない
系統的にテストした結果:
| LoRA rank | 結果 |
|---|---|
| r=8 | 学習不足(epochs=0.3では不十分) |
| r=12 | 有害 |
| r=16 | 一貫して有害(2回テストして2回とも悪化) |
| r=32 | 唯一有効 |
| r=48 | 過学習(eval_lossは改善するがタスク性能は悪化) |
r=32だけが有効という狭い最適値でした。「大きいrankで表現力を上げれば良くなるはず」という直感は裏切られます。
3. モデルマージは銀の弾丸じゃない
ALFWorldが得意なモデルとDBBenchが得意なモデルをマージすれば両方得意になるのでは? と考えて3種類試しました:
| 手法 | 結果 |
|---|---|
| SLERP | ALFWorld低下 |
| DARE-TIES (1回目) | DBBench壊滅(25%) |
| DARE-TIES (2回目) | ALFWorld壊滅(50%)、DBBenchは過去最高だがトータルで不足 |
マージは「良いとこ取り」ではなく「トレードオフの再分配」でした。少なくともこのタスクでは、個別に最適化されたモデルの方がマージモデルより強いです。
4. データ増強が裏目に出る
ALFWorldの学習データを350例→696例に倍増させました。直感的には「データが多いほうが良い」はずです。
結果:ALFWorld 64%→58%に悪化。
3,500サンプル規模のファインチューニングでは、データの「量」より「質」と「バランス」が重要でした。既存の最適なバランスを崩すと、増やしても逆効果になります。
5. DPOは構造化タスクに向かない(メインコンペの知見)
メインコンペ(Struct-Eval)では、SFTで問題率10.7%まで下げた後にDPOを適用しました。
結果:問題率が73.3%に爆増。
DPOは「人間が好む出力」を学習しますが、構造化出力タスクでは「人間が好む」=「丁寧な説明付き」が「正しい出力」=「生のJSON/YAMLのみ」と矛盾します。SFTで「これを出せ」と教える方が、DPOで「こっちの方が良い」と教えるよりも確実です。
6. エポック数の感度は想像以上
epochs=1.0前後の微調整結果:
| epochs | ALFWorld | DBBench |
|---|---|---|
| 0.8 | 大幅低下 | 低下 |
| 0.9 | 大幅低下 | やや低下 |
| 1.0 | 最適 | 最適 |
| 1.1 | 維持 | 低下 |
| 1.2 | 維持 | 低下 |
epochs=1.0が「ALFWorldのフォーマットを十分学習する最小値」かつ「DBBenchが過学習しない最大値」のピンポイントでした。0.1刻みの違いが致命的です。
7. 弱いカテゴリは動かない
DBBenchのSELECT AGG-MAX(16.67%)やCOUNT(27.27%)は、どんな実験をしても精度が変わりませんでした。データ増強、蒸留、リサンプリング、重み付け — すべて効果なし。
これはタスク自体の難易度であり、学習データの不足ではなかったということです。「改善できないものに時間を使わない」判断も重要です。
8. bf16がQLoRA 4-bitに負ける
精度の高いbf16(16ビット)でLoRA学習すれば、4ビット量子化(QLoRA)より良い結果が出るはず… と思いきや、ALFWorldが44%まで崩壊しました(QLoRAでは66%)。
4ビット量子化による暗黙的な正則化が、過学習防止に寄与していたようです。
開発効率化の工夫
ドキュメント駆動開発
すべての実験はMarkdownドキュメントで管理しました:
- AGENTS.md: 現在の状態・制約・次のアクションを常に更新
- ERROR.md: エラー台帳。同じ失敗を繰り返さないため
- HANDOVER.md: 引き継ぎガイド。コンテキストが失われても復帰できる
100+モデルを管理するには、このレベルの記録が必須でした。「あの実験のハイパーパラメータ何だったっけ?」が即座に調べられる状態を維持することで、判断の速度と精度が上がります。
変数分離の徹底
「データを変えたらハイパーパラメータも変えたくなる」衝動を抑えて、1フェーズ1変数を厳守しました。これにより:
- 良い結果が出たとき → 何が効いたか明確
- 悪い結果が出たとき → 何がダメだったか明確
- 後から振り返って法則を見つけやすい
地味ですが、系統的実験の基本です。
来年の参加者へのアドバイス
- ハイパーパラメータを最初に試せ。データ作成に何時間もかけるより、LoRA rank・epochs・学習率のグリッドサーチ(各十数分)を先にやるべき
- 評価環境の信頼性を確認しろ。環境ごとにスコアがずれる。どの環境が公式に近いか早期に把握すること
- eval_lossを信じるな。タスク性能との相関がないケースがある。必ずタスク評価で判断すること
- 「常識的に正しそう」を疑え。r=48 > r=32、データ多い > データ少ない、bf16 > 4bit — 全部裏切られた
- フォーマットの一致を確認しろ。学習データと評価環境のフォーマット不一致は致命的。最初に確認すべき
- 実験記録を残せ。100+モデル作るなら必須。記憶に頼ると同じ失敗を繰り返す
おわりに
100以上のモデルを作って、その大半は「効かなかった」実験でした。しかし、系統的に失敗を記録し分析したことで、最終的にどのパラメータが本当に重要かを見極められました。
ファインチューニングは「正解のレシピ」がないので、いかに効率よく仮説を検証できるかが勝負です。この記事が、次回のコンペ参加者の参考になれば幸いです。
使用モデル: Qwen2.5-7B-Instruct + QLoRA (4-bit) + LoRA
学習フレームワーク: Unsloth + TRL SFTTrainer
関連記事
実験の詳細な数値データ(LoRA rankグリッドサーチの全結果・マージ手法の比較表など)は以下にまとめています。