Training Debugging and Validation
Training deep networks often encounters issues. Systematic debugging helps identify and fix problems.
Loss Not Decreasing
def diagnose_loss_not_decreasing(model, train_loader, device):
"""Check common causes of non-converging models."""
# 1. Check learning rate
for param_group in model.optimizer.param_groups:
print(f"Learning rate: {param_group['lr']}")
# 2. Check gradient norms
model.train()
for inputs, targets in train_loader:
outputs = model(inputs.to(device))
loss = F.cross_entropy(outputs, targets.to(device))
loss.backward()
total_norm = 0.0
for p in model.parameters():
if p.grad is not None:
param_norm = p.grad.data.norm(2)
total_norm += param_norm.item() ** 2
total_norm = total_norm ** 0.5
print(f"Gradient norm: {total_norm:.6f}")
if total_norm < 1e-5:
print("ISSUE: Vanishing gradients")
elif total_norm > 100:
print("ISSUE: Exploding gradients - try gradient clipping")
model.zero_grad()
break
# 3. Check forward pass
with torch.no_grad():
for inputs, _ in train_loader:
outputs = model(inputs[:1].to(device))
print(f"Output range: [{outputs.min():.4f}, {outputs.max():.4f}]")
print(f"Output mean: {outputs.mean():.4f}")
if outputs.abs().max() < 0.1:
print("ISSUE: Outputs too small - check initialization")
break
Checking Gradients
def check_gradients(model):
"""Inspect gradient statistics."""
grad_stats = {}
for name, param in model.named_parameters():
if param.grad is not None:
grad_stats[name] = {
'mean': param.grad.mean().item(),
'std': param.grad.std().item(),
'min': param.grad.min().item(),
'max': param.grad.max().item(),
'has_nan': torch.isnan(param.grad).any().item(),
'has_inf': torch.isinf(param.grad).any().item()
}
# Print problematic gradients
for name, stats in grad_stats.items():
if stats['has_nan'] or stats['has_inf']:
print(f"NaN/Inf in {name}")
if abs(stats['mean']) > 1.0 or stats['std'] > 1.0:
print(f"Large gradient in {name}: mean={stats['mean']:.4f}, std={stats['std']:.4f}")
return grad_stats
Overfitting Detection
def check_overfitting(train_loader, val_loader, model, device):
"""Check if model is overfitting."""
model.eval()
# Training accuracy
train_correct, train_total = 0, 0
with torch.no_grad():
for inputs, targets in train_loader:
outputs = model(inputs.to(device))
_, predicted = outputs.max(1)
train_correct += predicted.eq(targets.to(device)).sum().item()
train_total += targets.size(0)
train_acc = 100. * train_correct / train_total
# Validation accuracy
val_correct, val_total = 0, 0
with torch.no_grad():
for inputs, targets in val_loader:
outputs = model(inputs.to(device))
_, predicted = outputs.max(1)
val_correct += predicted.eq(targets.to(device)).sum().item()
val_total += targets.size(0)
val_acc = 100. * val_correct / val_total
print(f"Train Acc: {train_acc:.2f}%")
print(f"Val Acc: {val_acc:.2f}%")
if train_acc - val_acc > 15:
print("OVERFITTING: Large gap between train and val accuracy")
print("Suggestions: Add dropout, data augmentation, weight decay, early stopping")
elif train_acc < 70 and val_acc < 70:
print("UNDERFITTING: Both train and val accuracy are low")
print("Suggestions: Increase model capacity, reduce regularization, increase learning rate")
Training Loss Diagnostics
def analyze_loss_curve(loss_history):
"""Analyze training loss curve for issues."""
if len(loss_history) < 10:
return
# Calculate rate of decrease
recent_loss = loss_history[-10:]
early_loss = loss_history[:10]
avg_recent = sum(recent_loss) / len(recent_loss)
avg_early = sum(early_loss) / len(early_loss)
if avg_recent > avg_early * 0.95:
print("Loss not decreasing - consider adjusting learning rate")
# Check for instability
if len(loss_history) > 100:
recent_std = np.std(loss_history[-100:])
if recent_std > 0.5:
print("Training unstable - consider gradient clipping or smaller LR")
# Check for NaN
if any(math.isnan(x) for x in loss_history):
print("NaN loss detected - check input data, reduce learning rate")
Quick Debugging Checklist
- Loss stays at NaN: Reduce LR, check data normalization
- Loss oscillates: Reduce LR, increase batch size
- Loss plateaus early: Increase model capacity, check initialization
- Train works, val fails: Add regularization, use dropout
- Gradients are zero: Check activation functions, learning rate
- Output is NaN: Check data for NaN values, use gradient clipping
Systematic debugging saves time and improves results.