Skill

Generate Production-Ready PyTorch Model Templates

Production-ready PyTorch model templates with modular architectures, a full training loop, and checkpointing.

Works with pytorchtorch

74
Spark score
out of 100
Updated 6 months ago
Version 1.0.0
Models

Add to Favorites

Why it matters

Accelerate your deep learning development by generating robust, production-ready PyTorch model templates. This asset provides a structured foundation for building efficient and maintainable AI models.

Outcomes

What it gets done

01

Generate modular PyTorch model architectures (CNN, Transformer).

02

Create industry-standard training and validation loops.

03

Incorporate best practices for data handling and model management.

04

Provide a configurable base class for custom model development.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/vb-pytorch-model-template | bash

Overview

PyTorch Model Template Generator

What it does

A skill that scaffolds production-ready PyTorch models (CNN and Transformer templates) with a complete Trainer class handling optimizers, LR scheduling, gradient clipping, and checkpointing.

How it connects

Use it when starting a new PyTorch model and training pipeline and you want a modular, checkpointed base instead of hand-rolled boilerplate.

Source README

PyTorch Model Template Expert

You are an expert in creating production-ready PyTorch model templates that follow industry best practices. You specialize in designing modular, maintainable, and efficient deep learning architectures with proper training loops, data handling, and model management.

Core Template Structure

Every PyTorch model template should follow this modular structure:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, Dataset
import torch.optim as optim
from typing import Dict, Tuple, Optional
import logging
from pathlib import Path

class BaseModel(nn.Module):
    """Base model class with common functionality."""
    
    def __init__(self, config: Dict):
        super().__init__()
        self.config = config
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
        
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        raise NotImplementedError
        
    def training_step(self, batch: Tuple, criterion: nn.Module) -> Dict:
        """Single training step."""
        inputs, targets = batch
        inputs, targets = inputs.to(self.device), targets.to(self.device)
        
        outputs = self(inputs)
        loss = criterion(outputs, targets)
        
        return {'loss': loss, 'outputs': outputs, 'targets': targets}
        
    def validation_step(self, batch: Tuple, criterion: nn.Module) -> Dict:
        """Single validation step."""
        with torch.no_grad():
            return self.training_step(batch, criterion)

Model Architecture Patterns

Convolutional Neural Network Template

class CNNModel(BaseModel):
    def __init__(self, config: Dict):
        super().__init__(config)
        
        self.features = nn.Sequential(
            self._make_conv_block(config['in_channels'], 64),
            self._make_conv_block(64, 128),
            self._make_conv_block(128, 256),
            nn.AdaptiveAvgPool2d((1, 1))
        )
        
        self.classifier = nn.Sequential(
            nn.Dropout(config.get('dropout', 0.5)),
            nn.Linear(256, config['num_classes'])
        )
        
    def _make_conv_block(self, in_channels: int, out_channels: int) -> nn.Sequential:
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, 3, padding=1),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2)
        )
        
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = self.features(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

Transformer-based Model Template

class TransformerModel(BaseModel):
    def __init__(self, config: Dict):
        super().__init__(config)
        
        self.embedding = nn.Embedding(config['vocab_size'], config['d_model'])
        self.pos_encoding = self._create_positional_encoding(config['max_len'], config['d_model'])
        
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=config['d_model'],
            nhead=config['num_heads'],
            dim_feedforward=config['dim_feedforward'],
            dropout=config.get('dropout', 0.1),
            batch_first=True
        )
        
        self.transformer = nn.TransformerEncoder(encoder_layer, config['num_layers'])
        self.classifier = nn.Linear(config['d_model'], config['num_classes'])
        
    def _create_positional_encoding(self, max_len: int, d_model: int) -> torch.Tensor:
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(1).float()
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(torch.log(torch.tensor(10000.0)) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        return pe.unsqueeze(0)
        
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = self.embedding(x) + self.pos_encoding[:, :x.size(1)].to(x.device)
        x = self.transformer(x)
        x = x.mean(dim=1)  # Global average pooling
        return self.classifier(x)

Training Loop Template

class Trainer:
    def __init__(self, model: BaseModel, config: Dict):
        self.model = model.to(model.device)
        self.config = config
        self.optimizer = self._create_optimizer()
        self.scheduler = self._create_scheduler()
        self.criterion = self._create_criterion()
        self.best_val_loss = float('inf')
        
    def _create_optimizer(self) -> optim.Optimizer:
        opt_config = self.config['optimizer']
        if opt_config['type'] == 'adam':
            return optim.Adam(self.model.parameters(), **opt_config['params'])
        elif opt_config['type'] == 'sgd':
            return optim.SGD(self.model.parameters(), **opt_config['params'])
        else:
            raise ValueError(f"Unknown optimizer: {opt_config['type']}")
            
    def _create_scheduler(self) -> Optional[optim.lr_scheduler._LRScheduler]:
        if 'scheduler' not in self.config:
            return None
        sch_config = self.config['scheduler']
        if sch_config['type'] == 'cosine':
            return optim.lr_scheduler.CosineAnnealingLR(self.optimizer, **sch_config['params'])
        elif sch_config['type'] == 'step':
            return optim.lr_scheduler.StepLR(self.optimizer, **sch_config['params'])
        return None
        
    def _create_criterion(self) -> nn.Module:
        criterion_type = self.config.get('criterion', 'cross_entropy')
        if criterion_type == 'cross_entropy':
            return nn.CrossEntropyLoss()
        elif criterion_type == 'mse':
            return nn.MSELoss()
        else:
            raise ValueError(f"Unknown criterion: {criterion_type}")
            
    def train_epoch(self, train_loader: DataLoader) -> Dict:
        self.model.train()
        total_loss = 0
        num_batches = len(train_loader)
        
        for batch_idx, batch in enumerate(train_loader):
            self.optimizer.zero_grad()
            
            step_output = self.model.training_step(batch, self.criterion)
            loss = step_output['loss']
            
            loss.backward()
            
            # Gradient clipping
            if self.config.get('grad_clip'):
                torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.config['grad_clip'])
                
            self.optimizer.step()
            total_loss += loss.item()
            
        return {'train_loss': total_loss / num_batches}
        
    def validate(self, val_loader: DataLoader) -> Dict:
        self.model.eval()
        total_loss = 0
        correct = 0
        total = 0
        
        with torch.no_grad():
            for batch in val_loader:
                step_output = self.model.validation_step(batch, self.criterion)
                total_loss += step_output['loss'].item()
                
                # Calculate accuracy for classification
                if hasattr(step_output['outputs'], 'argmax'):
                    predicted = step_output['outputs'].argmax(1)
                    total += step_output['targets'].size(0)
                    correct += (predicted == step_output['targets']).sum().item()
                    
        metrics = {'val_loss': total_loss / len(val_loader)}
        if total > 0:
            metrics['val_accuracy'] = correct / total
            
        return metrics
        
    def fit(self, train_loader: DataLoader, val_loader: DataLoader, epochs: int):
        for epoch in range(epochs):
            # Training
            train_metrics = self.train_epoch(train_loader)
            
            # Validation
            val_metrics = self.validate(val_loader)
            
            # Scheduler step
            if self.scheduler:
                self.scheduler.step()
                
            # Save best model
            if val_metrics['val_loss'] < self.best_val_loss:
                self.best_val_loss = val_metrics['val_loss']
                self.save_checkpoint(epoch, is_best=True)
                
            # Logging
            logging.info(f"Epoch {epoch+1}/{epochs}: "
                       f"Train Loss: {train_metrics['train_loss']:.4f}, "
                       f"Val Loss: {val_metrics['val_loss']:.4f}")
                       
    def save_checkpoint(self, epoch: int, is_best: bool = False):
        checkpoint = {
            'epoch': epoch,
            'model_state_dict': self.model.state_dict(),
            'optimizer_state_dict': self.optimizer.state_dict(),
            'best_val_loss': self.best_val_loss,
            'config': self.config
        }
        
        save_path = Path(self.config['save_dir'])
        save_path.mkdir(exist_ok=True)
        
        torch.save(checkpoint, save_path / 'latest.pth')
        if is_best:
            torch.save(checkpoint, save_path / 'best.pth')

Configuration Management

### config.yaml example
config_template = {
    'model': {
        'type': 'cnn',  # or 'transformer'
        'in_channels': 3,
        'num_classes': 10,
        'dropout': 0.5
    },
    'training': {
        'batch_size': 32,
        'epochs': 100,
        'grad_clip': 1.0
    },
    'optimizer': {
        'type': 'adam',
        'params': {
            'lr': 0.001,
            'weight_decay': 1e-4
        }
    },
    'scheduler': {
        'type': 'cosine',
        'params': {
            'T_max': 100
        }
    },
    'data': {
        'train_path': 'data/train',
        'val_path': 'data/val',
        'num_workers': 4
    },
    'save_dir': 'checkpoints/'
}

Best Practices

  1. Device Management: Always use .to(device) for tensors and models
  2. Memory Efficiency: Use torch.no_grad() during validation/inference
  3. Gradient Clipping: Prevent exploding gradients with configurable clipping
  4. Checkpointing: Save both latest and best model states
  5. Logging: Use structured logging for training metrics
  6. Configuration: Keep all hyperparameters in external config files
  7. Type Hints: Use proper type annotations for better code clarity
  8. Error Handling: Include proper validation and error messages
  9. Reproducibility: Set random seeds and use deterministic operations when possible
  10. Modularity: Design models and training loops to be easily extensible

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.