ResNet Implementation Guide
Complete ResNet Pipeline
This guide walks through implementing image classification with ResNet from start to finish.
Step 1: Imports and Setup
import numpy as np
import torch
import torch.nn as nn
from torchvision.models import resnet50, ResNet50_Weights
from torchvision import transforms
from PIL import Image
import matplotlib.pyplot as plt
import torch.nn.functional as F
Each import's purpose:
torch.nn: Neural network layersResNet50_Weights: Pre-trained weightstransforms: Image preprocessingPIL.Image: Image loadingmatplotlib.pyplot: Visualization
Step 2: Load Pre-trained Model
# Load weights
weights = ResNet50_Weights.DEFAULT
model = resnet50(weights=weights)
# Set to evaluation mode
model.eval()
# Get metadata
categories = weights.meta['categories']
num_classes = len(categories)
print(f"Model loaded: {model.__class__.__name__}")
print(f"Number of classes: {num_classes}")
print(f"Model parameters: {sum(p.numel() for p in model.parameters()):,}")
Output:
Model loaded: ResNet
Number of classes: 1000
Model parameters: 25,557,032
Key points:
ResNet50_Weights.DEFAULT: Latest recommended weightsmodel.eval(): Critical for inference (disables dropout, freezes batch norm)weights.meta: Contains important metadata
Step 3: Create Preprocessing Pipeline
# Define transforms
preprocess = transforms.Compose([
transforms.Resize((256, 256)), # Resize to 256
transforms.CenterCrop((224, 224)), # Crop center 224×224
transforms.ToTensor(), # Convert to tensor [0, 1]
transforms.Normalize(
mean=[0.485, 0.456, 0.406], # ImageNet mean (per channel)
std=[0.229, 0.224, 0.225] # ImageNet std (per channel)
)
])
print("Preprocessing pipeline created:")
print("1. Resize to 256×256")
print("2. Center crop to 224×224")
print("3. Convert to tensor")
print("4. Normalize with ImageNet statistics")
Why this pipeline?
- Resize to 256: Maintains aspect ratio without distortion
- CenterCrop to 224: Standard ResNet input size
- ToTensor: Converts PIL Image to torch tensor
- Normalize: Uses ImageNet training statistics for compatibility
Step 4: Load and Preprocess Image
# Load image
image_path = '../data/image-3.jpg'
image = Image.open(image_path).convert('RGB')
print(f"Original image size: {image.size}")
# Apply preprocessing
image_tensor = preprocess(image)
print(f"Tensor shape: {image_tensor.shape}")
print(f"Value range: [{image_tensor.min():.2f}, {image_tensor.max():.2f}]")
# Add batch dimension
batch = image_tensor.unsqueeze(0)
print(f"Batch shape: {batch.shape}")
# Move to device (GPU if available)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
batch = batch.to(device)
model = model.to(device)
print(f"Using device: {device}")
Output example:
Original image size: (640, 480)
Tensor shape: torch.Size([3, 224, 224])
Value range: [-2.03, 2.62]
Batch shape: torch.Size([1, 3, 224, 224])
Using device: cuda
Step 5: Run Inference
# Disable gradient computation
with torch.no_grad():
# Forward pass
logits = model(batch)
print(f"Output logits shape: {logits.shape}")
# Output: torch.Size([1, 1000])
# 1 = batch size, 1000 = number of classes
# Examine raw logits
print(f"Min logit: {logits.min():.2f}")
print(f"Max logit: {logits.max():.2f}")
print(f"Mean logit: {logits.mean():.2f}")
Understanding logits:
Logits are raw, unnormalized scores
- Can be negative or very large
- Not probabilities yet
- Need softmax to convert to probabilities
Step 6: Convert Logits to Probabilities
# Apply softmax to convert to probabilities
probabilities = F.softmax(logits, dim=1)
print(f"Probabilities shape: {probabilities.shape}")
print(f"Sum of probabilities: {probabilities.sum(dim=1).item():.4f}") # Should be ~1.0
print(f"Min probability: {probabilities.min():.4f}")
print(f"Max probability: {probabilities.max():.4f}")
Output:
Probabilities shape: torch.Size([1, 1000])
Sum of probabilities: 1.0000 ← Valid probability distribution!
Min probability: 0.0001
Max probability: 0.9247
What softmax does:
Logits: [-2.5, 1.2, 3.8, 0.5, -1.1]
↓ (softmax)
Probs: [0.001, 0.017, 0.925, 0.050, 0.007]
Step 7: Get Predictions
# Get predicted class
predicted_idx = torch.argmax(probabilities, dim=1).item()
confidence = probabilities[0, predicted_idx].item()
# Get class name
predicted_class = categories[predicted_idx]
print(f"Predicted class: {predicted_class}")
print(f"Confidence: {confidence:.2%}")
Output example:
Predicted class: golden_retriever
Confidence: 92.47%
Step 8: Get Top-K Predictions
# Get top-5 predictions
top_k = 5
top_probs, top_indices = torch.topk(probabilities, top_k, dim=1)
print(f"\nTop-{top_k} Predictions:")
print("-" * 40)
for i, (prob, idx) in enumerate(zip(top_probs[0], top_indices[0]), 1):
class_name = categories[idx.item()]
confidence = prob.item()
print(f"{i}. {class_name:<30} {confidence:>6.2%}")
print("-" * 40)
Output example:
Top-5 Predictions:
----------------------------------------
1. golden_retriever 92.47%
2. Labrador_retriever 4.32%
3. Yellow_Labrador 2.15%
4. Irish_setter 0.89%
5. Great_Pyrenees 0.17%
----------------------------------------
Step 9: Visualization
def visualize_predictions(image_path, top_k=5):
"""Visualize image with top-k predictions"""
# Load and process image
image = Image.open(image_path).convert('RGB')
image_tensor = preprocess(image).unsqueeze(0).to(device)
# Get predictions
with torch.no_grad():
logits = model(image_tensor)
probabilities = F.softmax(logits, dim=1)
top_probs, top_indices = torch.topk(probabilities, top_k, dim=1)
# Create visualization
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# Left: Original image
ax1.imshow(image)
ax1.set_title('Input Image')
ax1.axis('off')
# Right: Top predictions
class_names = [categories[idx.item()] for idx in top_indices[0]]
probs = [prob.item() for prob in top_probs[0]]
# Replace underscores and capitalize
class_names = [name.replace('_', ' ').title() for name in class_names]
bars = ax2.barh(range(top_k), probs)
ax2.set_yticks(range(top_k))
ax2.set_yticklabels(class_names)
ax2.set_xlabel('Confidence')
ax2.set_title(f'Top-{top_k} Predictions')
ax2.invert_yaxis()
# Color bars by confidence
colors = plt.cm.RdYlGn(np.array(probs))
for bar, color in zip(bars, colors):
bar.set_color(color)
# Add percentage labels
for i, (prob, bar) in enumerate(zip(probs, bars)):
ax2.text(prob, i, f' {prob:.1%}', va='center')
plt.tight_layout()
plt.show()
return class_names[0], probs[0]
# Usage
predicted_class, confidence = visualize_predictions('../data/image-3.jpg')
print(f"\nFinal Prediction: {predicted_class} ({confidence:.2%})")
Complete Example Function
class ResNetClassifier:
"""Reusable ResNet classifier"""
def __init__(self, device=None):
# Load model
weights = ResNet50_Weights.DEFAULT
self.model = resnet50(weights=weights)
self.model.eval()
self.categories = weights.meta['categories']
# Set device
if device is None:
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
else:
self.device = device
self.model = self.model.to(self.device)
# Preprocessing
self.preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
def predict(self, image_path, top_k=5):
"""
Predict class of image
Args:
image_path: Path to image file
top_k: Return top-k predictions
Returns:
dict with prediction results
"""
# Load and preprocess image
image = Image.open(image_path).convert('RGB')
image_tensor = self.preprocess(image).unsqueeze(0).to(self.device)
# Inference
with torch.no_grad():
logits = self.model(image_tensor)
# Get probabilities
probabilities = F.softmax(logits, dim=1)
# Get top-k predictions
top_probs, top_indices = torch.topk(probabilities, top_k, dim=1)
# Format results
results = []
for prob, idx in zip(top_probs[0], top_indices[0]):
class_idx = idx.item()
class_name = self.categories[class_idx]
confidence = prob.item()
results.append({
'class': class_name,
'confidence': confidence,
'class_idx': class_idx
})
return {
'prediction': results[0]['class'],
'confidence': results[0]['confidence'],
'top_k': results,
'image_path': image_path
}
# Usage
classifier = ResNetClassifier()
results = classifier.predict('../data/image-3.jpg', top_k=5)
print(f"Prediction: {results['prediction']}")
print(f"Confidence: {results['confidence']:.2%}")
print("\nTop-5:")
for i, result in enumerate(results['top_k'], 1):
print(f"{i}. {result['class']}: {result['confidence']:.2%}")
Batch Processing
def batch_predict(image_paths, top_k=1):
"""Predict on multiple images efficiently"""
classifier = ResNetClassifier()
results = []
for image_path in image_paths:
result = classifier.predict(image_path, top_k=top_k)
results.append(result)
print(f"✓ {image_path}: {result['prediction']}")
return results
# Usage
images = [
'../data/image-1.jpg',
'../data/image-2.jpg',
'../data/image-3.jpg'
]
batch_results = batch_predict(images)
Performance Tips
GPU Acceleration
# Check if GPU is available
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
# Move model to GPU
model = model.to(device)
# For large batches, process in chunks
batch_size = 32
for i in range(0, len(images), batch_size):
batch = images[i:i+batch_size]
# Process batch
Model Optimization
# Half precision for faster inference (on newer GPUs)
model = model.half()
image_tensor = image_tensor.half()
# Quantization
from torch.quantization import quantize_dynamic
model_int8 = quantize_dynamic(
model, {nn.Linear},
dtype=torch.qint8
)
# Export to ONNX for production
import torch.onnx
torch.onnx.export(
model,
image_tensor,
"resnet50.onnx",
input_names=['input'],
output_names=['output']
)
Error Handling
def safe_predict(image_path, classifier):
"""Safely predict with error handling"""
try:
# Check file exists
if not os.path.exists(image_path):
return {'error': f"File not found: {image_path}"}
# Check file is readable
try:
image = Image.open(image_path)
image.verify()
except Exception as e:
return {'error': f"Invalid image file: {e}"}
# Predict
result = classifier.predict(image_path)
return result
except torch.cuda.OutOfMemoryError:
return {'error': "GPU out of memory"}
except Exception as e:
return {'error': f"Prediction failed: {e}"}
Summary
Complete ResNet inference pipeline:
- Load pre-trained model
- Create preprocessing pipeline
- Load and preprocess image
- Run inference
- Convert logits to probabilities
- Extract predictions
- Visualize results
Key code patterns to remember:
- Always use
.eval()mode for inference - Always preprocess consistently
- Always use
torch.no_grad()for inference - Always normalize with ImageNet statistics
- Always add batch dimension
Next Steps
- Compare with ViT → Vision Transformer Implementation
- Run Notebook → Notebooks Guide
- Architecture Details → ResNet Overview