Accélération GPU / CUDA PyTorch
Les opérations fondamentales de l'apprentissage profond sont la multiplication matricielle à grande échelle et les opérations élément par élément. Les CPU sont conçus pour traiter une logique série complexe, généralement avec 8 à 64 cœurs ; les GPU, en revanche, possèdent des milliers de cœurs parallèles simples, ce qui les rend naturellement adaptés à ce type de calcul numérique hautement parallèle. PyTorch, grâce auCUDA(Compute Unified Device Architecture) framework, fait appel au GPU, ce qui peut accélérer l'entraînement de dizaines voire de centaines de fois.
1. Différences entre CPU et GPU
L'amélioration de la vitesse d'entraînement sur GPU vient principalement de deux aspects : premièrement, l'exécution parallèle d'un grand nombre de calculs identiques ; deuxièmement, la mémoire GPU à haut débit permet des transferts de données bien plus rapides que la mémoire CPU.
Pour les opérations intensives en calcul telles que la multiplication matricielle, l'effet d'accélération est particulièrement important.
| Élément de comparaison | CPU | GPU(NVIDIA) |
|---|---|---|
| Nombre de cœurs | 8-64 grands cœurs | Des milliers de petits cœurs (CUDA Cores) |
| Objectif de conception | Traitement série à faible latence | Calcul parallèle à haut débit |
| Bande passante mémoire | ~50~100 GB/s | ~500~3000 GB/s |
| Vitesse de multiplication matricielle | Référence | 10x-100x plus rapide |
| Interface PyTorch | "cpu" |
"cuda" |
Si vous utilisez Apple Silicon (M1/M2/M3), PyTorch, via lempsbackend, prend en charge l'accélération GPU Metal. L'utilisation est presque identique à CUDA ; changez simplement le device pour"mps"。
2. Détection de l'environnement CUDA
Avant d'utiliser le GPU, vous devez vérifier si l'environnement actuel dispose d'un PyTorch compatible CUDA installé.
Vérifiez également si des périphériques GPU sont disponibles sur le système.
Exemple
# Si CUDA est pris en charge
print("CUDA disponible :", torch.cuda.is_available())
# Nombre de périphériques GPU
print("Nombre de GPU :", torch.cuda.device_count())
# Index du GPU par défaut actuel
print("GPU actuel :", torch.cuda.current_device())
# Nom du modèle de GPU
print("Modèle de GPU :", torch.cuda.get_device_name(0))
# Version de PyTorch et version de CUDA avec laquelle il a été compilé
print("Version de PyTorch :", torch.__version__)
print("Version de CUDA :", torch.version.cuda)
Exemple de sortie :
CUDA 可用: True GPU 数量: 1 当前 GPU: 0 GPU 型号: NVIDIA GeForce RTX 4090 PyTorch 版本: 2.3.0+cu121 CUDA 版本: 12.1
2.1 Sélection dynamique d'un périphérique (approche recommandée)
Codage en dur dans le code"cuda"Cela provoquera directement une erreur sur les machines sans GPU.
Exemple
# Méthode 1 : Approche classique, meilleure compatibilité
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Méthode 2 : Recommandée pour PyTorch 2.0+, prend automatiquement en charge CUDA / MPS / CPU
device = (
"cuda" if torch.cuda.is_available()
else "mps" if torch.backends.mps.is_available()
else "cpu"
)
print(f"Using device: {device}")
3. Déplacer des tenseurs entre les périphériques
Dans PyTorch, les tenseurs sont créés sur le CPU par défaut.
Pour utiliser le calcul GPU, vous devez explicitement déplacer les tenseurs vers le GPU, ou les créer directement sur le GPU.
3.1 Méthodes de déplacement de base
Exemple
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Créer un tenseur CPU
cpu_tensor = torch.tensor([1.0, 2.0, 3.0])
print(cpu_tensor.device) # cpu
# Méthode 1 : .to(device) — recommandée, la plus polyvalente
gpu_tensor = cpu_tensor.to(device)
# Méthode 2 : .cuda() — environnement CUDA uniquement
gpu_tensor = cpu_tensor.cuda()
# Méthode 3 : Spécifier l'appareil directement lors de la création
gpu_tensor = torch.tensor([1.0, 2.0, 3.0], device=device)
gpu_tensor = torch.randn(3, 4, device=device)
# Revenir au CPU (pour l'affichage, la conversion numpy, la sauvegarde, etc.)
back_to_cpu = gpu_tensor.cpu()
print(gpu_tensor.device) # cuda:0
print(back_to_cpu.device) # cpu
Lors de la conversion d'un tenseur GPU en numpy, vous devez d'abord le ramener sur le CPU, et si le tenseur a des gradients, vous devez également le détacher d'abord :
Exemple
arr = gpu_tensor.cpu().numpy()
# Convertir un tenseur GPU avec gradients en numpy
arr = gpu_tensor.detach().cpu().numpy()
3.2 Contrainte de cohérence des périphériques
Les tenseurs sur différents périphériques ne peuvent pas participer directement à la même opération.
Sinon, cela lèvera une erreurRuntimeError:
Exemple
b = torch.randn(3) # Sur CPU
# c = a + b # RuntimeError: Tous les tenseurs doivent être sur le même périphérique
# Approche correcte : unifier d'abord le périphérique
c = a + b.to("cuda")
Vérifiez l'appareil sur lequel se trouve le tenseur :
Exemple
print(x.device) # cuda:0
print(x.is_cuda) # True
print(x.get_device()) # 0 (indice GPU)
3.3 Vérification de la comparaison de vitesse
Exemple
import time
device = torch.device("cuda")
n = 5000
# Multiplication matricielle 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"Temps CPU : {time.time() - start:.3f}s")
# Multiplication matricielle GPU
a_gpu = a_cpu.to(device)
b_gpu = b_cpu.to(device)
torch.cuda.synchronize() # Assurez-vous que le transfert de données est terminé avant de démarrer le chronomètre
start = time.time()
c_gpu = torch.matmul(a_gpu, b_gpu)
torch.cuda.synchronize() # Attendez que le GPU termine l'exécution avant d'arrêter le chronomètre
print(f"Temps GPU : {time.time() - start:.3f}s")
Exemple de sortie :
CPU 耗时: 1.847s GPU 耗时: 0.021s
Le calcul GPU estexécuté de manière asynchrone—après le retour de l'appel Python, l'opération GPU peut ne pas être terminée. Lors de la mesure du temps, vous devez appeler
torch.cuda.synchronize()pour attendre que le GPU ait réellement terminé ; sinon, les résultats de mesure sont inexacts.
4. Déplacement du modèle vers le GPU
Tous les paramètres du modèle (weight、bias) sont essentiellement des tenseurs.
Ces paramètres doivent également être déplacés vers le GPU afin d'effectuer la propagation avant et la rétropropagation sur le GPU.
Appel.to(device)sur l'ensemble du modèle, et PyTorch parcourra et déplacera automatiquement tous les paramètres internes.
Exemple
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)
# Déplacer le modèle vers le GPU, il suffit de l'appeler une fois
model = SimpleNet().to(device)
# Vérifier que tous les paramètres sont sur le GPU
for name, param in model.named_parameters():
print(f"{name}: {param.device}")
# net.0.weight: cuda:0
# net.0.bias: cuda:0
# ...
# Les données d'entrée doivent également être sur le même périphérique
x = torch.randn(32, 784).to(device)
output = model(x)
print(output.shape) # torch.Size([32, 10])
Lorsque le modèle est sur le GPU mais que les données d'entrée sont encore sur le CPU, la propagation avant déclenchera une erreur. Veillez, après que DataLoader a lu chaque lot,
inputsetlabelsà appeler .to(device) sur chacun d'eux..to(device)。
5. Flux de travail d'entraînement complet
Ce qui suit est un gabarit standard d'entraînement sur GPU qui comprend le chargement des données, l'entraînement du modèle et l'évaluation de validation.
Exemple
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 1. Configuration du périphérique
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Périphérique d'entraînement : {device}")
# 2. Chargement des données
# pin_memory=True : Verrouille les données en mémoire pour accélérer le transfert CPU -> GPU
# num_workers : Préchargement multiprocessus pour réduire le temps d'attente des données
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. Définir le modèle
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) # Déplacer le modèle vers le GPU
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
# 4. Fonction d'entraînement
def train_epoch(model, loader, optimizer, criterion):
model.train()
total_loss, correct = 0.0, 0
for inputs, labels in loader:
# non_blocking=True : transfert asynchrone, le CPU peut continuer à préparer le lot de données suivant
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. Fonction de validation
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. Boucle d'entraînement principale
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"Époque {epoch:02d} | "
f"Perte d'entraînement : {train_loss:.4f}, Précision : {train_acc:.4f} | "
f"Perte de validation : {val_loss:.4f}, Précision : {val_acc:.4f}")
Points clés :
deviceLes variables sont déclarées tout en haut du code, référencées globalement, jamais codées en dur"cuda"- Modèle
.to(device)ne doit être appelé qu'une seule fois - Pour chaque batch,
inputsetlabelsdoit également être.to(device) - Paramètres de DataLoader
pin_memory=Trueetnon_blocking=Trueaccélérer le transfert de données - Utilisation en phase de validation
torch.no_grad()désactiver les gradients pour économiser de la mémoire
6. Entraînement multi-GPU
Lorsque la mémoire d'un seul GPU est insuffisante, envisagez d'utiliser l'entraînement parallèle multi-GPU.
De plus, le multi-GPU peut encore améliorer la vitesse d'entraînement. PyTorch propose deux approches principales.
6.1 DataParallel
C'est l'approche multi-GPU la plus simple.
Il utilise un seul processus, répartit chaque lot uniformément entre les GPU, exécute la propagation avant en parallèle et agrège les gradients sur le GPU principal pour les mises à jour. Il est facile à utiliser, mais le GPU principal est fortement chargé, l'utilisation entre les GPU est inégale, et il convient aux démarrages rapides.
Exemple
import torch.nn as nn
model = CNN()
if torch.cuda.device_count() > 1:
print(f"Using {torch.cuda.device_count()} GPUs")
model = nn.DataParallel(model)
# Vous pouvez également spécifier quels GPU utiliser
# model = nn.DataParallel(model, device_ids=[0, 1])
model = model.to("cuda")
# Le code d'entraînement suivant est exactement le même que pour un seul GPU
# DataParallel répartit automatiquement le batch uniformément entre les GPU et agrège les résultats
Si vous devez accéder aux attributs du modèle d'origine (tels que des méthodes personnalisées), vous devez.moduley accéder via :
Exemple
print(model.module.classifier)
# Lors de l'enregistrement du modèle, il est recommandé d'enregistrer model.module pour faciliter le chargement sur un seul GPU
torch.save(model.module.state_dict(), "model.pth")
6.2 DistributedDataParallel
Il s'agit de l'approche multi-processus.
Chaque processus est lié à un GPU, chaque carte détient une copie complète du modèle, et les gradients sont synchronisés via All-Reduce. C'est la solution recommandée pour les environnements de production et l'entraînement à grande échelle, avec une efficacité bien supérieure à celle de DataParallel.
Exemple
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):
# Initialiser le groupe de processus ; le backend NCCL est optimisé pour les GPU NVIDIA
dist.init_process_group(backend="nccl", rank=rank, world_size=world_size)
# Lier chaque processus à son GPU correspondant
torch.cuda.set_device(rank)
device = torch.device(f"cuda:{rank}")
# Envelopper avec DDP après avoir déplacé le modèle vers le GPU correspondant
model = CNN().to(device)
model = DDP(model, device_ids=[rank])
# Utiliser DistributedSampler dans DataLoader pour s'assurer qu'aucune donnée ne se chevauche entre les GPU
sampler = DistributedSampler(train_dataset,
num_replicas=world_size, rank=rank)
loader = DataLoader(train_dataset, batch_size=64,
sampler=sampler, pin_memory=True)
# La logique d'entraînement est identique à celle d'un seul GPU
# ...
dist.destroy_process_group()
# Comment lancer (recommandé : torchrun) :
# torchrun --nproc_per_node=4 train_ddp.py
| Élément de comparaison | DataParallel | DistributedDataParallel |
|---|---|---|
| Nombre de processus | Processus unique | Multi-processus (un par GPU) |
| Backend de communication | Limitation du GIL de Python | NCCL (efficace) |
| Charge principale du GPU | Lourd (agrégation de gradients) | Équilibré (All-Reduce) |
| Modifications du code | Minimal | Modéré |
| Scénarios applicables | Expérimentation rapide | Entraînement en production |
7. Entraînement en précision mixte AMP
L'entraînement utilise par défaut la précision float32 (FP32).
Précision mixte automatique (AMP)Utilise float16 (FP16) ou bfloat16 (BF16) pour certains calculs, offrant des avantages significatifs avec presque aucune perte de précision :
- Utilisation de la mémoire réduite d'environ 50%
- Vitesse d'entraînement améliorée de 2x–3x (accélération matérielle Tensor Core)
- Très peu de modifications du code nécessaires ; seulement trois modifications
Exemple
model = CNN().to(device)
optimizer = optim.Adam(model.parameters(), lr=1e-3)
scaler = GradScaler() # Scaler de gradient FP16 pour éviter que les gradients ne subissent un underflow vers zéro
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()
# Modification 1 : sélectionner automatiquement FP16/FP32 dans la région autocast
with autocast(device_type="cuda"):
outputs = model(inputs)
loss = criterion(outputs, labels)
# Modification 2 : mettre à l'échelle la perte avec scaler avant la rétropropagation
scaler.scale(loss).backward()
# Modification 3 : scaler met à jour les paramètres et gère automatiquement la mise à l'échelle des gradients en interne
scaler.step(optimizer)
scaler.update()
7.1 Choisir entre FP16 et BF16
| Format | Bits de précision | Bits d'exposant | Matériel compatible | Caractéristiques |
|---|---|---|---|---|
| float16 | 10 bits | 5 bits | Séries RTX 20/30/40 | Nécessite GradScaler pour éviter l'overflow |
| bfloat16 | 7 bits | 8 bits | A100 / H100 / RTX 4090 | Même plage que FP32, entraînement plus stable |
Si votre GPU prend en charge BF16, utilisez-le en priorité ; aucun GradScaler n'est nécessaire :
Exemple
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
8. Conseils d'optimisation des performances
Cette section présente plusieurs techniques pour optimiser l'utilisation de la mémoire et la vitesse d'entraînement.
8.1 Gestion de la mémoire GPU
Exemple
print(f"Alloué : {torch.cuda.memory_allocated() / 1024**2:.1f} MB")
print(f"En cache : {torch.cuda.memory_reserved() / 1024**2:.1f} MB")
# Afficher un rapport détaillé sur la mémoire
print(torch.cuda.memory_summary())
# Vider le pool de cache (ne libère pas la mémoire allouée)
torch.cuda.empty_cache()
# Utiliser inference_mode pendant l'inférence (plus rapide que no_grad, désactive complètement le moteur de gradient)
with torch.inference_mode():
output = model(x)
# Gradient checkpointing : échanger du recalcul contre de la mémoire (couramment utilisé dans l'entraînement de grands modèles)
from torch.utils.checkpoint import checkpoint
output = checkpoint(model_block, x)
8.2 Optimisation de DataLoader
Exemple
dataset,
batch_size=256,
num_workers=4, # Préchargement multiprocessus ; recommandé de le définir à 50% des cœurs CPU
pin_memory=True, # Mémoire épinglée pour accélérer le transfert CPU → GPU
persistent_workers=True, # Garder les processus worker en vie pour éviter de recréer les processus à chaque epoch
prefetch_factor=2, # Nombre de batches préchargés par worker
)
8.3 Compilation de modèle torch.compile (PyTorch 2.0+)
Une ligne de code compile et optimise le graphe de calcul, améliorant la vitesse de 30%–200% (selon l'architecture du modèle) :
Exemple
model = torch.compile(model)
# Compromis entre les différents modes de compilation
model = torch.compile(model, mode="default") # Équilibré, adapté à la plupart des scénarios
model = torch.compile(model, mode="reduce-overhead") # Réduit la surcharge d'ordonnancement
model = torch.compile(model, mode="max-autotune") # Optimisation maximale, temps de compilation plus long
8.4 Accumulation de gradient (Simulation de grands lots)
Lorsque la mémoire est insuffisante, l'accumulation de gradient peut simuler une taille de lot plus grande sans réellement augmenter l'utilisation de la mémoire :
Exemple
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 # Diviser la perte équitablement
loss.backward() # Accumuler les gradients sans les remettre à zéro
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad() # Ne remettre les gradients à zéro qu'après la mise à jour des paramètres
8.5 Stratégies pour une mémoire GPU insuffisante
| Stratégie | Méthode | Effet |
|---|---|---|
| Réduire la taille du lot | 256 -> 64 | Réduit linéairement la mémoire |
| Précision mixte | AMP + FP16/BF16 | Mémoire réduite d'environ 50% |
| Accumulation de gradients | Mettre à jour toutes les N étapes | Simuler un grand lot sans augmenter la mémoire |
| Checkpointing de gradient | torch.utils.checkpoint |
Réduit considérablement la mémoire, mais réduit la vitesse d'entraînement |
| Geler certains paramètres | Geler le backbone en apprentissage par transfert | Réduit l'utilisation de la mémoire de la rétropropagation |
9. Erreurs courantes et dépannage
Cette section résume les erreurs les plus courantes lors de l'entraînement sur GPU et leurs solutions.
9.1 RuntimeError : tous les tenseurs doivent être sur le même appareil
Cause : les tenseurs impliqués dans l'opération sont sur des appareils différents (l'un sur CPU, l'autre sur GPU).
Exemple
print(inputs.device, labels.device, next(model.parameters()).device)
# Solution : assurez-vous que .to(device) est exécuté pour chaque lot
inputs = inputs.to(device)
labels = labels.to(device)
9.2 RuntimeError : mémoire CUDA insuffisante
Cause : mémoire insuffisante, souvent due à une taille de lot trop grande, un modèle trop grand, ou une accumulation accidentelle des graphes de calcul.
Exemple
# Code incorrect
total_loss += loss # loss est un tenseur qui contient tout le graphe de calcul
# Code correct
total_loss += loss.item() # .item() extrait un scalaire Python et libère le graphe de calcul
# Autres étapes de dépannage :
# 1. Réduire batch_size
# 2. S'assurer que la boucle de validation utilise torch.no_grad()
# 3. Appeler torch.cuda.empty_cache() pour vider le cache
# 4. Utiliser torch.cuda.memory_summary() pour localiser la source de l'utilisation de la mémoire GPU
9.3 Impossible d'appeler numpy() sur un Tensor qui nécessite un gradient
Raison : les tenseurs avec gradients ne peuvent pas être convertis directement en tableaux numpy.
Exemple
arr = gpu_tensor.numpy()
# Code correct
arr = gpu_tensor.detach().cpu().numpy()
9.4 Erreur CUDA : assertion côté périphérique déclenchée
Raison : Il s'agit généralement de valeurs d'étiquette hors limites (par exemple, 10 classes mais la valeur d'étiquette est égale à 10), ou d'un indice de tableau hors limites. Le message d'erreur est généré sur le GPU, et l'emplacement d'affichage par défaut est inexact.
Exemple
import os
os.environ["CUDA_LAUNCH_BLOCKING"] = "1"
9.5 La vitesse d'entraînement ne s'améliore pas (faible utilisation du GPU)
Raison : Le chargement des données devient le goulot d'étranglement ; le GPU passe la plupart de son temps à attendre que le CPU prépare les données.
Étapes de dépannage :
- Exécutez
nvidia-smiouwatch -n 1 nvidia-smiObservez l'utilisation du GPU- Une utilisation constamment inférieure à 80% indique un goulot d'étranglement des données.
- Augmenter le paramètre num_workers du DataLoader
- Activer pin_memory=True
- Déplacer le prétraitement des données vers le GPU (torchvision.transforms prend en charge les opérations GPU)
- Précharger en mémoire les jeux de données de petits fichiers
9.6 Utilisation de PyTorch Profiler pour localiser les goulots d'étranglement
Exemple
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()
# Trier par temps CUDA, afficher les 15 premiers éléments
print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=15))
10. Référence rapide de l'API
Ce qui suit est un tableau de référence rapide pour les API courantes liées au GPU de PyTorch.
10.1 Gestion des périphériques
| Opération | Code |
|---|---|
| Détecter la disponibilité de CUDA | torch.cuda.is_available() |
| Sélectionner le périphérique | torch.device("cuda" if ... else "cpu") |
| Nombre de GPU | torch.cuda.device_count() |
| Nom du GPU | torch.cuda.get_device_name(0) |
| Définir le GPU actuel | torch.cuda.set_device(0) |
| Attendre que le GPU termine | torch.cuda.synchronize() |
10.2 Opérations sur les tenseurs
| Opération | Code |
|---|---|
| Déplacer le tenseur vers le GPU | tensor.to(device)outensor.cuda() |
| Déplacer le tenseur vers le CPU | tensor.cpu() |
| Afficher l'appareil actuel | tensor.device |
| Indiquer s'il est sur GPU | tensor.is_cuda |
| Convertir un tenseur GPU en numpy | tensor.detach().cpu().numpy() |
| Transfert asynchrone | tensor.to(device, non_blocking=True) |
10.3 Opérations sur le modèle
| Opération | Code |
|---|---|
| Déplacer le modèle vers le GPU | model.to(device) |
| Activer le mode entraînement | model.train() |
| Activer le mode inférence | model.eval() |
| Accélération de la compilation | torch.compile(model) |
| Désactiver les gradients | with torch.no_grad(): |
| Mode inférence | with torch.inference_mode(): |
10.4 Gestion de la mémoire GPU
| Opération | Code |
|---|---|
| Mémoire GPU allouée | torch.cuda.memory_allocated() |
| Mémoire GPU en cache | torch.cuda.memory_reserved() |
| Rapport sur la mémoire GPU | torch.cuda.memory_summary() |
| Vider le cache | torch.cuda.empty_cache() |
10.5 Référence rapide des principes fondamentaux
1. 用 device 变量统一管理,不要硬编码 "cuda" 2. 模型和数据必须在同一设备上,每个 batch 都要 .to(device) 3. 验证和推理时一定使用 torch.no_grad() ou 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()Autres extensions