
Hugging Face のモデルをそのまま学習できる NeMo AutoModel を DGX Spark で動かしてみた
はじめに
こんにちは、クラスメソッド製造ビジネステクノロジー部の森茂です。
NVIDIA の学習ライブラリ NeMo AutoModel が、いつのまにか DGX Spark で普通に使える状態になっていました。以前 NeMo Framework を俯瞰する記事を書いたとき、AutoModel の ARM64 対応は「要検証」のままにしていたのですが、上流には DGX Spark 向けのレシピが揃い、GB10 の実機で CI まで回っています。
自分はこれまで、DGX Spark でのファインチューニングに AutoModel を使ってきませんでした。Nemotron 3 Nano の日本語学習では Hugging Face の PEFT と TRL を直接使い、Nemotron 3.5 Lightning を LLM ルーターの判定役に仕立てたときは、cookbook が H100 8 枚前提だったので Megatron-Bridge をクラウドで回しています。1 台の DGX Spark で素直に使える形になったのかを確かめるのが、この記事の動機です。
上の俯瞰記事(2026-04-21 時点の記事です)で △ にしていた AutoModel の行を、この記事で更新します。
この記事では、NeMo AutoModel の概要と DGX Spark 対応の現状、公式コンテナ 26.08 で Qwen3-8B の LoRA と full SFT を回した最初の収穫を紹介します。DGX Spark を 1 台持っていて、Hugging Face のモデルを手元で学習させたい人に刺さるといいなと思っています。
NeMo AutoModel は Hugging Face のモデルをそのまま学習に回す仕組み
NeMo AutoModel は、NVIDIA が公開している PyTorch ネイティブの学習ライブラリです。公式の説明を借りると「PyTorch DTensor ネイティブの SPMD な学習ライブラリで、Hugging Face のモデルを day-0 でサポートする」ものです。Hugging Face Hub にあるモデルをそのまま from_pretrained で受け取り、SFT、LoRA や QLoRA などの PEFT、知識蒸留、VLM の学習までを同じ枠組みで扱えます。学習した結果も Hugging Face 形式の safetensors で出てくるので、変換を挟まずに vLLM などの推論側へ渡せます。
NeMo と名のつくツールは増え続けているので、位置付けを先に整理しておきます。NeMo Framework の LLM 向けの部分は、役割ごとに独立したリポジトリへ分かれています。
| 役割 | コンポーネント | AutoModel との関係 |
|---|---|---|
| データの準備 | NeMo Curator、Data Designer | ここで作った JSONL を AutoModel のレシピに渡す |
| 学習・カスタマイズ | NeMo AutoModel、Megatron-Bridge | 同じ層の 2 本立て。HF 形式なら AutoModel、Megatron 形式なら Megatron-Bridge |
| 強化学習 | NeMo RL | AutoModel と Megatron-Bridge と vLLM の上に組まれている |
| 評価 | NeMo Evaluator | 学習後のモデルを OpenAI 互換 API 経由で採点する |
| 配信 | NIM、vLLM | AutoModel が出した safetensors や LoRA adapter を読む |
学習の層には AutoModel と Megatron-Bridge の 2 本があり、ここが一番迷いやすいところです。Megatron-Bridge は Megatron-Core ベースで、チェックポイントを Megatron の形式に変換して扱い、大規模な事前学習や深いカスタマイズに向きます。AutoModel は Hugging Face 互換のチェックポイントをそのまま扱う側で、1 GPU の LoRA から複数ノードの学習まで同じレシピの形式で書けます。自分の理解では、Hugging Face にある重みから始めて Hugging Face の形式で終わりたいなら AutoModel、Megatron の世界で学習を組みたいなら Megatron-Bridge、という切り分けです。
使い方の骨格はシンプルで、YAML のレシピ 1 枚に「どのモデルを、どのデータで、どの並列化で学習するか」を書き、automodel <recipe.yaml> --nproc-per-node N で起動します。モデルの指定は nemo_automodel.NeMoAutoModelForCausalLM.from_pretrained という Hugging Face 互換のクラスで、レシピの中身は普通の PyTorch と Hugging Face の語彙で読めます。分散学習は FSDP2 と DTensor が土台で、モデルの大きさと GPU の枚数に合わせて tensor parallel、context parallel、expert parallel を重ねる設計です。checkpoint は DCP と safetensors の両方に対応し、FP8 の学習や投機デコード用 drafter の学習もレシピの選択肢に入っています。Hugging Face 側にも AutoModel を使ったファインチューニングのドキュメントがあり、MoE モデルでは Transformers v5 比で 3.4〜3.7 倍のスループットが出たという記事を NVIDIA が公開しています。
あえて AutoModel を選ぶ理由
Hugging Face のモデルを LoRA するだけなら、PEFT と TRL を直接使う道も、Unsloth のような軽いツールもあります。自分も Nemotron 3 Nano を日本語で学習したときは PEFT と TRL を直接使いました。それでも今は AutoModel を選ぶ場面が増えていて、理由は 4 つあります。
1 つ目は、入口と出口が Hugging Face 形式のまま閉じることです。学習の入力は Hub のモデル ID かローカルのスナップショット、出力は safetensors か PEFT 形式の adapter で、別の検証では AutoModel が出した adapter を vLLM にそのまま読ませて chat completion が返ってきました。学習と配信の間で形式変換を挟まないので、試行の回数を増やしやすいですね。
2 つ目は、NVIDIA がハードウェアの面倒を見ていることです。DGX Spark の GB10 は compute capability 12.1 で、データセンター向けの Blackwell とは別物です。ここに合わせた bitsandbytes のビルドや、cuDNN が扱えない経路を避けるレシピの指定は、自分で追いかけると地味に時間を取られます。AutoModel では GB10 の実機が CI に入っていて、この手の対応がコンテナとレシピに畳み込まれた状態で届きます。
3 つ目は、1 GPU で書いたレシピがそのまま大きい環境に持っていけることです。LoRA の rank や学習率は YAML の値で、並列化の設定は distributed のブロックで独立しています。手元の 1 台で回して当たりを付けたレシピを、後からクラウドの GPU に持っていくときに書き直しが要りません。
4 つ目は、NeMo の他のツールとつながることです。自分は合成データを Data Designer で作り、Curator で整え、AutoModel で学習して、Evaluator で採点するという流れを組んでいます。それぞれ独立したツールなので個別に差し替えられますが、データの形式や評価の API が揃っているので、パイプラインとして組んだときの継ぎ目が少ないです。
自分が組んでいる流れを図にすると次のようになります。AutoModel は真ん中の学習の箱で、入出力はどちらも Hugging Face の形式です。
一方で、深いカスタマイズが要る学習は Megatron-Bridge の領分ですし、コンテナの中身は 2 か月ごとに更新されるので、レシピの互換性は版ごとに確かめる必要があります。後半で触れるバージョン表示の癖もその一例です。過度な期待は禁物ですが、Hugging Face のモデルを DGX Spark で普通に学習したい、という用途には今のところ一番手間の少ない道かなと思っています。
DGX Spark 対応はリリースノートではなく PR の積み重ねで見える
自分が対応を見落としていた理由は単純で、リリースノートに「DGX Spark 対応」と書かれた行が v0.2.0 の 1 行しかなかったからです。実際の対応は PR とレシピの YAML で積み上がっていて、GitHub の履歴をたどると次のように見えます。
| 日付 | 変更 | 収録 |
|---|---|---|
| 2025-10-14 | Discussion #640 で DGX Spark 向け playbook を告知 | — |
| 2025-10-15 | PR #645 Qwen3-8B の DGX Spark 向け SFT レシピを追加 | v0.2.0 以降 |
| 2025-10-19 | PR #657 GPT-OSS を DGX Spark の 1 GPU で回す dequantization | v0.2.0 以降 |
| 2026-02-05 | PR #1164 統合メモリ向けの streaming safetensors writer | v0.3.0 以降 |
| 2026-08-14 | PR #3538 DGX Spark 向けレシピを GB10 の実機 CI ランナーへ | v0.6.0(NGC 26.08) |
| 2026-08-14 | PR #3553 bitsandbytes を SM121 ネイティブでビルド | v0.6.0(NGC 26.08) |
| 2026-08-18 | PR #3533 単 GPU の DCP ロード修正と Lightning 向け単 GPU LoRA レシピ | main のみ |
| 2026-09-02 | PR #3766 64GB の RTX Spark 向け学習サポート | main のみ |
転機は 2026 年 8 月 14 日の 2 件です。GB10 の実機が CI ランナーに入り、Qwen3-8B の SFT、GPT-OSS-20B の LoRA、Llama-3.3-70B の QLoRA の 3 本が GB10 実機の CI 対象になりました。同じ日に bitsandbytes を SM121 向けにネイティブビルドする変更が入っています。この 2 件は v0.6.0 のリリースブランチに cherry-pick され、v0.6.0 は 8 月 26 日に公開、対応する NGC のタグが 26.08 です。
v0.6.0 に入っている DGX Spark 向けレシピは 4 本です。Qwen3-8B の SQuAD SFT、GPT-OSS-20B の単 GPU LoRA、Llama-3.3-70B の QLoRA、そして Nemotron 3 Nano 30B-A3B の単 GPU LoRA です。最後の 1 本は ep_size 1 で書かれていて、自分が以前「cookbook は H100 8 枚前提」と判断して避けた MoE の LoRA が、1 台向けのレシピとして公式に存在していました。
導入経路は 3 つあるがコンテナ一択でよい
DGX Spark で AutoModel を動かす経路は、NGC のコンテナ、pip の wheel、build.nvidia.com の playbook の 3 つがあります。手元で確認した範囲を並べると次のとおりです。
| 経路 | 中身の鮮度 | arm64 での実績 | 備考 |
|---|---|---|---|
NGC nemo-automodel:26.08 |
2026-08-26、r0.6.0 ブランチ | この記事で確認。multi-arch で linux/arm64 あり | 圧縮約 13GB、展開後 29.4GB |
pip nemo-automodel |
0.6.0(PyPI) | 未確認 | wheel は純 Python。TE や bitsandbytes の aarch64 ビルドは別 |
| build.nvidia.com playbook | 2026-03-04、26.02 を指定 |
フォーラムで 70B QLoRA が "Killed" になった報告あり | 手順は同じ構造。タグだけ 3 世代古い |
playbook は「Fine-tune with NeMo」として公開されていて、手順自体は今回と同じく docker run でコンテナに入ってレシピを叩く形です。ただ指定しているタグが 26.02 のままで、GB10 の CI や bitsandbytes の SM121 ビルドが入る前の版です。pip の wheel は py3-none-any で中身は純 Python なので、AutoModel 本体よりも Transformer Engine や bitsandbytes を aarch64 でどう揃えるかが本題になります。今回はそこに踏み込まず、コンテナだけを使いました。
個人的には、DGX Spark で使うならコンテナ一択でいいと思っています。NGC のイメージは linux/arm64 のマニフェストを持っていて docker pull するだけで揃いますし、レシピも /opt/Automodel/examples/ に同梱されています。
公式 Spark レシピを LoRA 化して Qwen3-8B を短走させる
コンテナに同梱されている Spark 向けレシピ examples/llm_finetune/qwen/qwen3_8b_squad_spark.yaml は full SFT の設定です。今回は使い方の確認が目的なので、このレシピをベースに LoRA を足し、30 step で止まる短い版を作りました。変更したのは step 数とバッチサイズ、検証と保存の間隔、peft と checkpoint のブロック、データのサンプル数、モデルのパスで、CI 用の ci ブロックは外しました。Spark 向けの肝である attention とロス関数、sequence packing はそのまま残しています。
step_scheduler:
global_batch_size: 2 # 公式は 64
local_batch_size: 1 # 公式は 2
ckpt_every_steps: 30
val_every_steps: 30
num_epochs: 1
max_steps: 30
model:
_target_: nemo_automodel.NeMoAutoModelForCausalLM.from_pretrained
pretrained_model_name_or_path: /workspace/models/Qwen3-8B # 取得済みの重みを読み取り専用でマウント
# Keep packed training on the HF FlashAttention path for GB10. TE's packed THD
# backward requests ragged LSE, which cuDNN does not support on SM12x GPUs.
force_hf: true
attn_implementation: flash_attention_2
peft:
_target_: nemo_automodel.components._peft.lora.PeftConfig
target_modules: '*_proj'
dim: 8
alpha: 32
use_triton: false
checkpoint:
enabled: true
checkpoint_dir: /workspace/run/checkpoints
model_save_format: safetensors
save_consolidated: final
dataset:
_target_: nemo_automodel.components.datasets.llm.squad.make_squad_dataset
dataset_name: rajpurkar/squad
split: train
limit_dataset_samples: 512
force_hf: true と flash_attention_2 の 2 行は公式レシピにコメント付きで入っている GB10 向けの指定です。Transformer Engine の packed attention が使う backward を GB10 の cuDNN が扱えないため、Hugging Face 側の FlashAttention 2 経路に固定しています。この手の事情がレシピにコメントとして書かれているのは、実機で CI を回している副産物かなと思います。
実行はコンテナを 1 回起動するだけです。モデルの重みは事前に取得したものをマウントし、SQuAD だけをコンテナ内から取得しました。以下は実際に使った run.sh から抜き出した再現用の形で、作業ディレクトリ直下に config/ を置く前提です。
docker run --rm --gpus all --network=host --shm-size=32g \
--ulimit memlock=-1 --ulimit stack=67108864 \
--workdir /opt/Automodel \
-v "$PWD:/workspace/run" \
-v "$HOME/models/qwen3-8b-b968826d:/workspace/models/Qwen3-8B:ro" \
-v "$HOME/.cache/huggingface:/root/.cache/huggingface" \
nvcr.io/nvidia/nemo-automodel:26.08 \
automodel /workspace/run/config/qwen3-8b-squad-spark-lora.yaml --nproc-per-node 1
結果は次のとおりです。ロードから 30 step、検証、checkpoint 保存までコンテナの中で 90 秒でした。
| 項目 | 値 |
|---|---|
| step 数 | 30(すべて有限の loss) |
| 学習 loss | 7.9741 → 1.9950(この回の 30 step) |
| 検証 loss(64 件) | 2.7526 |
| CUDA 最大割り当て | 20.78 GiB |
| 1 step の時間(中央値) | 1.69 秒 |
| スループット(中央値) | 1,069.63 tok/s |
| コンテナ内の壁時計 | 90 秒 |
| adapter の大きさ | 43,713,952 bytes(rank 8、alpha 32) |
loss が下がっているのは SQuAD を 512 件に制限した設定で 30 step だけ回した結果なので、学習の成果としては読まないでください。ここで確かめたかったのは、公式の Spark レシピの経路が 26.08 の arm64 イメージでそのまま通ること、LoRA の adapter が safetensors で出てくること、そしてメモリが 128GB のどのあたりに収まるかの 3 点です。今回の条件では、Qwen3-8B の BF16 LoRA の CUDA 最大割り当ては 20.78 GiB でした。
128GB の統合メモリでどこまで載るかを手元の実績で並べる
今回の 30 step だけでは物足りないので、同じ AutoModel の一つ前のコンテナ(26.06.00)で回した自分の実績も並べておきます。条件が違うので横並びの比較ではなく、あくまで 1 台に載るかどうかの目安として見てください。
| モデルと方式 | コンテナ | 条件 | CUDA 最大割り当て |
|---|---|---|---|
| Qwen3-8B BF16 LoRA、30 step | 26.08 | packing 1024、batch 2 | 20.78 GiB |
| Qwen3-8B BF16 LoRA、360 step、720 件 1 epoch | 26.06.00 | seq 1,024、batch 2、6 分 09 秒 | 19.16 GiB |
| Qwen3-14B LoRA、176 step | 26.06.00 | 文脈長 2,048、batch 2 | 42.85 GiB |
| Nemotron 3.5 Lightning 30B-A3B BF16 LoRA、10 step | 26.06.00 | MTP 層を外す、1 step 約 4 秒 | 63.25 GiB |
| Qwen3-8B BF16 full SFT、公式レシピ、305 step 完走 | 26.08 | batch 64、packing 1024、約 72.54 秒/step | 77.70 GiB |
8B の LoRA が 20.78 GiB、14B で 42.85 GiB、30B の MoE でも 63.25 GiB で、表の条件ではどれも 1 台の GB10 に収まりました。一方で 8B の full SFT は公式レシピの設定のまま 77.70 GiB まで使い、1 step が約 72.54 秒でした。
この full SFT は最後まで回しています。SQuAD の 1 epoch にあたる 305 step を 6 時間 11 分で完走し、学習 loss は 7.0378 から 0.0637 まで、検証 loss は 4.4213 から 0.0960 まで下がりました。検証 loss は step 39 以降ほぼ横ばいなので、この後半はレシピの都合で回しているだけです。完了時には checkpoint として、Hugging Face 形式に統合した safetensors が 5 ファイル約 16.4GB、学習再開用の分割済み重みと optimizer の状態を合わせて 62GB が書き出されました。統合した重みはそのまま vLLM に渡せる形です。8B の BF16 full SFT は、1 台の DGX Spark で「載る」だけでなく「6 時間待てば終わる」と言える範囲でした。
では、どのくらいの大きさまで full SFT が現実的なのでしょうか。8B で 77.70 GiB なので、同じ計算を 14B に当てはめると約 136 GiB になり、Linux から見える 121 GiB を超えます。optimizer の状態を CPU 側へ逃がす、8-bit の optimizer を使うといった手はありますが、手元では試していません。公式の playbook も DGX Spark の組み合わせを「8B は full SFT、70B は QLoRA」で示しているので、BF16 の full SFT は 8B クラスまで、それより上は LoRA か QLoRA、というのが今の自分の見立てです。
個人的に意外だったのは Lightning です。以前の自分は「BF16 の LoRA は統合メモリでは載らない」と判断して 4-bit の QLoRA か H100 を選んでいたのですが、同梱の Nemotron 3 Nano 用レシピにある ep_size 1 と LoRA 対象の絞り込みを下敷きに、Lightning 固有の MTP 層を外せば 63.25 GiB で回りました。この Lightning の走り込みは別の記事で扱う予定なので、ここでは数字だけにとどめます。
26.08 で気をつける点を先に書いておく
使い方は以上なのですが、コンテナの中を見ていて引っかかった点がいくつかあったので、同じことを試す方のために先に書いておきます。
まずバージョンの表示です。NGC のタグは 26.08 で、リリースとしては v0.6.0 に対応するはずなのですが、コンテナ内で nemo_automodel.__version__ を見ると 0.5.0+922e83e42 と出ます。起動時のバナーも「NVIDIA Release 26.06」です。GitHub で追うと、このコミットは v0.6.0 のリリースブランチにあり、タグの 3 コミット手前でビルドされたものでした。差分は LoRA backward の修正 1 件とバージョン番号のバンプ 2 件で、GB10 の CI と bitsandbytes の SM121 ビルドは含まれています。中身は v0.6.0 相当と考えてよさそうですが、バージョン文字列を信じて判断すると混乱します。
次に CUDA です。コンテナは CUDA 13.3 で、手元の DGX Spark のカーネルドライバは 580.159.03 でした。起動時に「CUDA Forward Compatibility mode ENABLED」と出て、この組み合わせでは 30 step が完了しました。ドライバを上げずに新しいコンテナを動かす仕組みで、今回は問題になっていません。
ログの出方にも癖があります。学習の loss と mem と tps は 1 step ごとに INFO 行で出ますが、これは stderr 側です。stdout にはレシピの内容と進捗バーが出ます。2> で分けてしまうと loss が見えなくなるので、両方を残しておくのが無難です。
checkpoint まわりは 2 点あります。コンテナは root で動くので、checkpoints/ 配下のファイルは root 所有で書かれます。自分は同じイメージで chown をかけてから回収しました。もう 1 点は起動時の警告で、save_consolidated: final を指定すると「v4_compatible=False なので transformers v4 とは互換性がないかもしれない」と出ます。コンテナの transformers は 5 系なので今回はそのまま進めましたが、古い transformers で読む予定があるなら checkpoint.v4_compatible を見ておくとよさそうです。
なお、PR #3533 で追加された Lightning 向けの単 GPU LoRA レシピは main にはありますが、26.08 のイメージには入っていません。同梱の nemotron_nano_v3_singlegpu_lora.yaml をベースに自分で書き足す必要があります。
まとめ
NeMo AutoModel は、DGX Spark 向けの公式レシピと GB10 の実機 CI を持つ状態になっていました。NGC の 26.08 コンテナは linux/arm64 のイメージを持ち、公式の Spark レシピを LoRA 化しただけの YAML で Qwen3-8B の学習が CUDA 最大割り当て 20.78 GiB で回り、同じレシピの full SFT も 77.70 GiB で 305 step を完走しました。以前の俯瞰記事で △ にしていた行は、「8B の LoRA と full SFT が公式コンテナで完走する」まで進めてよさそうです。
一方で、今回確かめたのは LoRA の 30 step と full SFT の完走、それに 1 台に載るメモリの目安までです。学習後のモデルの品質、pip での導入、FP8 は見ていません。コンテナのバージョン表示が r0.6.0 ブランチの途中を指している点は、数字を人に伝えるときに注意が要ります。
Nemotron 3.5 Lightning の単 GPU LoRA は、一つ前のコンテナで学習から vLLM での配信まで一周させてあります。こちらは日本語の Tool Calling を題材にした学習と評価を含めて、別の記事で扱う予定です。
参考リンク
- NVIDIA-NeMo/Automodel(GitHub)
- NeMo AutoModel ドキュメント
- NeMo AutoModel コンテナ(NGC)
- Fine-tune with NeMo(DGX Spark playbook) — 2026-03-04 更新、
26.02を指定 - PR #3538 ci: route DGX Spark recipes to GB10(2026-08-14 マージ)
- PR #3553 fix(docker): build bitsandbytes for SM121(2026-08-14 マージ)
- PR #3533 fix(checkpoint): restore single-GPU custom-model DCP loading(2026-08-18 マージ、main のみ)
- Discussion #640 DGX Spark + NeMo: Model Training Power in the Palm of Your Hand
- Accelerating Transformers Fine-Tuning with NVIDIA NeMo AutoModel(Hugging Face Blog)
- NVIDIA NeMo Framework を俯瞰してみた(2026-04-21 時点の記事)
- Nemotron 3.5 Lightning を LoRA 事後学習で LLM ルーターの判定役に仕立ててみた
- DGX Spark で Nemotron 3 Nano を日本語ファインチューニングしてみた







