PyTorch GPU / CUDA 高速化

ディープラーニングの中核となる操作は、大規模な行列乗算と要素演算です。CPU の設計目標は複雑なシリアルロジックを処理することで、コア数は通常 8〜64 個です。一方 GPU は数千の単純な並列コアを備えており、このような高度に並列な数値計算に自然に適しています。PyTorch は NVIDIA のCUDA(Compute Unified Device Architecture)フレームワークを通じて GPU を呼び出し、トレーニング速度を数十倍から百倍にまで向上させることができます。


1. CPU と GPU の違い

GPU トレーニング速度の向上は主に 2 つの要因に由来します。1 つは大量の同一計算を並列実行すること、もう 1 つは高帯域幅の VRAM によりデータ転送速度が CPU メモリよりもはるかに高速になることです。

行列乗算のような演算集約型の操作では、高速化の効果が特に顕著です。

比較項目 CPU GPU(NVIDIA)
コア数 8〜64 個の大コア 数千個の小コア(CUDA Cores)
設計目標 低遅延シリアル処理 高スループット並列計算
メモリ帯域幅 ~50~100 GB/s ~500~3000 GB/s
行列乗算速度 ベンチマーク 10x〜100x 高速
PyTorch インターフェース "cpu" "cuda"

Apple Silicon(M1/M2/M3)を使用する場合、PyTorch はmpsバックエンドを通じて Metal GPU 高速化をサポートします。使用方法は CUDA とほぼ完全に同じで、デバイスを変更するだけです。"mps"。


2. CUDA 環境の確認

GPU を使用する前に、現在の環境に CUDA 対応の PyTorch がインストールされているかどうかを確認する必要があります。

また、システム上に利用可能な GPU デバイスが存在するかどうかを確認する必要があります。

例

import torch

# CUDA 対応かどうか
print("CUDA 使用可能:", torch.cuda.is_available())

# GPU デバイスの数
print("GPU の数:", torch.cuda.device_count())

# 現在のデフォルト GPU のインデックス
print("現在の GPU:", torch.cuda.current_device())

# GPU モデル名
print("GPU モデル:", torch.cuda.get_device_name(0))

# PyTorch のバージョンと、コンパイル時にバンドルされた CUDA バージョン
print("PyTorch バージョン:", torch.__version__)
print("CUDA バージョン:", torch.version.cuda)

出力例:

CUDA 可用: True
GPU 数量: 1
当前 GPU: 0
GPU 型号: NVIDIA GeForce RTX 4090
PyTorch 版本: 2.3.0+cu121
CUDA 版本: 12.1

2.1 デバイスの動的選択(推奨コード)

コード内にハードコードすると、"cuda"GPU のないマシンでは直接エラーが発生します。

例

import torch

# 方法1: 古典的な書き方で、互換性が最も高い
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 方法2: PyTorch 2.0+ 推奨、CUDA / MPS / CPU を自動サポート
device = (
    "cuda" if torch.cuda.is_available()
    else "mps" if torch.backends.mps.is_available()
    else "cpu"
)

print(f"使用デバイス: {device}")

3. テンソルのデバイス間移動

PyTorch のテンソルはデフォルトで CPU 上に作成されます。

GPU 計算を使用するには、テンソルを明示的に GPU へ移動するか、GPU 上で直接作成する必要があります。

3.1 基本的な移動方法

例

import torch

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# CPU テンソルの作成
cpu_tensor = torch.tensor([1.0, 2.0, 3.0])
print(cpu_tensor.device)   # cpu

# 方法1: .to(device) —— 推奨、汎用性が最も高い
gpu_tensor = cpu_tensor.to(device)

# 方法2: .cuda() —— CUDA 環境のみ
gpu_tensor = cpu_tensor.cuda()

# 方法3: 作成時にデバイスを直接指定
gpu_tensor = torch.tensor([1.0, 2.0, 3.0], device=device)
gpu_tensor = torch.randn(3, 4, device=device)

# CPU に戻す(表示、numpy 変換、保存などの操作に使用)
back_to_cpu = gpu_tensor.cpu()

print(gpu_tensor.device)    # cuda:0
print(back_to_cpu.device)   # cpu

GPU テンソルを numpy に変換する場合、先に CPU に戻す必要があります。また、テンソルに勾配が付いている場合は、最初に detach する必要があります。

例

# 通常の GPU テンソルを numpy に変換
arr = gpu_tensor.cpu().numpy()

# 勾配付き GPU テンソルを numpy に変換
arr = gpu_tensor.detach().cpu().numpy()

3.2 デバイス一貫性の制約

異なるデバイス上のテンソルは、同じ演算に直接参加できません。

そうしないとエラーがスローされます。RuntimeError:

例

a = torch.randn(3).to("cuda")
b = torch.randn(3)           # CPU 上

# c = a + b    # RuntimeError: Expected all tensors to be on the same device

# 正しい方法: まずデバイスを統一する
c = a + b.to("cuda")

テンソルが存在するデバイスを確認する:

例

x = torch.randn(3, 4).to(device)

print(x.device)           # cuda:0
print(x.is_cuda)          # True
print(x.get_device())     # 0(GPU インデックス)

3.3 速度比較の検証

例

import torch
import time

device = torch.device("cuda")
n = 5000

# CPU 行列乗算
a_cpu = torch.randn(n, n)
b_cpu = torch.randn(n, n)
start = time.time()
c_cpu = torch.matmul(a_cpu, b_cpu)
print(f"CPU 所要時間: {time.time() - start:.3f}s")

# GPU 行列乗算
a_gpu = a_cpu.to(device)
b_gpu = b_cpu.to(device)
torch.cuda.synchronize()    # データ転送が完了してから計測を開始
start = time.time()
c_gpu = torch.matmul(a_gpu, b_gpu)
torch.cuda.synchronize()    # GPU の実行完了を待ってから計測を停止
print(f"GPU 所要時間: {time.time() - start:.3f}s")

出力例:

CPU 耗时: 1.847s
GPU 耗时: 0.021s

GPU 計算は非同期実行であり、Python の呼び出しが戻った後も、GPU 上の演算が完了していない可能性があります。計測時には必ず呼び出して、torch.cuda.synchronize()GPU の実際の終了を待つ必要があります。そうしないと、測定結果が不正確になります。


4. モデルの GPU への移動

モデルのすべてのパラメータ(weight、bias)も本質的にはテンソルです。

これらのパラメータも GPU 上で順伝播と逆伝播を実行するには、GPU へ移動する必要があります。

モデル全体に対して呼び出す.to(device)だけで、PyTorch が自動的にすべての内部パラメータを走査して移動します。

例

import torch
import torch.nn as nn

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(784, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Linear(128, 10),
        )

    def forward(self, x):
        return self.net(x)

# モデルを GPU へ移動するには、1 回呼び出すだけでよい
model = SimpleNet().to(device)

# パラメータがすべて GPU 上にあるか確認
for name, param in model.named_parameters():
    print(f"{name}: {param.device}")
# net.0.weight: cuda:0
# net.0.bias:   cuda:0
# ...

# 入力データも同じデバイス上にある必要がある
x = torch.randn(32, 784).to(device)
output = model(x)
print(output.shape)   # torch.Size([32, 10])

モデルが GPU 上にあっても、入力データが CPU 上にある場合、順伝播でエラーが発生します。DataLoader が各バッチを読み込んだ後、必ずinputsとlabelsすべて .to(device) を呼び出してください。.to(device)。


5. 完全なトレーニングフロー

以下は、データ読み込み、モデルトレーニング、検証評価を含む標準的な GPU トレーニングテンプレートです。

例

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

# 1. デバイス設定
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"トレーニングデバイス: {device}")

# 2. データ読み込み
# pin_memory=True: データをメモリに固定し、CPU -> GPU 転送を高速化
# num_workers: マルチプロセスによるプリロードで、データ待機時間を短縮
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,)),
])
train_dataset = datasets.MNIST(root="./data", train=True,
                                download=True, transform=transform)
test_dataset  = datasets.MNIST(root="./data", train=False,
                                download=True, transform=transform)

train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True,
                          num_workers=4, pin_memory=True)
test_loader  = DataLoader(test_dataset,  batch_size=256, shuffle=False,
                          num_workers=4, pin_memory=True)

# 3. モデルを定義
class CNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(64 * 7 * 7, 256), nn.ReLU(), nn.Dropout(0.5),
            nn.Linear(256, 10),
        )

    def forward(self, x):
        return self.classifier(self.features(x))

model     = CNN().to(device)     # モデルを GPU へ移動
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)

# 4. トレーニング関数
def train_epoch(model, loader, optimizer, criterion):
    model.train()
    total_loss, correct = 0.0, 0
    for inputs, labels in loader:
        # non_blocking=True:非同期転送、CPUは次のバッチデータを準備し続けることができる
        inputs = inputs.to(device, non_blocking=True)
        labels = labels.to(device, non_blocking=True)

        optimizer.zero_grad()
        outputs = model(inputs)
        loss    = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        total_loss += loss.item() * inputs.size(0)
        correct    += (outputs.argmax(1) == labels).sum().item()

    n = len(loader.dataset)
    return total_loss / n, correct / n

# 5. 検証関数
def eval_epoch(model, loader, criterion):
    model.eval()
    total_loss, correct = 0.0, 0
    with torch.no_grad():
        for inputs, labels in loader:
            inputs = inputs.to(device, non_blocking=True)
            labels = labels.to(device, non_blocking=True)
            outputs = model(inputs)
            loss    = criterion(outputs, labels)
            total_loss += loss.item() * inputs.size(0)
            correct    += (outputs.argmax(1) == labels).sum().item()
    n = len(loader.dataset)
    return total_loss / n, correct / n

# 6. メインのトレーニングループ
for epoch in range(1, 11):
    train_loss, train_acc = train_epoch(model, train_loader, optimizer, criterion)
    val_loss,   val_acc   = eval_epoch(model,  test_loader,  criterion)
    print(f"Epoch {epoch:02d} | "
          f"Train Loss: {train_loss:.4f}, Acc: {train_acc:.4f} | "
          f"Val Loss: {val_loss:.4f}, Acc: {val_acc:.4f}")

重要なポイント:

  • device変数はコードの最上部で宣言し、グローバルに統一参照する。ハードコーディングしない。"cuda"
  • モデル.to(device)一度だけ呼び出す
  • 各バッチのinputsとlabelsすべて.to(device)
  • DataLoaderの設定pin_memory=Trueとnon_blocking=Trueデータ転送の高速化
  • 検証段階では使用torch.no_grad()勾配をオフにし、GPUメモリを節約

6. マルチ GPU トレーニング

単一GPUのメモリが不足する場合、複数GPUでの並列トレーニングを検討できます。

さらに、複数GPUはトレーニング速度をさらに向上させることができます。PyTorchには主に2つの方法があります。

6.1 DataParallel

これは最も簡単な複数GPU方式です。

シングルプロセスで、各バッチを各GPUに均等に分割し、順伝播を並列実行し、勾配をメインGPUで集約して更新します。使いやすいですが、メインGPUの負担が重く、複数GPUの利用率が不均衡で、クイックスタートに適しています。

例

import torch
import torch.nn as nn

model = CNN()

if torch.cuda.device_count() > 1:
    print(f"使用 {torch.cuda.device_count()} 個のGPU")
    model = nn.DataParallel(model)

    # 使用するGPUを指定することもできます
    # model = nn.DataParallel(model, device_ids=[0, 1])

model = model.to("cuda")

# 以降のトレーニングコードは単一GPUと完全に同じです
# DataParallel はバッチを各GPUに自動で均等分割し、結果を集約します

元のモデルの属性(カスタムメソッドなど)にアクセスする必要がある場合は、以下を使用する必要があります.moduleアクセス:

例

# model が DataParallel でラップされた後、元のモデルは model.module 内にあります
print(model.module.classifier)

# モデル保存時は model.module を保存することをお勧めします。単一GPUでの読み込みが容易になります
torch.save(model.module.state_dict(), "model.pth")

6.2 DistributedDataParallel

これはマルチプロセス方式です。

各プロセスが1つのGPUにバインドされ、各GPUが完全なモデルを保持し、All-Reduceで勾配を同期します。本番環境や大規模トレーニングに推奨される方法で、DataParallelよりもはるかに効率的です。

例

import torch
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
from torch.utils.data.distributed import DistributedSampler

def main(rank, world_size):
    # プロセスグループを初期化。nccl バックエンドは NVIDIA GPU 向けに最適化されています
    dist.init_process_group(backend="nccl", rank=rank, world_size=world_size)

    # 各プロセスを対応するGPUにバインド
    torch.cuda.set_device(rank)
    device = torch.device(f"cuda:{rank}")

    # モデルを対応するGPUに移動後、DDPでラップ
    model = CNN().to(device)
    model = DDP(model, device_ids=[rank])

    # DataLoader で DistributedSampler を使用し、各GPUのデータが重複しないようにします
    sampler = DistributedSampler(train_dataset,
                                  num_replicas=world_size, rank=rank)
    loader  = DataLoader(train_dataset, batch_size=64,
                         sampler=sampler, pin_memory=True)

    # トレーニングロジックは単一GPUと完全に同じ
    # ...

    dist.destroy_process_group()

# 起動方法(torchrun を推奨):
# torchrun --nproc_per_node=4 train_ddp.py
比較項目 DataParallel DistributedDataParallel
プロセス数 シングルプロセス マルチプロセス(GPUごとに1つ)
通信バックエンド Python GIL 制限 NCCL(高効率)
メインGPUの負担 重い(勾配の集約) 均衡(All-Reduce)
コード変更 極めて少ない 中程度
適用シーン 高速実験 本番トレーニング

7. 混合精度トレーニング AMP

デフォルトのトレーニングは float32(FP32)精度を使用します。

自動混合精度(Automatic Mixed Precision、AMP)一部の計算で float16(FP16)または bfloat16(BF16)を使用することで、精度をほとんど損なうことなく大きなメリットを得られます:

  • GPUメモリ使用量が約50%削減
  • トレーニング速度が2倍~3倍向上(Tensor Core ハードウェアアクセラレーション)
  • コード変更は極めて少なく、3箇所の修正のみ

例

from torch.cuda.amp import autocast, GradScaler

model     = CNN().to(device)
optimizer = optim.Adam(model.parameters(), lr=1e-3)
scaler    = GradScaler()    # FP16 勾配スケーラー。勾配のアンダーフローによるゼロ化を防ぎます

for epoch in range(num_epochs):
    model.train()
    for inputs, labels in train_loader:
        inputs = inputs.to(device, non_blocking=True)
        labels = labels.to(device, non_blocking=True)

        optimizer.zero_grad()

        # 変更1:autocast 領域内で FP16/FP32 を自動選択
        with autocast(device_type="cuda"):
            outputs = model(inputs)
            loss    = criterion(outputs, labels)

        # 変更2:scaler で loss をスケーリングしてから逆伝播
        scaler.scale(loss).backward()

        # 変更3:scaler がパラメータを更新。内部で勾配スケーリングを自動処理
        scaler.step(optimizer)
        scaler.update()

7.1 FP16 と BF16 の選択

形式 精度ビット 数値範囲ビット 対応ハードウェア 特徴
float16 10 ビット 5 ビット RTX 20/30/40 シリーズ GradScaler によるオーバーフロー防止が必要
bfloat16 7 ビット 8 ビット A100 / H100 / RTX 4090 FP32 と同じ範囲で、トレーニングがより安定

GPU が BF16 をサポートしている場合、優先的に使用し、GradScaler は不要です:

例

# BF16 の書き方(PyTorch 2.0+)
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
    outputs = model(inputs)
    loss    = criterion(outputs, labels)

loss.backward()
optimizer.step()

8. パフォーマンス最適化のテクニック

このセクションでは、GPUメモリとトレーニング速度を最適化するさまざまなテクニックを紹介します。

8.1 VRAM 管理

例

# 現在のGPUメモリ使用状況を確認
print(f"割り当て済み: {torch.cuda.memory_allocated() / 1024**2:.1f} MB")
print(f"キャッシュ済み: {torch.cuda.memory_reserved() / 1024**2:.1f} MB")

# 詳細なGPUメモリレポートを出力
print(torch.cuda.memory_summary())

# キャッシュプールを解放(割り当て済みのGPUメモリは解放しない)
torch.cuda.empty_cache()

# 推論時は inference_mode を使用(no_grad より速く、勾配エンジンを完全に無効化)
with torch.inference_mode():
    output = model(x)

# 勾配チェックポイント:再計算と引き換えにGPUメモリを節約(大規模モデルのトレーニングでよく使用)
from torch.utils.checkpoint import checkpoint
output = checkpoint(model_block, x)

8.2 DataLoader の最適化

例

loader = DataLoader(
    dataset,
    batch_size=256,
    num_workers=4,              # マルチプロセス事前読み込み。CPUコア数の50%に設定することを推奨
    pin_memory=True,            # ページロックメモリ。CPU -> GPU 転送を高速化
    persistent_workers=True,    # プロセスを常駐させ、各エポックでプロセスを再作成するのを防ぐ
    prefetch_factor=2,          # 各ワーカーが事前取得するバッチ数
)

8.3 torch.compile によるモデルコンパイル(PyTorch 2.0+)

1行のコードで計算グラフをコンパイル最適化し、速度が30%〜200%向上します(モデル構造によります):

例

# モデルをコンパイル。初回実行時はコンパイルオーバーヘッドがありますが、以降のイテレーション速度は大幅に向上します
model = torch.compile(model)

# 異なるコンパイルモードのトレードオフ
model = torch.compile(model, mode="default")          # バランスが取れており、ほとんどのシーンに適しています
model = torch.compile(model, mode="reduce-overhead")  # スケジューリングオーバーヘッドを低減
model = torch.compile(model, mode="max-autotune")     # 最大最適化。コンパイル時間が長い

8.4 勾配累積(大きなバッチをシミュレート)

GPUメモリが不足する場合、勾配累積により実際のGPUメモリ使用量を増やさずに、より大きなバッチサイズをシミュレートできます:

例

accumulation_steps = 8    # 8バッチごとにパラメータを更新。batch_size × 8 と同等

for i, (inputs, labels) in enumerate(train_loader):
    inputs = inputs.to(device)
    labels = labels.to(device)

    outputs = model(inputs)
    loss    = criterion(outputs, labels) / accumulation_steps   # loss を均等に分割
    loss.backward()    # 勾配累積。ゼロにしない

    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()    # パラメータ更新後にのみゼロにする

8.5 VRAM 不足時の対処戦略

戦略 方法 効果
バッチサイズを小さくする 256 -> 64 GPUメモリが線形に減少
混合精度 AMP + FP16/BF16 GPUメモリが約50%削減
勾配累積 Nステップごとに更新 大きいバッチをシミュレートし、GPUメモリを増やさない
勾配チェックポイント torch.utils.checkpoint GPUメモリを大幅に削減するが、トレーニング速度は低下
一部のパラメータを凍結 転移学習でバックボーンを凍結 逆伝播のGPUメモリ使用量を削減

9. よくあるエラーとトラブルシューティング

このセクションでは、GPUトレーニングで最もよくあるエラーとその解決策をまとめています。

9.1 RuntimeError: Expected all tensors to be on the same device

原因:演算に参加するテンソルが異なるデバイスに配置されている(一方がCPU、もう一方がGPU)。

例

# 調査方法:各テンソルの device を出力
print(inputs.device, labels.device, next(model.parameters()).device)

# 解決策:各バッチで .to(device) を実行していることを確認
inputs = inputs.to(device)
labels = labels.to(device)

9.2 RuntimeError: CUDA out of memory

原因:GPUメモリ不足。バッチサイズが大きすぎる、モデルが大きすぎる、計算グラフが意図せず蓄積される場合によく発生します。

例

# よくある落とし穴:ループ内で loss に .item() を呼び出していないため、計算グラフが蓄積され続ける
# 誤った書き方
total_loss += loss           # loss はテンソルであり、計算グラフ全体を保持する

# 正しい書き方
total_loss += loss.item()    # .item() で Python スカラーを取り出し、計算グラフを解放

# その他の調査手順:
# 1. batch_size を小さくする
# 2. 検証ループで torch.no_grad() を使用していることを確認
# 3. torch.cuda.empty_cache() を呼び出してキャッシュをクリアする
# 4. torch.cuda.memory_summary() を使用してVRAM使用量の原因を特定する

9.3 Can't call numpy() on Tensor that requires grad

原因:勾配を持つテンソルは直接numpy配列に変換できません。

例

# 誤った書き方
arr = gpu_tensor.numpy()

# 正しい書き方
arr = gpu_tensor.detach().cpu().numpy()

9.4 CUDA error: device-side assert triggered

原因:通常はラベル値が範囲外(例:10クラス分類なのにlabel値が10)、または配列インデックスが範囲外です。エラー情報はGPU上で発生するため、デフォルトの表示位置は正確ではありません。

例

# 環境変数を設定して、エラーを正確なコード行で発生させる(同期実行になるため速度が低下します)
import os
os.environ["CUDA_LAUNCH_BLOCKING"] = "1"

9.5 トレーニング速度が向上しない(GPU 使用率が低い)

原因:データ読み込みがボトルネックとなり、GPUはほとんどの時間CPUのデータ準備を待っています。

調査手順:

  1. 実行nvidia-smi 或 watch -n 1 nvidia-smiGPU使用率を確認
    • 使用率が継続的に80%未満の場合はデータのボトルネックが存在します
  2. DataLoaderのnum_workersを増やす
  3. pin_memory=True を有効にする
  4. データ前処理をGPUに移す(torchvision.transforms はGPU操作をサポート)
  5. 小さいファイルのデータセットをメモリに事前に読み込む

9.6 PyTorch Profiler を使用してボトルネックを正確に特定する

例

from torch.profiler import profile, ProfilerActivity

with profile(
    activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
    record_shapes=True,
) as prof:
    for i, (inputs, labels) in enumerate(train_loader):
        if i >= 10:
            break
        inputs = inputs.to(device)
        labels = labels.to(device)
        loss   = criterion(model(inputs), labels)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

# CUDAの実行時間順にソートし、上位15件を出力
print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=15))

10. API クイックリファレンス

以下は、よく使われるPyTorch GPU関連APIの早見表です。

10.1 デバイス管理

操作 コード
CUDAの利用可能性を確認 torch.cuda.is_available()
デバイスを選択 torch.device("cuda" if ... else "cpu")
GPUの数 torch.cuda.device_count()
GPU名 torch.cuda.get_device_name(0)
現在のGPUを設定 torch.cuda.set_device(0)
GPUの完了を待機 torch.cuda.synchronize()

10.2 テンソル操作

操作 コード
テンソルをGPUに移動 tensor.to(device) 或 tensor.cuda()
テンソルをCPUに移動 tensor.cpu()
デバイスを確認 tensor.device
GPU上にあるか tensor.is_cuda
GPUテンソルをnumpyに変換 tensor.detach().cpu().numpy()
非同期転送 tensor.to(device, non_blocking=True)

10.3 モデル操作

操作 コード
モデルをGPUに移動 model.to(device)
トレーニングモードに設定 model.train()
推論モードに設定 model.eval()
コンパイルによる高速化 torch.compile(model)
勾配を無効化 with torch.no_grad():
推論モード with torch.inference_mode():

10.4 VRAM 管理

操作 コード
割り当て済みVRAM torch.cuda.memory_allocated()
キャッシュ済みVRAM torch.cuda.memory_reserved()
VRAMレポート torch.cuda.memory_summary()
キャッシュをクリア torch.cuda.empty_cache()

10.5 核となる原則の早見メモ

1. 用 device 变量统一管理,不要硬编码 "cuda"
2. 模型和数据必须在同一设备上,每个 batch 都要 .to(device)
3. 验证和推理时一定使用 torch.no_grad() 或 torch.inference_mode()
4. 生产训练推荐开启 AMP 混合精度,几乎免费获得 2x 加速
5. DataLoader 设置 pin_memory=True 和 num_workers >= 4 减少数据瓶颈
6. PyTorch 2.0+ 可以用 torch.compile(model) 一行提速 30%+
7. GPU 计算是异步的,精确计时需要调用 torch.cuda.synchronize()
その他の拡張