ML 研究向け GPU · KYC 不要の暗号通貨決済
IteraGPU
方法 01 · 推定から実測へ

メモリのピークが推定値を超えるのはなぜですか?

重みの計算式はパラメータの保存量を表し、ピークは入力と一時的な割り当てを含む実行時のメモリを表します。この差を説明するには、単位を揃え、各フェーズを測定し、割り当て済みメモリ、予約済みメモリ、カードの使用量を区別してください。IteraGPU Lab v1の資料では、再現可能な計算と、計測を伴う小さな演習を提供しています。以下に示す数値はあくまで計算例であり、公表されたGPU実測値ではありません。

01 /

カウンターを選ぶ前に負荷を定義する

「7Bモデル」というだけでは、重みの表現も実行する作業も特定できません。リビジョン、フレームワーク、拡張機能のバージョン、実際に読み込まれる形式、そして操作(学習、適応、生成のいずれか)を確認してください。テキストの場合は入力長と出力長を、ビジョンの場合は解像度と画像枚数を記録します。マルチモーダルモデルでは、これら両方の次元を保持する必要があります。

通常の入力、長いが想定内の入力、そして実用上の上限に近い入力を用意してください。比較の間はこれらを保持します。トークン化、パディング、グループ化、リサイズ後の形状を確認してください。設定に記載された値は、実際に処理された形状を証明するものではありません。

同時にメモリ上に残るものも定めましょう。1つのシーケンス、1つのマイクロバッチ、複数のリクエスト、あるいは学習後に起動する評価です。これで疑問は検証可能になります。この完全な負荷は、最も要求の厳しい段階を含め、使用する各デバイスに収まるでしょうか?

  • 試行の識別情報: モデルまたはコード、リビジョン、入力セット、必要に応じてシード。
  • 次元: バッチ、コンテキスト、生成トークン数、解像度、同時リクエスト数。
  • 環境:選択したGPU、ドライバー、Python、PyTorch、CUDAまたはHIP/ROCmバックエンド、アロケータ設定。
  • 範囲:読み込み、計算、転送、評価、エクスポート。初回パスまたはウォームアップ後のパス。
02 /

GBとGiBを混同せずに重みを計算する

1GBは1,000,000,000バイト、1GiBは1,073,741,824バイトです。ここで使用するPyTorchのカウンターはバイトを返します。結果ファイルにはこの生の値を保存し、行を比較する際に一度だけ変換を適用してください。カードの販売上の表記は、デバイスが実際に申告する容量に代わるものではありません。

それぞれ2バイトで保存される70億パラメータの密なモデルの場合、重みは14 000 000 000バイト、すなわち14 GB、およそ13.04 GiBになります。この計算には活性化、勾配、KVキャッシュ、オプティマイザの状態は含まれません。4 GiBの余裕を加えると、準備の仮定としておよそ17.04 GiBになりますが、これはこの範囲内に負荷が収まることを証明するものではありません。

4ビットへの理論上の除算は、均一でコンパクトなストレージを前提としています。実際の量子化読み込みでは、スケールやその他の情報が追加されたり、一部のモジュールが別の精度で保持されたりする可能性があります。したがって、重みの保存形式と計算形式は、あなたの記録シートに別々に記載する必要があります。

推定重み(GiB) = パラメータ数 × パラメータあたりのビット数 ÷ 8 ÷ 1,073,741,824
7,000,000,000パラメータの例示的な計算。実行結果ではありません。
保存の前提計算されたバイト数おおよそのGiB
32ビット均一28 000 000 00026,08
16ビット均一14 000 000 00013,04
4ビットコンパクト、メタデータを除く3 500 000 0003,26

技術的な出典: NIST — 2進接頭辞と GB/GiB の比較 · Hugging Face — bitsandbytes による量子化形式とモジュール

03 /

学習では、1ステップ全体を測定する

重みは他のオブジェクトと共存します。勾配、オプティマイザ状態、逆伝播に必要な活性化、一時テンソルなどです。これらのサイズは、ループ、精度、ワークロードの次元によって変わります。パラメータあたりのバイト数という普遍的な定数では、とりわけマイクロバッチや入力の影響が見えなくなってしまいます。

順伝播、損失計算、逆伝播、更新の各段階を計測しましょう。オプティマイザが実際に生成する状態を観察するには、モデルの読み込みだけで止めてはいけません。最初の完全なステップの測定値も保持しておきましょう。ウォームアップが成功した場合、起動時にメモリ上で確保できる必要がある初期化がすでに実行されている可能性があります。

プロジェクトに必要な評価とエクスポートも追加しましょう。評価中に失敗が起きる場合、学習バッチを減らすだけではこのフェーズは解決しません。わずかなパラメータしか学習しない適応手法でも、ベースモデルや大きな活性化を保持していることがあります。

技術的な出典: Hugging Face — 学習中のメモリの区分

04 /

推論では、コンテキストと同時実行数を追う

アテンションを用いた自己回帰生成では、KVキャッシュがトークンに関連する状態を保持します。均一で密なキャッシュの場合、そのサイズはレイヤー数、KVヘッド数、その次元、保持されるトークン数、同時に存在するシーケンス数によって決まります。自動的にクエリヘッドではなく、モデルのKVヘッドを使用してください。

算術的な例:32レイヤー、KVヘッド8、次元128、8,192トークン、値あたり2バイト、1シーケンスの場合、1,073,741,824バイト、つまりKとVを合わせて1GiBになります。同一の4シーケンスでは、この項目だけで4GiBになります。この計算は、スループットやGPUの完全な使用状況を測定するものではありません。

実際に使用するキャッシュに合わせて式を調整しましょう。スライディングウィンドウは必ずしも全履歴を保持しません。静的キャッシュは最大容量を事前確保することがあります。量子化キャッシュやオフロードキャッシュも問題を変えます。入力の初期処理と生成を別々に測定し、その差を自動的にすべてキャッシュのせいにしないようにしましょう。

密なKVのバイト数 ≈ 2 × レイヤー数 × KVヘッド数 × ヘッド次元 × 保持トークン数 × シーケンス数 × 値あたりのバイト数

技術的な出典: Hugging Face — キャッシュ戦略、静的確保、ウィンドウ

05 /

Allocated と reserved:足し合わせられない2つの読み値

memory_allocatedは、PyTorchがデバイス上で追跡しているテンソルが占有するバイト数を示します。memory_reservedは、キャッシュ付きのアロケータが管理するメモリを示し、その一部はすでにこれらのテンソルに使われています。両者を足し合わせると、メモリの一部を二重に数えることになります。別々の2つの列で保持してください。

各maxバリアントは、追跡開始時点または最後のリセット以降のピークをそれぞれ記録します。これらは期間内の絶対ピークであり、開始時点にすでに存在していた割り当てを含みます。この結果は、そのフェーズで新規に作成されたオブジェクトだけを自動的に表すものではありません。

システム側の読み取りは、より広い範囲を対象とする場合があります。CUDAライブラリが直接行う割り当て、たとえば一部のNCCL通信は、PyTorchのアロケータではすべては見えません。したがって、システムツールとの差分があるからといって、それだけで漏れがあると断定はできません。

同じデバイスについて記録すべき、すべてバイト単位の4つのカウンタ。
カウンターそれが答える問い避けるべき誤り
memory_allocatedこの読み取り時点でテンソルはどれだけを占めているか?それをカード全体の占有量とみなす。
memory_reservedこの読み取り時点でアロケータはどれだけを管理しているか?それをallocatedに足す。
max_memory_allocated期間中に追跡されたテンソルのピークはどれか?フェーズ終了時の値と混同する。
max_memory_reservedアロケータが報告する予約のピークはどれか?もう一方のピークと同じ瞬間に発生すると仮定する。

技術的な出典: PyTorch — memory_allocated · PyTorch — memory_reserved · PyTorch — max_memory_allocated · PyTorch — アロケータ外の割り当て

06 /

2つのピークの差がキャッシュを測らない理由

表の2つの架空の時点だけを考えます。allocatedのピークは8 GiB、reservedのピークは12 GiBです。その差である4 GiBは、これら2つの時点のいずれにおいても観測される差ではありません。それぞれ2 GiBと6 GiBになります。2つの最大値は必ずしも同じ状態を表すとは限りません。

ある時点での両者の差を調べるには、同期後、読み取りの間に意図的な新規操作を挟まずに、同じチェックポイントでallocatedとreservedを取得します。得られるのはその瞬間のカウンターの差であり、モデルのKVキャッシュの測定値でも、その差のすべてが次の割り当てを満たせるという保証でもありません。

アロケータのバックエンドもレポートに残してください。PyTorch 2.14のドキュメントには、cudaMallocAsync では max_memory_reserved が2つのプールの最高水準を組み合わせ、同時ピークの上限を与える可能性があると明記されています。これはカウンタの名前と範囲を保持する必要性を強めます。

計算を説明するための2つの架空の状態。この表はGPUトレースではありません。
例示的な時点AllocatedReservedReserved − allocated(その時点)
A8 GiB10 GiB2 GiB
B6 GiB12 GiB6 GiB

技術的な出典: PyTorch — max_memory_reservedの定義と限界

07 /

ピークを取得する前に各フェーズを区切る

GPUの操作は、完了する前にキューに入ることがあります。フェーズ単位で測定するには、ピークをリセットする前に先行する処理を終わらせ、フェーズの終了を待ってから読み取ります。torch.cuda.synchronizeは選択したデバイスのすべてのストリームのカーネルを待機します。この選択が、このプロトコルにおける明示的な境界を定めます。

reset_peak_memory_statsはピークの追跡を現在の状態からリセットしますが、プログラムのテンソルは解放しません。まず開始時の水準を取得します。最後に、2つの絶対ピークと2つの現在の水準を保持します。開始水準の減算を、すべての一時テンソルの正確な量として提示しないでください。以前のオブジェクトがフェーズ中に解放された可能性もあります。

この計装は、通常のフェーズのオーバーラップを変える可能性があります。問題の特定に使ったうえで、実際のスケジューリングによるループ全体も検証してください。マルチカードでは、各デバイスについて読み取りを繰り返します。cuda:0での測定は、他のGPUを表しません。

  • 1. フェーズに名前を付け、その正確な入力を記録する。
  • 2. デバイスを同期し、開始時のallocatedとreservedを取得する。
  • 3. 同じデバイスでreset_peak_memory_statsを呼び出す。
  • 4. 定義したフェーズを、後続に必要な出力を保持しながら実行する。
  • 5. 同期し、ピークと終了時の水準を取得して、成功またはエラーを記録する。
  • 6. 生の結果、次元、構成を保持する。欠測した測定値をゼロで埋めない。

技術的な出典: PyTorch — デバイスの同期 · PyTorch — ピーク統計のリセット

08 /

初回パスとウォームアップ後のパスを区別する

初回の試行と、あらかじめ用意されたループでは、答える問いが異なります。ロードと初回パスの記録を残し、さらに本番の繰り返しの前に何回のウォームアップ反復を行ったかを記録してください。後続のパスがより軽かったはずという理由で、初期化の失敗を削除してはいけません。

IteraGPU のスクリプトは model_load、inputs、cold_forward、warmup、warm_forward を区別します。cold_forward は、デバイスの初期化後における小規模モデルの初回パスです。サーバー、ドライバー、サービスの起動全体を測定するものではありません。warm_forward の繰り返しは同じプロセス内に留まり、その既存の状態を活用します。

モデルについて、以前のオブジェクトや予約を残す可能性のある条件を変更する場合は、新しいプロセスで新しいシリーズを開始してください。試行の順序とウォームアップの方針を記録します。同じループを5回再実行することと、5つのプロセスを起動することは同じプロトコルではありません。

09 /

ノートブックと IteraGPU Lab v1 スクリプトを使う

まず README から始め、その後、自己完結型のノートブックまたは Python スクリプトをダウンロードしてください。estimate の計算は標準ライブラリを使用します。測定には、互換性のある GPU バックエンドを備えた PyTorch がインストールされ、アクセス可能なデバイスがあることが必要です。モデルやパッケージのダウンロードは行いません。ノートブックには ipynb ファイルを読み込める環境が必要です。

測定の演習では、独自の小さな密なネットワークと合成入力を使用します。batch、context、width の各オプションはそのテンソルを記述します。ここでの context は KVキャッシュ付きの実際のLLMの長さではありません。この教材は測定方法を検証し、1つの次元を変化させるためのものです。あなたの研究モデルに対するカードの能力を証明するものではありません。

スクリプトが入っているフォルダから、以下のコマンドを実行してください。まず environment レポートを確認します。PyTorch または GPU が不足している場合、measure はコード 2 で明示的に停止する必要があります。CPU の結果を GPU の測定として解釈してはいけません。測定に成功した場合の出力 JSON は、お使いの環境で生成されます。シリーズごとに新しいファイル名を選んでください。スクリプトは既存の結果の上書きを拒否します。

最初のコマンドは、重みと 4 GiB の仮定上の予備領域の計算をやり直します。以降のコマンドでは、負荷をかける許可があるデバイスを使用し、最初は控えめな次元に留めてください。実際に使用された PyTorch のバージョンを記録してください。このページの技術的参考情報は特にバージョン 2.14 について述べていますが、お使いの環境にそれがインストールされていることを強制するものではありません。

スクリプトの動作は小さなケースで確認されています。カタログ外のローカル RTX 5070、ドライバ610.62、Python 3.14.6、PyTorch 2.11.0+cu128です。試行では batch 1、context 16、width 64、float32、ウォームアップ1回、リピート2回を使用しました。これはこの実行経路を検証するものであり、LLM、学習、またはレンタル提供されるGPUを保証するものではありません。ノートブックは出力なしで、比較表は結果なしで提供されます。PyTorch不在時のガードも、別の環境で検証されています。

shell
python mesure_memoire.py estimate --parameters 7000000000 --bits 16 --reserve-gib 4
python mesure_memoire.py environment --device cuda:0
python mesure_memoire.py measure --device cuda:0 --batch 2 --context 128 --width 1024 --dtype float32 --warmup 3 --repeats 5 --output mesures.json
10 /

カードを変更する前に障害を解釈する

不完全な試行も有用な観察です。フェーズ、要求した次元、エラーメッセージ、利用可能な最後の値を保存してください。飽和前の部分的なピークは、完全な実行のメモリ要件ではありません。1つの次元だけを減らして成功するケースを構築し、その後、成功と失敗の境界を探します。

empty_cache はアロケータのキャッシュから未使用ブロックを解放しますが、まだ生きているテンソルは解放しません。これは負荷が大きすぎる場合の万能な修正ではありません。各反復の間で呼び出すと条件が変わります。この試行をキャッシュを保持する試行と混ぜるのではなく、その選択を文書化してください。

単純なカウンタで状況を説明できない場合は、メモリトレースが時間の経過に伴う割り当ての特定に役立ちます。その範囲は PyTorch から見える割り当てに限られます。したがって、allocated のピークが低くても、外部からの割り当てやカード上の別のユーザーを排除できません。

症状を次の確認につなげる。自動診断はしない。
観察有効な確認次の試行
読み込み中に失敗読み込む形式、重みの配置、すでに占有されているメモリ。新しいプロセスで読み込みだけを再現する。
読み込みは成功したが逆伝播ができないマイクロバッチ、入力、保持される活性化、ループの状態。次元を削減してから、完全なステップをやり直します。
評価だけが失敗する評価バッチ、保持される出力、計算コンテキスト。評価を独自の制限で測定する。
allocated が反復ごとに増加するリスト、アプリケーションのキャッシュ、グラフに保持された参照。アロケータを疑う前に、その生存期間を確認する。
計算後も reserved が高いままになるまだ生きているテンソルとキャッシュポリシー。カウンタを足し合わせずに、現在の値を比較する。
システムツールがより多くを示すツールの範囲、GPU のコンテキスト、他のプロセスとライブラリ。負荷を分離し、同時刻に取得した測定値を突き合わせる。

技術的な出典: PyTorch — empty_cache が解放するもの · PyTorch — メモリ可視性のトレースと限界

11 /

比較可能な負荷からマージンを構築する

万能として提示されるマージンのパーセンテージは避けてください。マージンは特定された変動をカバーする必要があります。より長い入力、許容されるバッチ、評価、エクスポート、ライブラリのバージョン、カードの他の使用状況などです。想定される境界ケースをテストし、範囲外に残るものを記録してください。小さな入力1つでの成功は、最大負荷を検証するものではありません。

一度に1つの変数だけを変えてください。コンテキストを一定にしてバッチ1、次に2、あるいはバッチを一定にしてコンテキスト2 048、次に4 096などです。同じ内容と同じ準備ルールを保ってください。必要な情報を削り取る切り詰めは、ピークを下げてもタスクを別物にします。

重みが支配的な場合は、品質を管理しながら別の形式を検討してください。活性化が支配的な場合は、マイクロバッチや活性化のチェックポイント化が候補になります。後者はメモリを再計算と引き換えにするので、所要時間も測定し、結果を確認してください。KV キャッシュが支配的な場合は、コンテキスト、同時実行数、キャッシュ戦略を検討してください。比較用ドシエは、共通の品質基準を備えたこの手順を補完します。

技術的な出典: PyTorch — 活性化のチェックポイント化と再計算

12 /

トレースから設定の決定へ移行する

期待する成果物は短い一枚のシートです。重みの見積もり、テストした最大負荷、成功または失敗したフェーズ、単位付きの4つのカウンター、環境、そして採用した選択肢を記載します。生の結果をこのシートに添付してください。算出したもの、観察したもの、まだ仮定しているものを分けてください。

次に、ソフトウェアの制約を保ったまま、必要量と各カードの容量を比較してください。複数の GPU は作業とデータの分散を必要とします。それらが存在するだけで、アプリケーションに対して単一のメモリプールが自動的に作られるわけではありません。あるカードでの失敗は、別のカードに空きメモリがあっても続くことがあります。

PyTorchのプロトコルは torch.cuda インターフェースを使用しており、HIP/ROCm ビルドの PyTorch でもこの名前が再利用されます。2つのハードウェアファミリーを比較する前に、実際にインストールされているバックエンドを確認してください。同じ Python 関数を使うことは、カーネル、精度、結果が等価であることを示すものではありません。

サイジングツールで初期の仮定に立ち返ることができます。GPU シートで容量を比較できます。その後、同じ作業ケースに戻って選択を確認してください。ダウンロードに含まれる小さなネットワークは計測の演習にとどまります。文書化された環境でご自身的負荷を実行することだけが、ご自身のマージンを検証できます。

技術的な出典: NVIDIA — 複数 GPU への作業の分散 · PyTorch — HIP/ROCm ビルドにおける torch.cuda インターフェース