本文へ移動

ホーム / ML / 第4部 学習と推論の基盤

第4部 学習と推論の基盤・第7章

学習の基盤

★★☆ 重要7トピック・面接 3問

大きなモデルを速く安く学習させるための基盤を押さえる。

別のページで開く →

ネットワーク図を読み込んでいます…

7.1GPU と分散学習

用語★★☆ 重要
ひとことで

データ並列は全員が同じモデルで別のデータを学習し、勾配をそろえる。通信が詰まると台数を増やしても速くならない。

大きなモデルや大量のデータを学習すると、GPU 1枚では時間がかかりすぎたり、そもそもモデルがメモリに載らなかったりします。複数の GPU にどう分けるかは学習の費用と期間を大きく左右し、「GPU を倍にしたのに速くならない」という問題の多くは、分け方と GPU 間の通信から来ます。

GPU は、同じ計算を大量に並べて同時に行うのが得意で、ニューラルネットの行列の計算に向いています。1台で時間がかかりすぎるときや、モデルが1枚の GPU のメモリに載らないときに、複数の GPU で分けて学習します(分散学習)。

何を分けるかで3つに分かれるデータ並列テンソル並列パイプライン並列GPU0:モデル全体データの 1 番目の束GPU1:モデル全体データの 2 番目の束GPU2:モデル全体データの 3 番目の束勾配を平均してそろえるGPU01層目の左半分GPU11層目の右半分GPU02層目の左半分GPU12層目の右半分層ごとに毎回たくさん通信GPU0:1〜10 層GPU1:11〜20 層GPU2:21〜30 層流れ作業で順に渡すまずデータ並列。モデルが1枚に載らないときに、ほかの分け方を組み合わせる
分散学習の3つの分け方
方式分け方使う場面
データ並列全 GPU が同じモデルのコピーを持ち、別々のデータで計算して、勾配を平均してそろえる(PyTorch の DDP など)モデルが1枚に載る。まずこれ
モデル並列(テンソル並列)1つの層の行列を GPU に分けて持ち、一緒に計算するモデルが1枚に載らない。GPU 間の通信がとても多いので、同じサーバーの中の高速な接続で使う
パイプライン並列層を前半・後半のように分けて GPU に置き、データを流れ作業で渡すとても深いモデル。待ち時間(空き)を減らす工夫が要る

大きなモデルでは、これらを組み合わせます。データ並列でも、モデルの重みやオプティマイザーの状態を GPU に分けて持ち、メモリを節約する方式(FSDP・ZeRO)がよく使われます。

データ並列では毎回の更新で勾配をそろえる通信が入るので、GPU 間・サーバー間の通信が遅いと、台数を増やしても速くなりません。サーバーをまたぐ学習では、高速なネットワーク(InfiniBand など)が要ります。

下の「ブラウザで試す」の図は、データ並列で4台の GPU が勾配をそろえる通信(リング all-reduce)です。最初に全員が rank 0 に集まって住所を教え合い、各自が勾配を計算したあと、勾配を4つに分けて右隣へ送りながら足し合わせ(reduce-scatter)、完成した切れをまた回して全員に配ります(all-gather)。毎回の更新でモデルと同じ大きさのデータが GPU の間を流れることと、1台が遅いと全員が待つことに注目すると、最後の段落の「通信が遅いと台数を増やしても速くならない」理由が見えます。

面接ではこう答える

分散学習には、全 GPU が同じモデルのコピーを持って別のデータを計算し、勾配を平均してそろえるデータ並列と、モデルが1枚の GPU に載らないときに層や行列を分けて持つモデル並列があります。まずはデータ並列を使い、メモリが足りなければ重みやオプティマイザーの状態を分けて持つ FSDP や ZeRO を使います。データ並列は毎回の更新で勾配をそろえる通信が入るので、GPU 間やサーバー間の通信が遅いと台数を増やしても速くなりません。

別のページで開く →

ネットワーク図を読み込んでいます…

7.2混合精度(FP16・BF16)

用語★★☆ 重要
ひとことで

計算の多くを16ビットの数で行い、メモリを減らして速くする方法。対応する GPU ならまず BF16。

BF16 は指数部が FP32 と同じなので、表せる範囲が広いFP32指数 8仮数 23FP16指数 5仮数 10BF16指数 8仮数 7指数:表せる範囲の広さ/仮数:細かさ(有効桁)
浮動小数点の形式(ビットの内訳)

混合精度(mixed precision):計算の多くを 32 ビットではなく 16 ビットの数(FP16・BF16)で行う方法です。メモリが約半分になり、最近の GPU では計算も速くなります。FP16 は表せる範囲が狭く、値が小さすぎて 0 になる問題があるので、損失を拡大して計算する仕組み(loss scaling)と組み合わせます。BF16 は範囲が 32 ビットと同じなのでこの問題が起きにくく、対応する GPU ならまず BF16 を選びます。PyTorch では torch.autocast で使えます。

面接ではこう答える

混合精度は、計算の多くを32ビットではなく16ビットの数で行う方法です。メモリが約半分になり、最近の GPU では計算も速くなります。FP16 は表せる範囲が狭く、小さな値が0になってしまうので、損失を拡大して計算する loss scaling と組み合わせます。BF16 は範囲が32ビットと同じなのでこの問題が起きにくく、対応する GPU ならまず BF16 を選びます。

7.3GPU メモリの見積もり

用語★★☆ 重要
ひとことで

推論は重み(16ビットなら1パラメーター約2バイト)、学習は勾配とオプティマイザーの状態も持つので約16バイト+途中の計算結果が要る。

「このモデルは何 GB の GPU に載るか」は、パラメーターの数からおおまかに見積もれます。面接でもよく聞かれる計算です。

学習は推論の何倍もメモリを使う(重み以外に勾配とオプティマイザーの状態)推論(16ビット)重み 2 バイト + KV キャッシュなど学習(混合精度+Adam)32bit の重みAdam の状態重み・勾配 各2 + 32bit の重み 4 + Adam 8 = 約16 バイト(+途中の計算結果)例:70億パラメーター → 推論は重みだけで約14GB、学習は約112GB+α
パラメーター1つあたりのメモリ(目安)
用途1パラメーターあたり70億(7B)パラメーターの例
推論(FP16・BF16)約2バイト約14GB + KV キャッシュ
推論(8ビット量子化)約1バイト約7GB + KV キャッシュ
推論(4ビット量子化)約0.5バイト約3.5GB + KV キャッシュ
学習(混合精度+Adam)約16バイト+途中の計算結果約112GB 以上 → 1枚に載らないので分割や LoRA

学習では、重みのほかに勾配、32ビットの重みの控え、Adam の2つの状態を持つので、重みだけの何倍ものメモリを使います。さらに、層ごとの途中の計算結果(活性化)がバッチサイズと系列の長さに比例して増えます。足りないときは、バッチを小さくして勾配をためる(勾配累積)、途中の計算を保存せず再計算する(gradient checkpointing)、FSDP・ZeRO で分けて持つ、LoRA で学習する重みを減らす、を組み合わせます。

面接ではこう答える

GPU メモリは、パラメーターの数から見積もれます。16ビットでの推論なら1パラメーターあたり約2バイトなので、70億パラメーターのモデルは重みだけで約14GBで、それに KV キャッシュが加わります。混合精度と Adam で学習すると、重みと勾配に加えて32ビットの重みの控えと Adam の状態を持つので約16バイトになり、さらに活性化も要ります。足りないときは量子化、勾配累積、gradient checkpointing、FSDP、LoRA を組み合わせます。

ひとことで

GPU を待たせないように、並列・先読みでデータを読む。

GPU のサーバーは高価で、計算していない時間にも料金がかかります。学習が遅いと GPU を増やしたり大きくしたりしたくなりますが、本当の原因はその手前の「データを GPU まで届ける部分」にあることが少なくありません。ここを直せば、同じ GPU のまま学習が速くなり、費用も下がります。

GPU がどんなに速くても、データが届くのが遅ければ GPU は待つだけです。GPU の使用率が低いのに学習が遅いときは、まずデータの読み込みを疑います。

先読みすると、GPU が計算している間に次のデータを用意できる先読みなし読み込みGPU読む読む読む読む計算計算計算計算終わり待ち待ち待ち待ち先読みあり読み込みGPU読む読む読む読む計算計算計算計算ここで終わる時間
先読みの効果

基本の対策:読み込み・デコード(画像の展開など)・前処理を、複数のプロセスで並列に行い、GPU が今のバッチを計算している間に次のバッチを用意しておきます(先読み)。PyTorch の DataLoader なら num_workers(並列の数。CPU のコア数を目安に調整)、prefetch_factor、pin_memory=True を設定します。前処理が重すぎる場合は、学習の前に済ませて保存しておきます。

S3 からの読み込み:オブジェクトストレージ(S3 など)は、1回のリクエストの待ち時間が長めですが、同時にたくさん読めば全体の速さは出ます。小さなファイル(画像1枚ずつなど)を何百万個も1つずつ読むと、待ち時間ばかりになって遅くなります。

  • 小さなファイルを数百 MB 程度の大きなファイルにまとめ、順に読む(WebDataset の tar、TFRecord など)。
  • 並列でたくさんのリクエストを出す。
  • 同じデータを何周も読むなら、ローカルの SSD にコピーするかキャッシュする。
  • GPU のサーバーと S3 を同じリージョンに置く(別のリージョンだと遅く、転送料金もかかる)。

S3 の基本は SRE の解説で

下の「ブラウザで試す」の図では、学習のサーバーが VPC エンドポイント経由で S3 からファイルの一覧を取り、1つのファイルを 8MB ずつに分けて Range ヘッダーで並列に読み、206 で範囲だけを受け取る流れを追えます。混みすぎたときの 503 SlowDown と、読みながら学習する(先読み)手順もあります。上の箇条書きの「大きなファイルにまとめる」「並列でたくさんのリクエストを出す」が、どの手順にあたるかに注目してください。

面接ではこう答える

GPU がどんなに速くても、データが届くのが遅ければ GPU は待つだけです。GPU の使用率が低いのに学習が遅いときは、まずデータの読み込みを疑います。読み込みや前処理を複数のプロセスで並列に行い、GPU が今のバッチを計算している間に次のバッチを用意する先読みを使います。S3 から読む場合は、小さなファイルを大きなファイルにまとめて順に読み、並列にリクエストを出し、同じリージョンに置きます。

別のページで開く →

ネットワーク図を読み込んでいます…

Parquet は列ごとにまとめて保存するので、必要な列だけ読めるCSV(行ごと)行1:id, 年齢, 金額, 地域行2:id, 年齢, 金額, 地域行3:id, 年齢, 金額, 地域Parquet(列ごと)id年齢金額地域「金額」だけ読める・型ごとによく縮む
行指向と列指向(Parquet)
ファイル形式向いているもの
CSV・JSON Lines小さなデータ・確認用。読み込みが遅く、型の情報がない
Parquet表形式のデータ。列ごとに圧縮され、必要な列だけ読める。迷ったらまずこれ
tar にまとめた形式(WebDataset など)・TFRecord画像・音声などの大量の小さなサンプルを、順に速く読む

大きな表データは、日付などで分けたフォルダ(パーティション)に Parquet で置くと、必要な期間と列だけを読めて速く安くなります。画像や音声は、数百 MB 程度の tar などにまとめると、S3 から順に速く読めます。

ひとことで

GPU は高価。スポットインスタンス・チェックポイント・使っていない時間の削減で抑える。

GPU のインスタンスは、普通のサーバーより1桁以上高いことが多く、ML のチームの費用の大部分を占めがちです。「使っている時間を減らす」「安い買い方をする」「使っている間は使い切る」の3つで抑えます。

スポットと中断への備え:スポットインスタンス(クラウドの余っている計算機を安く借りる仕組み)は、通常の料金より大きく安くなることがありますが、クラウドの都合で短い予告で止められます。学習に使うなら、止められても続きから再開できるようにします。

スポットで止められても、最後のチェックポイントから再開すれば失うのは少しだけ学習保存保存保存保存中断(スポットの回収)失うのはここだけ最後の保存から再開保存するもの:重み・オプティマイザーの状態・ステップ数・乱数の状態(保存先は S3 などの外部)
チェックポイントと中断からの再開
  • チェックポイントを定期的に保存する:モデルの重みだけでなく、オプティマイザーの状態・学習率のスケジュール・何ステップ目か・乱数の状態も保存します。保存先は S3 などインスタンスの外にします。
  • 保存の間隔は、「止められたときに失ってもよい時間」と、保存にかかる時間で決めます(例:30分ごと)。
  • 中断の通知を受けたら、すぐチェックポイントを保存する処理を入れる。
  • 起動したら、最新のチェックポイントがあれば自動でそこから再開するようにしておく。

止められると困る本番の推論や、締め切りの迫った学習には、オンデマンドや予約(長期の利用を約束して割引を受ける)を使います。

クラウドの費用の管理全般は SRE の「FinOps」で

面接ではこう答える

GPU は高価なので、使っている時間を減らす、安い買い方をする、使っている間は使い切る、の3つで費用を抑えます。学習にはスポットインスタンスが安く使えますが、短い予告で止められるので、重みだけでなくオプティマイザーの状態や何ステップ目かも含めたチェックポイントを外部のストレージに定期的に保存し、起動したら自動で続きから再開できるようにします。止められると困る本番の推論には、オンデマンドや予約を使います。

使用率のグラフで「待っている時間」と「放置されている時間」を見つける使用率山と谷:データの読み込み待ち0% のまま:使い終わったのに付けっぱなし
GPU の使用率の見え方(イメージ)

使用率の監視:nvidia-smi や DCGM のエクスポーターで、GPU の使用率とメモリの使用量を記録し、ダッシュボードで見えるようにします。

  • 使用率がずっと低い:データの読み込みが遅い、バッチが小さすぎる、GPU が大きすぎる。
  • 何もしていない GPU:使い終わったノートブックや開発用のインスタンスが付けっぱなし。一定時間使われなければ自動で止める仕組みを入れます。
  • チームやプロジェクトごとにタグを付け、誰がいくら使っているかを見えるようにします。
見えた状態打ち手
使用率が上下にのこぎり状DataLoader の並列数・先読みを増やす。前処理を事前に済ませる
使用率は高いがメモリに余裕バッチサイズを上げる。小さい GPU に替える
長時間 0%自動停止。ノートブックの放置を検知して通知
誰が使っているか分からないチーム・プロジェクトのタグ。費用を週次で共有

この章のまとめ

🖨 まとめシート(印刷・PDF)
  • GPU と分散学習(データ並列・モデル並列)
  • 学習データの読み込みとコスト

この章のクイズ

面接ではこう聞かれる

まず答えを見ずに声に出して答え、そのあと開いて比べる。

Q1GPU を増やしても学習が速くならない原因は?

模範回答

主に3つを確認します。1つ目は通信です。データ並列では毎ステップ勾配をそろえる通信があり、GPU 間やサーバー間の帯域が足りないと、計算より通信の待ちが長くなります。サーバーをまたいでいるなら、高速なネットワークがあるか、勾配を貯めてから通信して回数を減らせるかを見ます。

2つ目はデータの読み込みで、GPU の使用率が低いなら、読み込みや前処理が追いついていません。並列の数を増やす、先読みする、小さなファイルをまとめる、ローカルにキャッシュする、で改善します。3つ目はバッチサイズで、台数を増やしても1台あたりのバッチが小さいと、計算に比べて通信の割合が増えます。全体のバッチを大きくしたら学習率も調整し、精度が落ちていないかも確かめます。プロファイラーで、時間が計算・通信・読み込みのどこにかかっているかを測ってから手を打ちます。

深掘りされたら

  • GPU のメモリが足りないときは、どうしますか?

見られているポイント通信・データ読み込み・バッチサイズ

Q2混合精度とは何ですか?使うときの注意は?

模範回答

計算の多くを 32 ビットではなく 16 ビットの数(FP16 や BF16)で行う方法です。メモリがおよそ半分になり、対応した GPU では計算も速くなるので、大きなモデルやバッチを扱いやすくなります。

注意点は、FP16 は表せる範囲が狭く、小さな勾配が 0 になったり、大きな値があふれたりすることです。そのため損失を拡大して計算する loss scaling と組み合わせます。BF16 は範囲が 32 ビットと同じなので、この問題が起きにくく、使える GPU ならまず BF16 を選びます。精度が落ちていないかは、32 ビットでの学習と比べて確かめます。

深掘りされたら

  • 勾配チェックポイント(activation checkpointing)とは何ですか?

見られているポイントメモリと速さの利点・FP16 と BF16 の違い

Q3学習のジョブがスポットインスタンスで何度も止まります。どうしますか?

模範回答

止まる前提で、続きから再開できる作りにします。チェックポイントには重みだけでなく、オプティマイザーの状態・何ステップ目か・学習率のスケジュール・乱数の状態も保存し、保存先はインスタンスの外(S3 など)にします。起動したら最新のチェックポイントを自動で探して再開するようにし、中断の通知を受けたらすぐ保存する処理も入れます。

それでも頻繁に止まるなら、別のインスタンスの種類やリージョンも候補にする、締め切りが近い学習だけオンデマンドにする、などで調整します。保存の間隔は、失ってもよい時間と保存にかかる時間のバランスで決めます。

深掘りされたら

  • チェックポイントの保存自体が遅いときは、どうしますか?

見られているポイントチェックポイントの中身と保存先・自動再開・インスタンスの選び方