NVIDIA Japan の NPN Partner Model Customization Bootcamp に参加してきました

NVIDIA Japan の NPN Partner Model Customization Bootcamp に参加してきました

NVIDIA が主催するモデルカスタマイズブートキャンプに参加し、合成データ生成から GRPO 強化学習を経てエージェント実装まで、小型言語モデルを 1 日で鍛え上げるハンズオンを体験してきました。論文では見かけるけど自分で回したことのない GRPO の仕組みから、データ検証の落とし穴、報酬設計の難しさまで、実装を通じて学んだ知見をまとめます。
2026.08.06

はじめに

こんにちは、クラスメソッド製造ビジネステクノロジー部の森茂です。

2026 年 8 月 5 日、NVIDIA Japan とマクニカ主催の「Japan NVIDIA NPN Partner Model Customization Bootcamp」に参加してきました。6 月の Agentic AI Bootcamp に続く NPN(NVIDIA Partner Network)パートナー向けハンズオンの第 2 弾で、今回のテーマはモデルカスタマイズです。合成データ生成と強化学習を 1 日で回して、小型モデルをエージェント用に鍛え上げるという、前回よりさらに一歩踏み込んだ内容でした。

前回の参加レポートはこちらです(2026 年 6 月時点の記事です)。この記事の最後で「続編は強化学習がテーマになるとアナウンスされていた」と書いたのですが、その続編が今回にあたります。

https://dev.classmethod.jp/articles/nvidia-npn-agentic-ai-bootcamp-report/

今回のスライドや教材リポジトリは非公開なのですが、ハンズオンのベースになった参照実装は NVIDIA GenerativeAIExamples として GitHub で公開されています。この記事と合わせて眺めると、当日の流れをかなり再現できるはずです。

この記事では、当日のアジェンダと各セッションで触れた要点、そして自分の手元で観測できた実測値やデータの落とし穴を参加レポートとしてまとめます。GRPO を「論文で名前は見るけど自分で回したことはない」という方に刺さるといいなと思っています。

ひとことでまとめると

自然文の指示を受け取って LangGraph CLI のコマンドを JSON で吐き出すエージェントを、合成データ生成 → 強化学習 → 実行という 3 ステップで 1 日かけて作る構成でした。データは NeMo Data Designer で 500 件を機械生成し、報酬は NeMo Gym の検証サーバーが機械採点し、学習は TRL の GRPOTrainer が回す。つまり人手のアノテーションも人手の評価もない、検証可能報酬(RLVR)による強化学習パイプラインを丸ごと体験する内容です。

前回の Agentic AI Bootcamp が「エージェントを組む」回だったとすると、今回は「エージェントの中身のモデルを鍛える」回です。題材の選び方も攻めていて、ファインチューニング入門の定番で未経験でもキャッチアップしやすい SFT ではなく、経験者の少ない GRPO を正面から据えています。ベースモデルの Qwen3-0.6B-Base は SFT を一切していない素の事前学習モデルで、講師の言葉を借りると「DeepSeek-R1-Zero と同じ状態」から報酬だけで JSON を吐けるようにしていきます。

イベント概要

Day 0(オンライン Dry Run)と Day 1(インパーソンの本編)の 2 部構成です。

  • 開催日: 2026 年 8 月 5 日 9:30〜19:00(JST)、Day 0 は 8 月 3 日にオンライン開催
  • 会場: マクニカ品川オフィス
  • 参加対象: NVIDIA NPN パートナー社員
  • 主催: NVIDIA Japan + マクニカ + OpenACC organization

ハンズオンのベースになった参照実装はこちらです。

https://github.com/NVIDIA/GenerativeAIExamples/tree/main/nemotron/LLM/bash_computer_use_agent

実行環境は事前配布された共有 GPU クラスタで、SSH で入ってシェルスクリプトを叩くと JupyterLab のコンテナが起動し、ポートフォワーディングでブラウザから接続する流れでした。GPU は A100 です。小ネタとして、会場のネットワークは SSH の 22 番ポートが塞がれていて 443 番を明示する必要があり、接続で詰まった人の原因はほぼこの指定漏れだったようです。TA の方が複数名常駐していて、この手の詰まりはすぐに解消してもらえる体制でした。

当日のアジェンダ

本編のアジェンダはおよそ以下のとおりでした。

時間 セッション
10:00 - 10:30 Introduction and Cluster setup
10:30 - 11:00 Tutorial 1: Overview & Synthetic Data Generation
11:15 - 12:15 Lab 1: Synthetic Data Generation
13:15 - 14:15 Tutorial 2: Introduction to GRPO
14:30 - 15:30 Lab 2: GRPO
15:45 - 16:45 Lab 3: Run Bash Agent
16:45 - 17:00 Wrap up

3 つの Lab をつなげると、以下のパイプラインになります。

ここからは、各セッションで何に触れたかと、自分が観測したポイントを順に振り返ります。

Tutorial 1 で SFT と RL の違いを頭に入れる

最初の座学は LLM の基礎の復習です。アーキテクチャは 2017 年の Transformer からほぼ変わっておらず、Attention と MLP のブロックが多段に重なっているだけ、パラメータの大半を占める MLP 部分が MoE(Mixture of Experts)に置き換わったのが今の主流、という整理から始まりました。リーズニングモデルについても「<think> タグを吐くように学習されているだけで、構造は同じ。出力トークンが長くなっただけ」とバッサリで、このあたりの割り切った説明は聞いていて気持ちよかったですね。

本題は学習の 3 段階、つまり事前学習、SFT、RL の対比です。講師の整理では、SFT は「データセットにある正解の尤度を上げる。学習データは固定」、RL は「モデル自身が生成した出力に報酬を与える。学習データがモデル自身からサンプリングされる」となります。この「学習データがどこから来るか」の違いが、後の Lab で効いてくる伏線になっていました。

文脈の説明も面白くて、OpenAI o1 の登場でクローズドモデルとオープンモデルの差が一気に開いたところに DeepSeek-R1 が現れ、RL で reasoning を獲得できることをハイパーパラメータ込みで公開したためオープンモデルが追いついた、今日やる GRPO はまさにその DeepSeek-R1 のアルゴリズムだ、という導入でした。会場への挙手アンケートでは SFT 経験者は多数、RL 経験者はちらほらという分布で、このブートキャンプの狙いどころがよく分かる結果になっていました。

Lab 1 で NeMo Data Designer の合成データ生成を回す

Lab 1 は学習データを作るパートです。NeMo Data Designer という合成データ生成ライブラリを使い、LangGraph CLI への日本語の指示文と、その正解となる JSON ツールコールのペアを 500 件生成します。

https://github.com/NVIDIA-NeMo/DataDesigner

生成モデルは Nemotron-3-Nano 系の 30B reasoning モデルを vLLM でローカルにサーブし、OpenAI 互換エンドポイントとして Data Designer に登録します。reasoning モデルをデータ生成に使うと <think> の思考トレースが生成テキストに漏れ込むため、enable_thinking: False で思考を切っておくのが最初のポイントでした。

今回の設計としてポイントは、正解ラベルを LLM に作らせないところです。コマンド名やポート番号はまずサンプラーで機械的に振ります。

# 引用元: ブートキャンプ教材 01_synthetic_data_generation_ja.ipynb(抜粋)
config_builder.add_column(SamplerColumnConfig(name="command", sampler_type=SamplerType.CATEGORY,
    params=CategorySamplerParams(values=["new", "dev", "up", "build", "dockerfile"])))
config_builder.add_column(SamplerColumnConfig(name="port", sampler_type=SamplerType.UNIFORM,
    params=UniformSamplerParams(low=3000, high=9000), convert_to="int"))

LLM が生成するのはこの値を埋め込んだ自然文の指示だけで、正解 JSON はサンプラーの値から決定論的に組み立てます。ラベルを LLM に書かせると間違えることがありますが、この構成ならラベルは常に正しい。生成後の検証も、指示文にサンプラーの値がそのまま含まれているかのチェックと必須引数チェックだけで済みます。自分の実行では 500 件生成して検証を通過したのが 459 件、歩留まりは約 92% でした。生成されたレコードはこんな形です。

{
  "input": "8807番ポートで変更を監視しながらサーバーコンテナを起動してください。",
  "output": {
    "command": "up",
    "template": null,
    "path": null,
    "port": 8807,
    "no_browser": null,
    "watch": true,
    "tag": null,
    "output_path": null
  }
}

一方で、生成されたデータを後から眺めていて気づいた落とし穴もあります。データの中にこんな行が混ざっていました。

{
  "input": "dev 서버をポート 3989 で起動してください。",
  "output": {
    "command": "dev",
    "template": null,
    "path": null,
    "port": 3989,
    "no_browser": null,
    "watch": null,
    "tag": null,
    "output_path": null
  }
}

日本語のはずの指示文に韓国語が混入しています。数えてみると train データ 413 件のうち約 16% にハングルが含まれていて、しかもほぼすべてが dev コマンドのレコードでした。「dev サーバー」という英単語まじりの文脈で、生成モデルが韓国語に転んでいるように見えます。検証フィルタは値の包含と引数しか見ておらず、言語チェックがないため素通りしていました。生成プロンプトが英語で書かれていることも一因のようです。SDG は生成して件数が揃ったら終わりではなく、中身を通読するフェーズが要る、というのが Lab 1 の一番の学びでした。

Tutorial 2 で GRPO の式を REINFORCE から導出する

午後の座学が本ブートキャンプの山場です。GRPO の目的関数を、REINFORCE から PPO を経由して段階的に導出していきます。講師いわく「マスヘビーで申し訳ないんですけど」とのことでしたが、この 1 時間があったおかげで後の Lab のハイパーパラメータが全部意味を持って読めるようになるので、個人的には今日一番価値のあるセッションだったかなと思っています。

流れを大づかみにすると、こうなります。1992 年頃からある REINFORCE は「報酬が正なら自分が通った生成経路を強化し、負なら弱める」という素朴な方策勾配で、報酬の分散が大きく学習が不安定です。そこで報酬からベースラインを引いて分散を下げる工夫が入り、ベースラインに状態価値関数を使ったのが PPO です。ただし PPO は価値を予測するモデルがもう 1 つ必要になります。GRPO はここで「value model を丸ごと捨てる」方針を取りました。同じプロンプトに対して 8 個とか 16 個の応答をまとめて生成し、グループ内の報酬の平均と標準偏差で正規化した Z スコアを advantage として使います。グループの統計量で済ませるので追加のモデルが要らない、というわけです。

講師のまとめが秀逸で、advantage を大きくしたい、ただし KL 正則化で元のモデルから離れすぎないようにしたい、それが「GRPO のお気持ち」だと集約されていました。PPO と GRPO の式を並べると、違うのは advantage の定義だけというのも、導出を追った後だとすっと入ってきます。

報酬の与え方についても、RLHF と RLVR の対比で整理されました。RLHF は人間の好みを学習した報酬モデルを使うため、報酬モデルが学習していない領域には報酬を与えられません。RLVR は正解をコードで機械判定するので、同じ出力には常に同じ報酬が返り、学習が安定します。実務では「RLVR で数学やコーディングのような検証可能なタスクを教え、最後の仕上げに RLHF で人間の好みに寄せる」という併用が現在地とのことでした。質疑応答では「学習自体はもう簡単に回せる。一方で報酬の構築は非常に難しくて、すぐに報酬ハッキングが起きる」という話や、コード実行での検証は GPU ではなく CPU 側で走るため RL では CPU の重要性が高まっているという運用面の話も出て、このあたりは論文だけ読んでいても出てこない肌感でしたね。

Lab 2 で Qwen3-0.6B-Base に GRPO をかける

いよいよ強化学習です。学習対象は SFT を一切していない Qwen3-0.6B-Base で、ここから報酬だけで JSON ツールコールを教えます。報酬は NeMo Gym のリソースサーバーとして実装された FastAPI の検証エンドポイントが返します。採点基準は以下のとおりです。

条件 報酬
JSON がパースできない -1.0
コマンド不一致 -1.0
コマンド一致、フラグは部分一致 0.0〜+1.0
完全一致 +1.0
形式ボーナス(JSON パース可能) +0.3

https://github.com/NVIDIA-NeMo/Gym

学習の設定は TRL の GRPOTrainer にそのまま渡します。座学で出てきた「グループ」が num_generations です。

# 引用元: ブートキャンプ教材 02_grpo_training_ja.ipynb(抜粋)
training_args = GRPOConfig(
    temperature=1.0,
    num_generations=8,               # 1 プロンプトあたりの生成数 = グループサイズ
    learning_rate=1e-5,
    per_device_train_batch_size=48,  # 6 プロンプト x 8 生成
    max_steps=60,
    bf16=True,
)

講師の「見るべきは、損失はもう全く見なくて良くて、見るべきは報酬です」という言葉どおり、GRPO の損失は方策勾配の目的関数値なので単調減少しませんし、負にもなります。自分の実行では、報酬の平均が最初の 5 ステップで -0.869、最後の 5 ステップで +0.324 まで上がりました。60 ステップ、A100 1 枚で 11 分 34 秒です。序盤 26 ステップほどはほぼ -1 に張り付いた停滞期で、報酬サーバーのログを見るとモデルは中国語やハングルまじりの崩れたテキストを吐いています。27 ステップ目あたりで JSON らしきものが出始めると一気にブレイクスルーし、あとは右肩上がりでした。形式ボーナスの +0.3 は、この停滞期を脱出するための「安い部分報酬」の梯子として機能しているようです。完全一致が出ない序盤でも、JSON の形さえ出せば報酬に差がつき、グループ内の標準偏差が立ち上がって学習信号が生まれます。

もうひとつ、この Lab で一番おいしかったのは講義と実装のギャップの種明かしです。実は Lab 2 の設定は GRPOTrainer のデフォルト値をほぼそのまま使っていて、num_iterations=1 なので古いポリシーが存在せず確率比は常に 1、clip も実質無効、さらに beta=0 なので KL 正則化項も消えています。つまりデフォルトの GRPOTrainer は、座学で丁寧に導出した項のうち advantage だけが残った素の形で動いているという話でした。このデフォルトはフレームワークごとに違うそうで、数式を追った直後にこの種明かしをされると「フレームワークのデフォルトを疑って読む」目が養われますね。

細かい詰まりどころとしては、Lab 1 で立てた vLLM が GPU メモリを掴んだままだと GRPO の学習開始時に OOM になります。会場でも多発していて、対処は Lab 1 のシャットダウンセルを実行するか、nvidia-smi で該当プロセスを特定して kill でした。

Lab 3 で学習済みエージェントを動かす

仕上げは学習した LoRA アダプタを載せたモデルをエージェントとして動かすパートです。日本語の指示を JSON ツールコールに変換し、コマンドの allowlist 検査を通し、実行前に人間の確認を挟んでから実際に CLI を実行します。手元でも「agent-python のテンプレを使って新しいプロジェクト作って」という指示から langgraph new が走り、実際にディレクトリが生成されるところまで動きました。

実装で個人的に一番好きだったのは、システムプロンプトの扱いです。学習ノートブックが推論用エージェントの設定ファイルからプロンプトを import する作りになっていて、学習と推論で同一の文字列が使われることが構造的に保証されています。

# 引用元: ブートキャンプ教材 02_grpo_training_ja.ipynb(抜粋)
# 学習と推論で同一のプロンプトを使うため、config.py の json_system_prompt を
# 単一の情報源として読み込む(不一致があると小さいモデルは崩壊する)
sys.path.insert(0, "bash_agent")
from config import Config
SYSTEM_PROMPT = Config().json_system_prompt

0.6B クラスの小型モデルは学習時と推論時のプロンプトが少しズレるだけで出力が崩壊するため、この「単一情報源」設計は実務でも真似したいパターンです。安全装置側も、バッククォートや $ を含むコマンドの拒否、コマンドを分割して先頭トークンを allowlist と突き合わせる検査、そして最後に y/N の人間確認と、教材ながら真っ当な多層防御になっていました。

一方で小型モデルの限界も観察できました。パスを指定しない指示を与えると、学習データの分布から app/agent のようなパスを勝手に補完してきます。スキーマ上 REQUIRED のフィールドは埋めるしかないので、モデルが値を発明するわけです。またツールの実行結果を受け取った後の応答は、オウム返しや文字化けで崩壊します。単発の JSON 生成しか学習していないので、マルチターンは分布外なんですね。何を学習させたかがそのまま挙動の境界になる、という意味で教育的な壊れ方でした。

自由時間で reasoning の獲得に挑んでみた

Lab 3 の後は自由改造タイムです。講師のおすすめ課題は「<think> タグに報酬を与えて、リーズニングモデルを作れるか試す」というもので、自分もこれに挑みました。<think> を含む出力に +0.5 の報酬を足して回したのですが、結果は 2,880 ロールアウトで発火ゼロ。プロンプトで思考を促しても変わりませんでした。

理由を考えると、これは Tutorial 1 の「RL の学習データはモデル自身からサンプリングされる」に戻ってきます。Qwen3-0.6B-Base の事前学習分布に <think> という形式がほぼ存在しないため、探索で一度もサンプリングされず、報酬はサンプリングされない行動を強化できません。DeepSeek-R1-Zero がテンプレートで <think> を強制していた理由を、時間切れの敗北とともに体で理解した格好です。few-shot 例やテンプレート強制で分布に種を撒いてから報酬で増幅する、というリベンジ案は手元の DGX Spark で検証する予定です。

印象に残ったポイント

参加してみて、自分の中で整理がついたポイントが 4 つあります。

1 つ目は、報酬はサンプリングされる行動にしか作用しない、という RL の原理です。座学で聞いた「SFT はデータ固定、RL はモデル自身からサンプリング」という対比が、自由時間の発火ゼロという実体験で腹落ちしました。新しい出力形式を教えたいなら、報酬を置く前に分布へ種を撒く必要があります。

2 つ目は、GRPOTrainer のデフォルトは advantage だけが残った素の形だという種明かしです。論文の式にある clip も KL も、デフォルト設定では消えています。フレームワークが違えばデフォルトも違うので、GRPO を回すときは「いまどの項が生きているのか」を確認してからハイパーパラメータを触るべきですね。

3 つ目は、報酬設計の難しさです。「学習自体はもう簡単に回せる。一方で報酬の構築は非常に難しい」という講師の言葉は、形式ボーナスが cold start の梯子として効く一方、大きくしすぎると形式だけ整えた出力に報酬が集まる報酬ハッキングの入口にもなる、という両面を見た後だと重みが違います。報酬関数は回す前に、実際の生成結果でユニットテストしておきたいところです。

4 つ目は、RL では CPU の重要性が高まっているという運用面の話です。検証可能報酬の「検証」はコード実行なので、GPU ではなく CPU で走ります。今回の NeMo Gym 報酬サーバーも CPU だけで動いていました。RL のインフラを見積もるとき、GPU だけ数えていると足をすくわれそうです。

こうして並べてみると、4 つとも派手な精度の話ではなく、報酬が上がらない、データに変な言語が混ざる、GPU メモリが解放されない、といった泥臭い部分ばかりです。ただ、実案件でモデルカスタマイズを選択肢に入れるときに効いてくるのはまさにこのあたりで、きれいに動くサクセスパスをなぞるだけでなく、詰まりどころや設計の勘所まで込みで体験させる構成は貴重だったかなと思っています。未経験者を集めてもとりあえず形になる SFT に寄せず、GRPO で 1 日やり切る設計にしたのも、この「泥臭さごと持ち帰ってほしい」という意図の表れなのかもしれません。

会場の雰囲気は前回同様で、各社から複数名参加のチームがお互いに聞き合いながら進めていました。講師が質疑で「RL は実験の試行回数が必要」と補足していたとおり、この領域は一発では決まらず、設定を変えては回し直す試行錯誤の繰り返しが前提の世界です。実際、同じ教材で回しているはずの隣の席と報酬カーブの形が全然違うのも、この題材ならではの光景でしたね。

まとめ

Model Customization Bootcamp は、合成データ生成から検証可能報酬の強化学習、そしてエージェント実行までを 1 日でつなげて体験させてくれるハンズオンでした。REINFORCE から GRPO までの数式の導出と、TRL のデフォルト値の種明かしがセットになっているおかげで、「なんとなく GRPO を回す」ではなく「どの項が効いているか分かった上で回す」ところまで連れて行ってもらえたのが大きかったです。一方で、韓国語混入や <think> の発火ゼロのように、自分の手を動かしたからこそ見えた課題も持ち帰ることができました。

参考リンク

本記事中のコードスニペットはブートキャンプ配布物より、出典を明記のうえ短い断片を引用しました。配布物は NVIDIA GenerativeAIExamples の bash_computer_use_agent(Apache License 2.0)をベースにしています。


AI白書2026 配布中

クラスメソッドが独自に行なったAI診断調査をもとに、企業のAI活用の現在地を調査レポートとしてまとめました。企業規模別の活用度傾向に加え、規模を超えてAI活用を進める企業に共通する取り組みまで、自社の現在地を捉えるためのヒントにぜひ。

AI白書2026

無料でダウンロードする

この記事をシェアする

関連記事