Bird
Raised Fist0
PyTorchml~20 mins

nn.MaxPool2d and nn.AvgPool2d in PyTorch - ML Experiment: Train & Evaluate

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Experiment - nn.MaxPool2d and nn.AvgPool2d
Problem:You are using a convolutional neural network (CNN) for image classification. The model uses nn.MaxPool2d layers to reduce the size of feature maps. However, you want to understand how using nn.AvgPool2d instead affects the model's performance.
Current Metrics:Training accuracy: 88%, Validation accuracy: 75%, Training loss: 0.35, Validation loss: 0.60
Issue:The model shows a gap between training and validation accuracy, indicating some overfitting. Also, you want to explore if average pooling can improve generalization.
Your Task
Replace nn.MaxPool2d layers with nn.AvgPool2d in the CNN and compare the training and validation accuracy and loss. Aim to reduce overfitting by improving validation accuracy to at least 78% while keeping training accuracy above 85%.
Keep the rest of the model architecture and training parameters the same.
Do not change the dataset or data augmentation.
Train for the same number of epochs.
Hint 1
Hint 2
Hint 3
Solution
PyTorch
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# Define transforms
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

# Load dataset
train_dataset = datasets.FashionMNIST(root='./data', train=True, download=True, transform=transform)
val_dataset = datasets.FashionMNIST(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=64, shuffle=False)

# Define CNN with AvgPool2d instead of MaxPool2d
class CNNAvgPool(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, padding=1)
        self.pool = nn.AvgPool2d(2, 2)  # Changed from MaxPool2d
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, 10)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.relu(self.conv1(x))
        x = self.pool(x)
        x = self.relu(self.conv2(x))
        x = self.pool(x)
        x = x.view(-1, 64 * 7 * 7)
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# Initialize model, loss, optimizer
model = CNNAvgPool()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# Training loop
num_epochs = 10
for epoch in range(num_epochs):
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item() * images.size(0)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    train_loss = running_loss / total
    train_acc = 100 * correct / total

    model.eval()
    val_loss = 0.0
    val_correct = 0
    val_total = 0
    with torch.no_grad():
        for images, labels in val_loader:
            outputs = model(images)
            loss = criterion(outputs, labels)
            val_loss += loss.item() * images.size(0)
            _, predicted = torch.max(outputs, 1)
            val_total += labels.size(0)
            val_correct += (predicted == labels).sum().item()
    val_loss /= val_total
    val_acc = 100 * val_correct / val_total

    print(f'Epoch {epoch+1}/{num_epochs} - Train loss: {train_loss:.4f}, Train acc: {train_acc:.2f}%, Val loss: {val_loss:.4f}, Val acc: {val_acc:.2f}%')
Replaced nn.MaxPool2d layers with nn.AvgPool2d layers in the CNN architecture.
Kept all other model parameters and training settings unchanged.
Results Interpretation

Before: Training accuracy: 88%, Validation accuracy: 75%, Training loss: 0.35, Validation loss: 0.60

After: Training accuracy: 86%, Validation accuracy: 79%, Training loss: 0.40, Validation loss: 0.55

Using average pooling instead of max pooling can reduce overfitting by smoothing feature maps, which helps improve validation accuracy and reduce validation loss, even if training accuracy slightly decreases.
Bonus Experiment
Try combining nn.MaxPool2d and nn.AvgPool2d layers in the CNN, for example, use MaxPool2d after the first convolution and AvgPool2d after the second. Observe how this hybrid approach affects model performance.
💡 Hint
Mixing pooling types can balance feature highlighting and smoothing, potentially improving generalization.

Practice

(1/5)
1. What is the main difference between nn.MaxPool2d and nn.AvgPool2d in PyTorch?
easy
A. nn.MaxPool2d selects the maximum value in each window, while nn.AvgPool2d computes the average value.
B. nn.MaxPool2d computes the average value, while nn.AvgPool2d selects the maximum value.
C. Both perform the same operation but on different input shapes.
D. nn.MaxPool2d increases data size, nn.AvgPool2d decreases it.

Solution

  1. Step 1: Understand pooling operations

    nn.MaxPool2d picks the highest value in each sliding window, emphasizing strong features. nn.AvgPool2d calculates the average, smoothing the features.
  2. Step 2: Compare their behavior

    Max pooling keeps the strongest signals, while average pooling provides a smoothed summary of the window.
  3. Final Answer:

    nn.MaxPool2d selects the maximum value in each window, while nn.AvgPool2d computes the average value. -> Option A
  4. Quick Check:

    MaxPool2d = max, AvgPool2d = average [OK]
Hint: MaxPool picks max; AvgPool averages values [OK]
Common Mistakes:
  • Confusing max and average operations
  • Thinking both increase data size
  • Assuming they work on different input shapes
2. Which of the following is the correct way to create a 2D max pooling layer with a kernel size of 3 and stride of 2 in PyTorch?
easy
A. nn.AvgPool2d(kernel=3, stride=2)
B. nn.MaxPool2d(kernel_size=3, stride=2)
C. nn.MaxPool2d(stride=3, kernel_size=2)
D. nn.MaxPool2d(size=3, step=2)

Solution

  1. Step 1: Check PyTorch pooling layer parameters

    The correct parameters for nn.MaxPool2d are kernel_size and stride. The order does not matter if named.
  2. Step 2: Validate each option

    nn.MaxPool2d(kernel_size=3, stride=2) uses correct parameter names and values. nn.MaxPool2d(stride=3, kernel_size=2) swaps kernel_size and stride values incorrectly. nn.AvgPool2d(kernel=3, stride=2) uses AvgPool2d instead of MaxPool2d. nn.MaxPool2d(size=3, step=2) uses invalid parameter names.
  3. Final Answer:

    nn.MaxPool2d(kernel_size=3, stride=2) -> Option B
  4. Quick Check:

    Correct params: kernel_size, stride [OK]
Hint: Use kernel_size and stride parameters exactly [OK]
Common Mistakes:
  • Using wrong parameter names like size or step
  • Confusing MaxPool2d with AvgPool2d
  • Swapping kernel_size and stride values
3. What is the output shape of the following PyTorch code snippet?
import torch
import torch.nn as nn

input_tensor = torch.randn(1, 1, 6, 6)
pool = nn.MaxPool2d(kernel_size=2, stride=2)
output = pool(input_tensor)
print(output.shape)
medium
A. torch.Size([1, 1, 2, 2])
B. torch.Size([1, 1, 6, 6])
C. torch.Size([1, 1, 4, 4])
D. torch.Size([1, 1, 3, 3])

Solution

  1. Step 1: Understand input and pooling parameters

    Input shape is (batch=1, channels=1, height=6, width=6). Kernel size and stride are both 2.
  2. Step 2: Calculate output dimensions

    Output height = floor((6 - 2) / 2) + 1 = floor(4 / 2) + 1 = 2 + 1 = 3. Similarly, output width = 3. So output shape is (1, 1, 3, 3).
  3. Final Answer:

    torch.Size([1, 1, 3, 3]) -> Option D
  4. Quick Check:

    Output size = floor((input - kernel)/stride)+1 [OK]
Hint: Output size = floor((input - kernel)/stride) + 1 [OK]
Common Mistakes:
  • Forgetting to apply floor function
  • Mixing up height and width calculations
  • Assuming output size equals input size
4. Identify the error in the following PyTorch code using nn.AvgPool2d:
import torch
import torch.nn as nn

input_tensor = torch.randn(1, 1, 5, 5)
pool = nn.AvgPool2d(kernel_size=2, stride=3)
output = pool(input_tensor)
print(output.shape)
medium
A. No error; code runs correctly
B. Kernel size must be odd
C. Stride cannot be greater than kernel size
D. Input tensor shape is invalid

Solution

  1. Step 1: Check parameter validity

    PyTorch allows stride to be different from kernel size, including stride > kernel size. Kernel size can be even or odd. Input tensor shape is valid.
  2. Step 2: Confirm code runs without error

    Running this code produces a valid output shape without errors.
  3. Final Answer:

    No error; code runs correctly -> Option A
  4. Quick Check:

    Stride can differ from kernel size [OK]
Hint: Stride can be any positive int, not limited by kernel size [OK]
Common Mistakes:
  • Assuming stride must be <= kernel size
  • Thinking kernel size must be odd
  • Believing input shape is invalid for pooling
5. You want to reduce the spatial size of a feature map from (1, 1, 10, 10) to (1, 1, 3, 3) using pooling layers. Which combination of nn.MaxPool2d or nn.AvgPool2d with kernel size and stride will achieve this output shape?
hard
A. Use nn.MaxPool2d with kernel_size=2, stride=2 twice sequentially
B. Use nn.AvgPool2d with kernel_size=4, stride=4
C. Use nn.MaxPool2d with kernel_size=3, stride=3
D. Use nn.AvgPool2d with kernel_size=5, stride=5

Solution

  1. Step 1: Calculate output size for kernel_size=3, stride=3

    Output size = floor((10 - 3)/3) + 1 = floor(7/3) + 1 = 2 + 1 = 3, matching desired size.
  2. Step 2: Check other options

    nn.AvgPool2d(kernel_size=4, stride=4): floor((10-4)/4)+1 = floor(6/4)+1 = 1 + 1 = 2 ≠ 3.
    nn.MaxPool2d(kernel_size=2, stride=2) twice: first floor((10-2)/2)+1 = 4 + 1 = 5, second floor((5-2)/2)+1 = 1 + 1 = 2 ≠ 3.
    nn.AvgPool2d(kernel_size=5, stride=5): floor((10-5)/5)+1 = 1 + 1 = 2 ≠ 3.
  3. Final Answer:

    Use nn.MaxPool2d with kernel_size=3, stride=3 -> Option C
  4. Quick Check:

    Output size = floor((input - kernel)/stride) + 1 [OK]
Hint: Output size = floor((input - kernel)/stride) + 1 [OK]
Common Mistakes:
  • Ignoring floor function in output size calculation
  • Assuming one pooling layer can't reduce to 3x3
  • Confusing stride and kernel size effects