Upgrade to Pro — share decks privately, control downloads, hide ads and more …

関東Kaggler会_NVIDIA_Nemotron_コンペ_振り返り

Avatar for rick rick
August 07, 2026

 関東Kaggler会_NVIDIA_Nemotron_コンペ_振り返り

Avatar for rick

rick

August 07, 2026

Other Decks in Programming

Transcript

  1. コンペ概要:目的 目的 NVIDIA 「Nemotron-3-Nano-30B」 の推論能力の向上 【例】 • 12 ◯ 5

    = 17 • 8 ◯ 3 = 11 • 7 ◯ 9 = 16 ◯は足し算を 表していそう… お題 推論(隠されたルール) 暗号パズルの解読 複数の「入力 → 出力」の例から背後 にある変換ルールを推論し、最後の問 題に適用して答えを導く暗号パズル形 式の知能評価タスク 【問題】 20 ◯ 7 = ??? お題イメージ 20+7=27だ! 5
  2. コンペ概要:評価指標と制約 評価指標 正答率(Accuracy) • 文字列出力:完全一致 • 数値出力:誤差 1e-2 以内 コンペの制約

    01 LoRAアダプタのみ提出可能 02 外部プログラムの実行不可 提出できるのは LoRAアダプタの重みのみ。 ベースモデルの変更や前処理/後処理、 複数のアンサンブル構築などは一切不可。 推論制限 • temperature = 0 • 1問につき入出力で最大 8192 トークン まで生成可能 これを超える長さの推論トレースは途中で 強制終了となり不正解となる。いかにCoT を短縮するかがポイント。 推論時にPython等のプログラムは実行 できない。そのため、推論トレース(CoT) 自体をテキストとして出力して、解を導く 必要がある。 6
  3. コンペ概要:7つのタスクカテゴリ カテゴリ 特徴 難易度 bit_manipulation (ビット演算ルールの推論) 入力ビット列から出力ビット列への変換規則(AND, OR, NOT・・・)を推論する。 難

    cipher (暗号文の復号) 与えられた暗号文と平文のペアから、適用されている換字式暗号や転置式暗号などの ルールを特定する。 易 cryptarithm (覆面算の推論) アルファベットや記号などの文字に置換された数字・演算子の対応関係を特定し、数式 を成立させる。 難 equation_numeric (記号が表す演算の推論) 未知の記号に割当てられた算術演算とその一貫した規則性を解明する。 難 gravity (重力加速度の推定) 落下時間と落下距離の例から重力加速度 𝑔 を推定し、別の落下条件に適用する。 易 numeral (ローマ数字ルールの推論) 整数とローマ数字の対応例から表記ルールを推論し、未知の整数をローマ数字に変換す る。 易 unit_conversion (変換係数の推定) 入力値と出力値の対応例から変換係数を推定し、未知の値を変換する。 易 7
  4. 公開ベースラインの紹介 中盤時点で1位だったコードが公開され、参加者全員のデファクトスタンダードとなった。 01 決定論的なソルバーの構築 02 推論過程(CoTトレース)の出力 03 トレースを用いたSFT学習 • 各カテゴリの法則をプログラムで解く

    • ソルバーの思考プロセスをテキスト化 • LLMにCoTトレースを学習させる • 「入力データ→変換ルール」を逆算・ 復元するPythonロジックを実装 • モデルが真似しやすいよう、曖昧さを排 除した明示的な思考手順を記述 • 入力プロンプト部分をマスクし、CoT思 考プロセスのみを最適化ターゲットに • ルール抽出の成功率を高め、教師 データの質を担保する • 最終出力を一貫したフォーマットで厳 密にカプセル化 • 決定論的なソルバーの思考プロセスを モデルに模倣させる LLM自体を、決定論的ソルバーと同じステップで考え、動く仮想マシンとして振る舞わせる 8
  5. 上位解法 / 私たちの取り組み bit_manipulation のアプローチ cryptarithm(deduce) のアプローチ 97.5% 1位 •

    • HEX(16進数)圧縮によるトークン節約 ルールカタログによる修復アルゴリズム 42.9% • 署名カタログをモデルに暗記させ、深さ優先 探索 99.1% 2位 • • HEX(16進数)圧縮によるトークン節約 系列分解 • • HEX(16進数)圧縮によるトークン節約 ルール適用順序の動的変更 27.8% • • • • 10位 (私達) • • ベース ライン 5つのテンプレートに集約・整理 ビット配列の両端アンカー絞り込み cryptarithmが解けていた1位 はぶっちぎりのスコアだった。 • bit_manipulationが解けてい るチームは金圏に入っている傾向 が見られた。 • 各カテゴリの解き方はチームによっ て大きく異なっていた。 乗算パターンの暗記 効率的なバックトラックの実行 99.4% 4位 • 改善なし 89.4% 3位 上位解法のトレンド 改善なし 私たちの取り組み • bit_manipulationと equation_numericに対して、 高水準な回答性能を持つトレー スを作成した。 • 苦手データの再学習やハイパラ 調整を通じて、トレースの考え方 を効果的に学習することができた。 98.8% キーテーブル方式による仮設空間の再定義 ビット配列の両端アンカー絞り込み 85.1% 改善なし 8.2% 12
  6. 主要なソルバー改善: bit_manipulation ナイーブな総当たり検索 𝑝, 𝑞 = 0,1 , 0,2 ,

    ⋯ , 6,7 𝑥 0 𝐴𝑁𝐷 𝑥 1 → 𝑚𝑖𝑠𝑚𝑎𝑡𝑐ℎ 𝑥 0 𝐴𝑁𝐷 𝑥 2 → 𝑚𝑖𝑠𝑚𝑎𝑡𝑐ℎ 𝑥 0 𝐴𝑁𝐷 𝑥 3 → 𝑚𝑖𝑠𝑚𝑎𝑡𝑐ℎ ⋯ 全ペアを試行 各出力ビットごとに、標準的な論理演算 (AND, OR, NOT, XOR)の組み合わせを 総当たりで試行していた。 トレースが非常に長くなり、3入力なども試せ ず精度は85%で頭打ち。中間過程を圧縮 すると論理の飛躍が生じ、モデルが学習でき ない。 3入力×キーテーブル方式 𝑜𝑢𝑡 𝑖 = 𝐹 𝑟𝑜𝑡𝑙 𝑥, 𝑎 𝑖 , 𝑠ℎ𝑙 𝑥, 𝑏 𝑖 , 𝑠ℎ𝑟 𝑥, 𝑐 𝑖 𝐹: 3入力ブール関数 𝑎, 𝑏, 𝑐: シフト/ローテートのオフセット Point 1: 境界ビット制約 シフトで溢れたビットが0になるという境界条件を活用。これにより、 オフセット候補の探索空間を数千通りから数十通りに削減。 Point 2: キーテーブルの作成 例から「3ビット入力状態→出力ビット」の対応表を生成する。論 理的な矛盾が1つでも発生した候補関数は即座に枝刈りする。 「1ビットずつ順番に推論する」無駄を排除し、「ビット配列全体に共通 するシフト則と真理値表(キーテーブル)」へと定式化し直すことで、 記述効率を劇的に改善 精度向上 85.1% → 98.8% 13
  7. 前提:学習データ =「解き方の実況」 このコンペの SFT データは、ルールベースの reasoner が生成する“決定論的トレース” ① reasoner が問題を解く

    ② SFT でトレースを模倣 ③ 推論は temperature=0 解く過程を全行出力 = 決定論的トレース モデルは解き方の 次トークンを学ぶ (暗記) トレースを“なぞれれば” 正答できる つまり 「何を学ばせるか」= トレースの書き方そのものが精度を決める。 担当:データ作り(中間1位の方の公開パイプラインを流用、変更したのはトレースだけ) 16
  8. 終了2日前:ポテンシャルはあるのに、モデルが学習できない Rick さんが bit_manipulation の効率の良いトレース案を発見 ― これが金メダルの種になった 解ける問題(被覆) 98.8% 旧エンジン

    85% から大きく向上 SFT 後の正答 なのに 103/169 旧トレースの 153/169 から悪化 正解トレースは作れるのに、モデルが学習できない。 「何がいけないのか?」を分析してトレースを改善した ― 今日の話。 17
  9. 定義:学習できるトレース = モデルが“なぞれる” 各行を「直前までに書いた情報」だけから 決定論的に再現できること 同じ問題・同じ結論でも、導出を書くか飛ばすか で学習可能性が変わる: ✗ 論理の飛躍あり ―

    学習できない ✓ 飛躍なし ― 学習できる 問題:x×y=12, x+y=7 問題:x×y=12, x+y=7 導出:―(書かれていない) 導出:x+y を確認 → (1,12)=13 (2,6)=8 (3,4)=7 ✓ 結論:x=3, y=4 結論:x=3, y=4 どう探したかが無い = 再現できない 各手が見える = そのままなぞれる 条件: ① 飛躍(隠れ推論)を書かない ② 問題ごとに違う列挙・値を残さない ③ 分岐は直前の表から導ける 被覆(解けること)だけでは不十分 ― “なぞれる”形かどうかが学習可能性を決める。 18
  10. 実例:トレース改善の詳細 元(抜粋) 改善後(抜粋) Before: conflict の“結論だけ”を1行で書く(いきなり bit2 から) List: a3b4c1

    a3b4c2 a3b4c3 a3b4c4 a3b4c5 …(98個を列挙) Try a3b4c1 -> bit2 (5,6,1): 111>0 010>0!=1 conflict Try a3b4c2 -> bit1 (4,5,-): 010>0!=1 conflict Try a3b4c3 -> bit1 (4,5,-): 010>0!=1 conflict After: どの Try も bit0 から探索し、conflict で打ち切る Try 1: a3b4c1 bit0 (3,4,-): 000>0 100>0 010>1 000. 110>0 100. 010. bit1 (4,5,0): 011>0 000. 101>0 000. 110. 011. 100. bit2 (5,6,1): 111>0 010>0!=1 conflict Try 2: a3b4c2 bit0 (3,4,-): 000>0 100>0 010>1 000. 110>0 100. 010. …(以降も同じ構造で続く) 「結論だけ」→「bit0 から探索・conflict で打ち切り」に統一。 どの Try も構造的に同一になり、モデルが再現できない情報が消える。 19
  11. 方法:ズレを見つけて直すループ ① 生成して照合 ② ズレの行を特定 出力を正解と行単位で比較 一致が切れる“最初の行”を見つける ↻ ④ 再学習して再測定

    ③ なぞれる形に書換 ズレが消えたかを確認 その行だけを直す ズレが消えるまで回す ― bit_manipulation では、このループで 103 → 152 まで回復。 20
  12. 分析:生成出力は正解と「どこから」ズレるか 不正解の出力を 正解トレースと行単位で照合すると―― ここからズレ始める 生成出力 前半(転記・計算)は一致 → “候補列挙”の行から崩れて暴走する 正解 :

    …前半一致… 候補 a1 a2 a3 a4 a5 a6 → 検証 → 答え 生成 : …前半一致… 候補 a1 a2 a3 a1 a2 a3 a1 a2 … 繰り返して答えに未到達 長さの問題ではない。最初にズレる行 = モデルが再現できない行(候補列挙)。 対応:列挙を消す・Try に番号を付ける・紛らわしいラベルを言い換える ― その行だけ書き換えた。 21
  13. 結果:103 → 152 → 162 bit_manipulation held-out 169問での正答数 発見した案を そのまま学習

    トレース改善(私) 学習の工夫(eikichi さん) 103 152 162 被覆は 98.8% と高いのに +49 ― なぞれる形に書換 最後の一押し → 次のパート 私の改善は「可能性を引き上げた」まで。仕上げの 152 → 162 は、この後の eikichi さんパート で。 22
  14. まとめ:エージェントで改善する「外枠」を作る 「どうすれば改善されるか」を 言語化 → ルール化 し、 その範囲内で繰り返し改善させる “外枠” を作る のが有効

    外枠(改善のフレーム) ① 言語化 ② ルール化 ③ 範囲内で反復 ↻ 何が「良い」かを言葉に 例:なぞれるトレース 条件・手順に落とす 例:条件3つ+照合ループ ルールの中で回し続ける 例:103 → 152 人がやるのは“外枠の設計”。枠の中の反復は エージェント(モデル)に任せられる。 その外枠の設計自体も、失敗の教訓をルール化するたびに、任せられる範囲が広がっていくはず。 23
  15. パイプライン 01 トレース Corpus作成 02 Stage 1 SFT 03 min

    logprobs を出力 Prompt と正解トレースを固定 通常データでLoRA adapterを作成 このadapter自身の苦手トレースを見る トレース毎のtokenのlogprobsを確認 min logprobをトレースの評価値とする 04 苦手問題の抽出 05 Replay Corpusの作成 06 Stage 2 SFT 各トレースにおいて評価値から閾値以下 (今回は-0.69)を抽出 苦手問題として抽出されたサンプルを 2倍複製してCorpusを再作成 苦手問題を2倍複製したCorpusで 1から再度学習(追加学習ではない) Stage1とStage2のbit_manipulationで 152 → 162 の改善 29
  16. min logprobs を出力 実装の流れ 各問題トレースの min を取得 生成ではなく、トレースの logprobを取得 トレース部分を抽出・評価

    prompt + トレースをfull textとしてvLLMに渡し 各token位置のlogprobsを取得 prompt_logprobsは全token分のリストのため prompt部分を除き、 作成トレース部分のtokenで確認 params = SamplingParams( prompt_logprobs=1, max_tokens=1, temperature=0.0, ) outputs = llm.generate(full_texts, params) output = outputs[0] prompt_logprobs = output.prompt_logprobs トレースのlogprobs例 -0.04 -0.12 -0.08 -3.91 -0.20 token 0 token 1 token 2 token 3 token 4 一番低いtokenのlogprobsをトレースの評価値とする 30
  17. LoRA alphaの調整 あるSnapshotでの実験 lora_alpha alpha_over_rank CV(3run平均) 32 1 0.872 (828.3

    / 950) 64 2 0.874 (830.3 / 950) 128 4 0.877 (832.7 / 950) 256 8 0.876 (831.7 / 950) 最終Subでの実験 lora_alpha alpha_over_rank CV(3run平均) 32 1 未実験 64 2 未実験 128 4 0.905 (859.7 / 950) 256 8 未実験 lora_alphaをsweepし、lora_alpha=128(lora_alpha / r = 4)を採用 33
  18. LoRA alphaの調整 あるSnapshotでの実験 lora_alpha alpha_over_rank CV(3run平均) 32 1 0.872 (828.3

    / 950) 64 2 0.874 (830.3 / 950) 128 4 0.877 (832.7 / 950) 256 8 0.876 (831.7 / 950) じゃあ実際はどうだったのか? 最終Subでの実験 lora_alpha alpha_over_rank CV(3run平均) 32 1 未実験 64 2 未実験 128 4 0.905 (859.7 / 950) 256 8 未実験 lora_alphaをsweepし、lora_alpha=128(lora_alpha / r = 4)を採用 34
  19. LoRA alphaの調整(追加検証) あるSnapshotでの実験 lora_alpha alpha_over_rank CV(3run平均) 32 1 0.872 (828.3

    / 950) 64 2 0.874 (830.3 / 950) 128 4 0.877 (832.7 / 950) 256 8 0.876 (831.7 / 950) 最終Subでの実験 lora_alpha alpha_over_rank CV(3run平均) 32 1 0.900 (855.3 / 950) 64 2 0.896 (851.3 / 950) 128 4 0.905 (859.7 / 950) 256 8 0.904 (858.7 / 950) lora_alpha=128がCVベストは変わらず 35