Communitygithub.com

pytorch-patterns

Modèles et bonnes pratiques PyTorch pour construire des pipelines d’entraînement, architectures de modèles et chargements de données robustes, efficaces et reproductibles.

Qu'est-ce que pytorch-patterns ?

Cette compétence fournit un guide complet des bonnes pratiques PyTorch pour le développement de deep learning. Elle couvre tous les aspects essentiels : écriture de code indifférent au périphérique (device-agnostic), reproductibilité via le contrôle des seeds, gestion explicite des formes de tenseurs, et structuration propre des modules `nn.Module`. Elle détaille des patterns concrets pour les boucles d’entraînement et de validation, la configuration efficace des DataLoader, le checkpointing, et l’optimisation des performances avec la précision mixte (`torch.amp.autocast`), le gradient checkpointing et `torch.compile`. L’accent est mis sur l’évitement des anti-patrons (comme l’oubli de `model.eval()`, les opérations in-place qui cassent l’autograd, ou le déplacement répété du modèle sur GPU). Cette compétence est idéale pour les développeurs utilisant PyTorch avec des agents comme Claude Code, Cursor ou Codex, cherchant à améliorer la qualité, la reproductibilité et les performances de leur code.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/pytorch-patterns

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Modèles de Développement PyTorch

Modèles idiomatiques PyTorch et bonnes pratiques pour construire des applications de deep learning robustes, efficaces et reproductibles.

Quand Activer

  • Écrire de nouveaux modèles PyTorch ou scripts d'entraînement
  • Réviser du code de deep learning
  • Déboguer des boucles d'entraînement ou des pipelines de données
  • Optimiser l'utilisation de la mémoire GPU ou la vitesse d'entraînement
  • Mettre en place des expériences reproductibles

Principes Fondamentaux

1. Code Indifférent au Périphérique (Device-Agnostic)

Toujours écrire du code qui fonctionne à la fois sur CPU et GPU sans coder en dur le périphérique.

# Bon : Indifférent au périphérique
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = MyModel().to(device)
data = data.to(device)

# Mauvais : Périphérique codé en dur
model = MyModel().cuda()  # Plante si pas de GPU
data = data.cuda()

2. Reproductibilité Avant Tout

Définir toutes les seeds aléatoires pour des résultats reproductibles.

# Bon : Configuration complète de reproductibilité
def set_seed(seed: int = 42) -> None:
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    np.random.seed(seed)
    random.seed(seed)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

# Mauvais : Aucun contrôle de seed
model = MyModel()  # Poids différents à chaque exécution

3. Gestion Explicite des Formes

Toujours documenter et vérifier les formes des tenseurs.

# Bon : Passage forward annoté avec les formes
def forward(self, x: torch.Tensor) -> torch.Tensor:
    # x : (batch_size, channels, height, width)
    x = self.conv1(x)    # -> (batch_size, 32, H, W)
    x = self.pool(x)     # -> (batch_size, 32, H//2, W//2)
    x = x.view(x.size(0), -1)  # -> (batch_size, 32*H//2*W//2)
    return self.fc(x)    # -> (batch_size, num_classes)

# Mauvais : Aucun suivi des formes
def forward(self, x):
    x = self.conv1(x)
    x = self.pool(x)
    x = x.view(x.size(0), -1)  # Quelle est cette taille ?
    return self.fc(x)           # Est-ce que ça va même marcher ?

Modèles d'Architecture de Modèle

Structure Propre de nn.Module

# Bon : Module bien organisé
class ImageClassifier(nn.Module):
    def __init__(self, num_classes: int, dropout: float = 0.5) -> None:
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2),
        )
        self.classifier = nn.Sequential(
            nn.Dropout(dropout),
            nn.Linear(64 * 16 * 16, num_classes),
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = self.features(x)
        x = x.view(x.size(0), -1)
        return self.classifier(x)

# Mauvais : Tout dans forward
class ImageClassifier(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, x):
        x = F.conv2d(x, weight=self.make_weight())  # Crée un poids à chaque appel !
        return x

Initialisation Correcte des Poids

# Bon : Initialisation explicite
def _init_weights(self, module: nn.Module) -> None:
    if isinstance(module, nn.Linear):
        nn.init.kaiming_normal_(module.weight, mode="fan_out", nonlinearity="relu")
        if module.bias is not None:
            nn.init.zeros_(module.bias)
    elif isinstance(module, nn.Conv2d):
        nn.init.kaiming_normal_(module.weight, mode="fan_out", nonlinearity="relu")
    elif isinstance(module, nn.BatchNorm2d):
        nn.init.ones_(module.weight)
        nn.init.zeros_(module.bias)

model = MyModel()
model.apply(model._init_weights)

Modèles de Boucle d'Entraînement

Boucle d'Entraînement Standard

# Bon : Boucle d'entraînement complète avec bonnes pratiques
def train_one_epoch(
    model: nn.Module,
    dataloader: DataLoader,
    optimizer: torch.optim.Optimizer,
    criterion: nn.Module,
    device: torch.device,
    scaler: torch.amp.GradScaler | None = None,
) -> float:
    model.train()  # Toujours définir le mode train
    total_loss = 0.0

    for batch_idx, (data, target) in enumerate(dataloader):
        data, target = data.to(device), target.to(device)

        optimizer.zero_grad(set_to_none=True)  # Plus efficace que zero_grad()

        # Entraînement en précision mixte
        with torch.amp.autocast("cuda", enabled=scaler is not None):
            output = model(data)
            loss = criterion(output, target)

        if scaler is not None:
            scaler.scale(loss).backward()
            scaler.unscale_(optimizer)
            torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
            scaler.step(optimizer)
            scaler.update()
        else:
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
            optimizer.step()

        total_loss += loss.item()

    return total_loss / len(dataloader)

Boucle de Validation

# Bon : Évaluation correcte
@torch.no_grad()  # Plus efficace que d'encapsuler dans un bloc torch.no_grad()
def evaluate(
    model: nn.Module,
    dataloader: DataLoader,
    criterion: nn.Module,
    device: torch.device,
) -> tuple[float, float]:
    model.eval()  # Toujours définir le mode eval — désactive dropout, utilise les stats BN courantes
    total_loss = 0.0
    correct = 0
    total = 0

    for data, target in dataloader:
        data, target = data.to(device), target.to(device)
        output = model(data)
        total_loss += criterion(output, target).item()
        correct += (output.argmax(1) == target).sum().item()
        total += target.size(0)

    return total_loss / len(dataloader), correct / total

Modèles de Pipeline de Données

Dataset Personnalisé

# Bon : Dataset propre avec indications de type
class ImageDataset(Dataset):
    def __init__(
        self,
        image_dir: str,
        labels: dict[str, int],
        transform: transforms.Compose | None = None,
    ) -> None:
        self.image_paths = list(Path(image_dir).glob("*.jpg"))
        self.labels = labels
        self.transform = transform

    def __len__(self) -> int:
        return len(self.image_paths)

    def __getitem__(self, idx: int) -> tuple[torch.Tensor, int]:
        img = Image.open(self.image_paths[idx]).convert("RGB")
        label = self.labels[self.image_paths[idx].stem]

        if self.transform:
            img = self.transform(img)

        return img, label

Configuration Efficace du DataLoader

# Bon : DataLoader optimisé
dataloader = DataLoader(
    dataset,
    batch_size=32,
    shuffle=True,            # Mélanger pour l'entraînement
    num_workers=4,           # Chargement parallèle des données
    pin_memory=True,         # Transfert CPU->GPU plus rapide
    persistent_workers=True, # Garder les workers actifs entre les époques
    drop_last=True,          # Tailles de batch cohérentes pour BatchNorm
)

# Mauvais : Valeurs par défaut lentes
dataloader = DataLoader(dataset, batch_size=32)  # num_workers=0, pas de pin_memory

Collate Personnalisé pour Données de Longueur Variable

# Bon : Remplir les séquences dans collate_fn
def collate_fn(batch: list[tuple[torch.Tensor, int]]) -> tuple[torch.Tensor, torch.Tensor]:
    sequences, labels = zip(*batch)
    # Remplir jusqu'à la longueur max dans le lot
    padded = nn.utils.rnn.pad_sequence(sequences, batch_first=True, padding_value=0)
    return padded, torch.tensor(labels)

dataloader = DataLoader(dataset, batch_size=32, collate_fn=collate_fn)

Modèles de Checkpointing

Sauvegarder et Charger des Checkpoints

# Bon : Checkpoint complet avec tout l'état d'entraînement
def save_checkpoint(
    model: nn.Module,
    optimizer: torch.optim.Optimizer,
    epoch: int,
    loss: float,
    path: str,
) -> None:
    torch.save({
        "epoch": epoch,
        "model_state_dict": model.state_dict(),
        "optimizer_state_dict": optimizer.state_dict(),
        "loss": loss,
    }, path)

def load_checkpoint(
    path: str,
    model: nn.Module,
    optimizer: torch.optim.Optimizer | None = None,
) -> dict:
    checkpoint = torch.load(path, map_location="cpu", weights_only=True)
    model.load_state_dict(checkpoint["model_state_dict"])
    if optimizer:
        optimizer.load_state_dict(checkpoint["optimizer_state_dict"])
    return checkpoint

# Mauvais : Sauvegarde uniquement des poids du modèle (impossible de reprendre l'entraînement)
torch.save(model.state_dict(), "model.pt")

Optimisation des Performances

Entraînement en Précision Mixte

# Bon : AMP avec GradScaler
scaler = torch.amp.GradScaler("cuda")
for data, target in dataloader:
    with torch.amp.autocast("cuda"):
        output = model(data)
        loss = criterion(output, target)
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()
    optimizer.zero_grad(set_to_none=True)

Gradient Checkpointing pour les Grands Modèles

# Bon : Échanger du calcul contre de la mémoire
from torch.utils.checkpoint import checkpoint

class LargeModel(nn.Module):
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # Recalculer les activations pendant la rétropropagation pour économiser la mémoire
        x = checkpoint(self.block1, x, use_reentrant=False)
        x = checkpoint(self.block2, x, use_reentrant=False)
        return self.head(x)

torch.compile pour la Vitesse

# Bon : Compiler le modèle pour une exécution plus rapide (PyTorch 2.0+)
model = MyModel().to(device)
model = torch.compile(model, mode="reduce-overhead")

# Modes : "default" (sûr), "reduce-overhead" (plus rapide), "max-autotune" (le plus rapide)

Référence Rapide : Expressions Idiomatiques PyTorch

ExpressionDescription
model.train() / model.eval()Toujours définir le mode avant train/eval
torch.no_grad()Désactiver les gradients pour l'inférence
optimizer.zero_grad(set_to_none=True)Effacement plus efficace des gradients
.to(device)Placement tenseur/modèle indifférent au périphérique
torch.amp.autocastPrécision mixte pour vitesse ×2
pin_memory=TrueTransfert de données CPU→GPU plus rapide
torch.compileCompilation JIT pour la vitesse (2.0+)
weights_only=TrueChargement sécurisé du modèle
torch.manual_seedExpériences reproductibles
gradient_checkpointingÉchanger du calcul contre de la mémoire

Anti-Patrons à Éviter

# Mauvais : Oublier model.eval() pendant la validation
model.train()
with torch.no_grad():
    output = model(val_data)  # Dropout toujours actif ! BatchNorm utilise les stats du lot !

# Bon : Toujours définir le mode eval
model.eval()
with torch.no_grad():
    output = model(val_data)

# Mauvais : Opérations in-place qui cassent l'autograd
x = F.relu(x, inplace=True)  # Peut casser le calcul du gradient
x += residual                  # L'ajout in-place casse le graphe autograd

# Bon : Opérations hors-place
x = F.relu(x)
x = x + residual

# Mauvais : Déplacer les données sur GPU de manière répétée dans la boucle d'entraînement
for data, target in dataloader:
    model = model.cuda()  # Déplace le modèle à CHAQUE itération !

# Bon : Déplacer le modèle une fois avant la boucle
model = model.to(device)
for data, target in dataloader:
    data, target = data.to(device), target.to(device)

# Mauvais : Utiliser .item() avant backward
loss = criterion(output, target).item()  # Détache du graphe !
loss.backward()  # Erreur : impossible de rétropropager à travers .item()

# Bon : Appeler .item() uniquement pour le logging
loss = criterion(output, target)
loss.backward()
print(f"Perte : {loss.item():.4f}")  # .item() après backward est correct

# Mauvais : Ne pas utiliser torch.save correctement
torch.save(model, "model.pt")  # Sauvegarde le modèle entier (fragile, non portable)

# Bon : Sauvegarder state_dict
torch.save(model.state_dict(), "model.pt")

Rappelez-vous : Le code PyTorch doit être indifférent au périphérique, reproductible et conscient de la mémoire. En cas de doute, profilez avec torch.profiler et vérifiez la mémoire GPU avec torch.cuda.memory_summary().

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills associés