Unslothのブログ「How to Make LLM Training Faster with Unsloth and NVIDIA」は、かなり実践的でおもしろい記事です。ざっくり言うと、NVIDIAと一緒にLLM学習のボトルネックを3つ潰して、さらに速くしたという話です。
ここで重要なのは、ただ「GPUを速いものにした」わけではないことです。
やっているのはもっと地味だけど効く仕事、つまり:

こういう改善です。個人的には、こういうアルゴリズムとシステムの両方をいじって速くする話がいちばん面白いです。派手さはないけど、効き方が本物だからです。
しかも今回の改善は、Unslothがもともと持っていた2〜5倍の高速化に“さらに上乗せ”される形です。ここ、かなり大きいです。

LLMの学習では、長さの違う文章をまとめてGPUに流します。
そのとき、短い文章を無理やり同じ長さまで引き伸ばすと、padding tokenという“空白”が増えて無駄になります。
そこで使うのが packed sequence です。
複数の短い例を連結して、1本の長い列として扱います。これでpaddingを減らせます。
でも、ここで新しい問題が出ます。
「元の文章がどこからどこまでだったか」を、モデルがちゃんと知っていないといけないからです。
そのために必要なのが、たとえば以下の情報です。

cu_seqlens)UnslothとNVIDIAが見つけたポイントは、このメタデータは1回のforward passの中では全レイヤーで同じだということです。
つまり、Transformerが何層もあるなら、本来は

わけです。
ところが、実際には層ごとに同じ情報を何度も組み立て直すことがありました。
これはかなりもったいない。しかも単なる計算コストだけでなく、GPUとCPUの同期が入ってしまうことがあるのが痛いです。GPUが「ちょっと待って、CPUから情報が来るまで止まるね」となるわけで、これが積み重なると地味に効きます。
そこでやったのが、

など、再利用できる情報をキャッシュすることです。
要するに、同じ材料を毎回こね直さず、一度作ったものを使い回すようにした、ということです。
こういう改善って、地味なんですが本当に効きます。
LLMの学習速度は「大きな演算」だけで決まるわけではなく、細かい段取りの悪さでもかなり削られるからです。
Qwen3-14BのQLoRA SFTでは:

特にforwardが大きく伸びています。
これは、メタデータやmaskの準備がforward側で何度も出てくるからです。
個人的には、この数字はかなり納得感があります。
「学習そのもの」より「準備作業」を削ると、forwardで効きやすいんですよね。

activation checkpointing は、メモリを節約するための定番技術です。
普通はforward中の中間結果(activation)をたくさん保存しますが、それを全部持っておくとVRAMを食います。
そこでcheckpointingでは、必要なところだけ保存して、backwardのときに再計算します。
メモリは節約できるけど、そのぶん計算が少し増える、というトレードオフです。
これは大きいモデルではとても有効です。
ただし問題は、保存しなかったactivationをどうやってbackward時にGPUへ戻すかです。

Unslothのsmart checkpointingでは、activationをpinned CPU memoryに置いておき、必要になったらGPUにコピーします。
ここでまずいのが、1つのバッファをコピーと計算で共用すると、次のような順番になりやすいことです。
つまり、コピーと計算が交互に順番待ちになってしまうんです。
これではせっかくGPUが強くても、待ち時間が目立ってしまいます。
そこで使うのが double buffering です。
バッファAでbackwardをしている間に、バッファBへ次のactivationを先読みしておく。終わったら役割を入れ替える。これでコピーと計算を重ねることができます。

もちろん、完全に重なるわけではありません。でも、待ち時間をかなり隠せます。
こういうのは、いかにもシステム最適化らしい改善です。
計算そのものを減らすのではなく、**“待つ”という無駄を見えなくする**のがうまいです。
NVIDIA B200 Blackwell GPUでの大きめdense modelの結果はこうです。

メモリ増加も比較的小さめです。
lossもほぼ変わっていないとのことなので、速くしたのに学習の中身は変えていないのがポイントです。

個人的には、この「追加のVRAMは少しだけ、でも効き目はちゃんとある」というバランスがかなり良いと思います。
実運用では、速さだけでなくメモリの余裕も大事なので。
MoE(Mixture of Experts) は、モデルの中に複数の“専門家”を持たせて、入力ごとに使う専門家を切り替える仕組みです。
全員を毎回フル稼働させるのではなく、必要な人だけ呼ぶイメージです。

うまくハマると効率が良いのですが、ルーティング処理がややこしくなります。
記事では、PyTorchベースのGPT-OSSのMoEパスで、各expertにどのtokenを送るかを調べる処理が重かったと説明しています。
素朴な実装だと、expertごとに
torch.where(router_indices == expert_idx)

みたいなことを繰り返してしまうことがあります。
でもこれ、expertの数だけ動的な問い合わせが走るので、無駄が増えやすいです。しかもデータ依存なので、CPU-GPU同期っぽいコストが見えにくく出ることがあります。
より良いやり方は、まとめて一気に処理することです。
bincount を1回使って各expertのtoken数を数える
要するに、1回でまとめて整列してから切り分けるやり方です。
こういう改善は、地味だけど「ちゃんとプログラムしてるなあ」と感じます。動くコードと速いコードは別物、というやつですね。
gpt-ossのtrainingでは、これで15%高速化したとのことです。
MoEは本質的に“分岐の多い仕組み”なので、こうしたルーティングのムダが効きやすいのだと思います。

正直、今回のブログはかなり好印象でした。
理由は、単に「速くなりました」と言うだけではなく、なぜ速くなるのかをかなり丁寧に説明しているからです。
特に良いのは、各改善について
まで追っている点です。こういう記事は、読んでいて信頼しやすいです。
![]()
また、今回の改善はすべて「魔法」ではありません。
どれも、
という、かなり王道の最適化です。
でも、王道だからこそ強い。LLMは巨大なので、こうした“細かい無駄”が積み重なると、最終的に大きな差になります。
個人的には、こういう改善は今後もかなり伸びしろがあると思います。
モデルが大きくなるほど、GPUの演算能力だけでなく、周辺の段取りがボトルネックになりやすいからです。
