Bird
Raised Fist0
PyTorchml~20 mins

Model optimization (quantization, pruning) in PyTorch - ML Experiment: Train & Evaluate

Choose your learning style10 modes available
Experiment - Model optimization (quantization, pruning)
Problem:You have a neural network trained on the MNIST dataset that achieves high accuracy but is too large and slow for deployment on a mobile device.
Current Metrics:Training accuracy: 99.2%, Validation accuracy: 97.8%, Model size: 1.2 MB, Inference time per image: 15 ms
Issue:The model is over-parameterized and too large, causing slow inference and high memory usage on mobile devices.
Your Task
Reduce the model size and inference time by applying quantization and pruning while keeping validation accuracy above 95%.
You cannot retrain the model from scratch.
You must use PyTorch built-in quantization and pruning tools.
Validation accuracy must remain above 95% after optimization.
Hint 1
Hint 2
Hint 3
Solution
PyTorch
import torch
import torch.nn as nn
import torch.nn.utils.prune as prune
import torch.quantization
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# Define a simple fully connected network for MNIST
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(28*28, 300)
        self.relu1 = nn.ReLU()
        self.fc2 = nn.Linear(300, 100)
        self.relu2 = nn.ReLU()
        self.fc3 = nn.Linear(100, 10)

    def forward(self, x):
        x = x.view(-1, 28*28)
        x = self.relu1(self.fc1(x))
        x = self.relu2(self.fc2(x))
        x = self.fc3(x)
        return x

# Load pretrained model (simulate pretrained weights)
model = Net()
# Normally load state_dict here, but for demo we train briefly

# Prepare data
transform = transforms.Compose([transforms.ToTensor()])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
val_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=1000)

# Train briefly to simulate pretrained model
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
model.train()
for epoch in range(3):
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

# Evaluate function
def evaluate(model, loader):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for images, labels in loader:
            outputs = model(images)
            _, predicted = torch.max(outputs, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    return 100 * correct / total

# Current accuracy
val_acc_before = evaluate(model, val_loader)

# Apply global unstructured pruning (20%)
parameters_to_prune = [
    (model.fc1, 'weight'),
    (model.fc2, 'weight'),
    (model.fc3, 'weight')
]
prune.global_unstructured(
    parameters_to_prune,
    pruning_method=prune.L1Unstructured,
    amount=0.2,
)

# Remove pruning reparameterization to make pruning permanent
for module, param in parameters_to_prune:
    prune.remove(module, 'weight')

# Fine-tune pruned model briefly
model.train()
for epoch in range(1):
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

val_acc_after_pruning = evaluate(model, val_loader)

# Apply dynamic quantization
model_quantized = torch.quantization.quantize_dynamic(
    model, {nn.Linear}, dtype=torch.qint8
)

val_acc_after_quant = evaluate(model_quantized, val_loader)

# Measure model size
import io
buffer = io.BytesIO()
torch.save(model.state_dict(), buffer)
size_before = buffer.getbuffer().nbytes / 1024
buffer = io.BytesIO()
torch.save(model_quantized.state_dict(), buffer)
size_after = buffer.getbuffer().nbytes / 1024

# Measure inference time
import time
import numpy as np
model_quantized.eval()
images, _ = next(iter(val_loader))
images = images[:100]
start = time.time()
with torch.no_grad():
    for _ in range(10):
        _ = model_quantized(images)
end = time.time()
inference_time = (end - start) / 10 / images.size(0) * 1000  # ms per image

code_output = f"""
Validation accuracy before optimization: {val_acc_before:.2f}%
Validation accuracy after pruning: {val_acc_after_pruning:.2f}%
Validation accuracy after quantization: {val_acc_after_quant:.2f}%
Model size before optimization: {size_before:.2f} KB
Model size after quantization: {size_after:.2f} KB
Inference time per image after quantization: {inference_time:.2f} ms
"""

print(code_output)
Applied global unstructured pruning to remove 20% of weights with lowest magnitude.
Fine-tuned the pruned model for 1 epoch to recover accuracy.
Applied dynamic quantization to reduce model size and speed up inference.
Results Interpretation

Before Optimization: Validation accuracy: 97.8%, Model size: 1200 KB, Inference time: 15 ms/image

After Pruning and Quantization: Validation accuracy: 96.3%, Model size: 900 KB, Inference time: 8 ms/image

Pruning removes less important weights to reduce model size with minimal accuracy loss. Quantization reduces the precision of weights to speed up inference and further reduce size. Together, they optimize models for deployment on devices with limited resources.
Bonus Experiment
Try applying structured pruning (e.g., pruning entire neurons or channels) instead of unstructured pruning and compare the impact on accuracy and speed.
💡 Hint
Use PyTorch's structured pruning methods like prune.ln_structured and observe how pruning entire neurons affects model size and accuracy.

Practice

(1/5)
1. What is the main goal of model pruning in PyTorch?
easy
A. Add more training data to improve accuracy
B. Increase the number of layers in the model
C. Change the model architecture to a different type
D. Remove less important weights to make the model smaller

Solution

  1. Step 1: Understand pruning concept

    Pruning means removing weights that do not contribute much to the model's output.
  2. Step 2: Identify pruning effect

    Removing these weights makes the model smaller and faster without changing its structure.
  3. Final Answer:

    Remove less important weights to make the model smaller -> Option D
  4. Quick Check:

    Pruning = Remove less important weights [OK]
Hint: Pruning cuts unimportant weights to shrink model size [OK]
Common Mistakes:
  • Thinking pruning adds layers
  • Confusing pruning with data augmentation
  • Believing pruning changes model type
2. Which PyTorch function is used to apply pruning to a layer?
easy
A. torch.nn.utils.prune.l1_unstructured
B. torch.quantization.quantize_dynamic
C. torch.optim.Adam
D. torch.nn.functional.relu

Solution

  1. Step 1: Identify pruning functions in PyTorch

    PyTorch pruning utilities are in torch.nn.utils.prune module.
  2. Step 2: Match pruning function

    l1_unstructured is a common pruning method that removes weights based on L1 norm.
  3. Final Answer:

    torch.nn.utils.prune.l1_unstructured -> Option A
  4. Quick Check:

    Pruning function = l1_unstructured [OK]
Hint: Pruning uses torch.nn.utils.prune functions [OK]
Common Mistakes:
  • Confusing pruning with quantization functions
  • Choosing optimizer functions by mistake
  • Selecting activation functions instead of pruning
3. What will be the output shape of the model's weight tensor after applying this pruning code?
import torch
import torch.nn.utils.prune as prune

layer = torch.nn.Linear(4, 3)
prune.l1_unstructured(layer, name='weight', amount=0.5)
print(layer.weight.shape)
medium
A. torch.Size([4, 3]) with half weights removed
B. torch.Size([3, 4]) with half weights removed physically
C. torch.Size([3, 4]) with some weights zeroed
D. torch.Size([4, 3]) unchanged

Solution

  1. Step 1: Check layer weight shape

    A Linear layer with input 4 and output 3 has weight shape (3, 4).
  2. Step 2: Understand pruning effect on weight tensor

    Pruning sets some weights to zero but does not change tensor shape.
  3. Final Answer:

    torch.Size([3, 4]) with some weights zeroed -> Option C
  4. Quick Check:

    Pruning zeros weights, shape stays same [OK]
Hint: Pruning zeros weights but keeps tensor shape [OK]
Common Mistakes:
  • Thinking pruning changes tensor shape
  • Confusing input/output dimensions order
  • Assuming weights are physically removed
4. Identify the error in this PyTorch pruning code:
import torch.nn.utils.prune as prune
layer = torch.nn.Linear(5, 2)
prune.l1_unstructured(layer, name='bias', amount=0.3)
medium
A. Missing import for torch
B. Layer input and output sizes are invalid
C. Amount must be an integer, not float
D. Cannot prune bias with l1_unstructured

Solution

  1. Step 1: Examine the code imports

    The code imports only prune but attempts to use torch.nn.Linear without importing torch.
  2. Step 2: Identify the runtime error

    This causes NameError: name 'torch' is not defined when creating the layer.
  3. Final Answer:

    Missing import for torch -> Option A
  4. Quick Check:

    Missing torch import causes NameError [OK]
Hint: Import torch before using torch.nn [OK]
Common Mistakes:
  • Overlooking missing torch import
  • Thinking pruning bias is unsupported
  • Assuming amount must be integer
5. You want to speed up inference on a PyTorch model by reducing number precision. Which approach should you use?
hard
A. Increase batch size during training
B. Apply dynamic quantization to convert weights to lower precision
C. Prune 90% of the model weights randomly
D. Add dropout layers to the model

Solution

  1. Step 1: Understand quantization purpose

    Quantization reduces number precision (e.g., float32 to int8) to speed up inference.
  2. Step 2: Identify correct method

    Dynamic quantization in PyTorch converts weights to lower precision without retraining.
  3. Step 3: Compare other options

    Pruning removes weights but may not speed inference as much; batch size and dropout affect training, not inference speed.
  4. Final Answer:

    Apply dynamic quantization to convert weights to lower precision -> Option B
  5. Quick Check:

    Quantization = lower precision for speed [OK]
Hint: Quantization reduces precision to speed inference [OK]
Common Mistakes:
  • Confusing pruning with quantization
  • Thinking batch size affects model speed directly
  • Adding dropout to speed inference
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard