Bird
Raised Fist0
Computer Visionml~20 mins

Cutout and CutMix in Computer Vision - ML Experiment: Train & Evaluate

Choose your learning style10 modes available
Experiment - Cutout and CutMix
Problem:You are training an image classification model on CIFAR-10 dataset. The model currently achieves 85% training accuracy but only 70% validation accuracy.
Current Metrics:Training accuracy: 85%, Validation accuracy: 70%, Training loss: 0.45, Validation loss: 1.10
Issue:The model is overfitting. Training accuracy is much higher than validation accuracy, indicating poor generalization.
Your Task
Reduce overfitting by applying Cutout and CutMix data augmentation techniques to improve validation accuracy to at least 78% while keeping training accuracy below 90%.
You must keep the same model architecture and optimizer.
You can only add Cutout and CutMix augmentations during training.
Training epochs should remain at 50.
Hint 1
Hint 2
Hint 3
Hint 4
Solution
Computer Vision
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import numpy as np
import random

# Define Cutout augmentation
def cutout(img, n_holes=1, length=16):
    h, w = img.size(1), img.size(2)
    mask = torch.ones((h, w), dtype=torch.float32)

    for _ in range(n_holes):
        y = random.randint(0, h - 1)
        x = random.randint(0, w - 1)

        y1 = max(0, y - length // 2)
        y2 = min(h, y + length // 2)
        x1 = max(0, x - length // 2)
        x2 = min(w, x + length // 2)

        mask[y1:y2, x1:x2] = 0.

    mask = mask.expand_as(img)
    img = img * mask
    return img

# Define CutMix augmentation
def cutmix(data, targets, alpha=1.0):
    indices = torch.randperm(data.size(0))
    shuffled_data = data[indices]
    shuffled_targets = targets[indices]

    lam = np.random.beta(alpha, alpha)

    bbx1, bby1, bbx2, bby2 = rand_bbox(data.size(), lam)

    data[:, :, bby1:bby2, bbx1:bbx2] = shuffled_data[:, :, bby1:bby2, bbx1:bbx2]

    lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (data.size(-1) * data.size(-2)))

    targets = (targets, shuffled_targets, lam)
    return data, targets

def rand_bbox(size, lam):
    W = size[3]
    H = size[2]
    cut_rat = np.sqrt(1. - lam)
    cut_w = int(W * cut_rat)
    cut_h = int(H * cut_rat)

    cx = np.random.randint(W)
    cy = np.random.randint(H)

    bbx1 = np.clip(cx - cut_w // 2, 0, W)
    bby1 = np.clip(cy - cut_h // 2, 0, H)
    bbx2 = np.clip(cx + cut_w // 2, 0, W)
    bby2 = np.clip(cy + cut_h // 2, 0, H)

    return bbx1, bby1, bbx2, bby2

# Define simple CNN model
class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.fc1 = nn.Linear(64 * 8 * 8, 128)
        self.fc2 = nn.Linear(128, 10)

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

# Prepare CIFAR-10 dataset with Cutout and CutMix
transform_train = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
])

transform_test = transforms.Compose([
    transforms.ToTensor(),
])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, batch_size=100, shuffle=False, num_workers=2)

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

model = SimpleCNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

def train():
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0

    for inputs, targets in trainloader:
        inputs, targets = inputs.to(device), targets.to(device)

        # Apply Cutout
        inputs = torch.stack([cutout(img) for img in inputs])

        # Apply CutMix
        inputs, targets_mix = cutmix(inputs, targets)

        optimizer.zero_grad()

        outputs = model(inputs)

        if isinstance(targets_mix, tuple):
            targets1, targets2, lam = targets_mix
            loss = lam * criterion(outputs, targets1) + (1 - lam) * criterion(outputs, targets2)
        else:
            loss = criterion(outputs, targets_mix)

        loss.backward()
        optimizer.step()

        running_loss += loss.item() * inputs.size(0)
        _, predicted = outputs.max(1)

        if isinstance(targets_mix, tuple):
            correct += (lam * predicted.eq(targets1).sum().item() + (1 - lam) * predicted.eq(targets2).sum().item())
        else:
            correct += predicted.eq(targets_mix).sum().item()

        total += inputs.size(0)

    train_loss = running_loss / total
    train_acc = 100. * correct / total
    return train_loss, train_acc

def test():
    model.eval()
    running_loss = 0.0
    correct = 0
    total = 0

    with torch.no_grad():
        for inputs, targets in testloader:
            inputs, targets = inputs.to(device), targets.to(device)
            outputs = model(inputs)
            loss = criterion(outputs, targets)

            running_loss += loss.item() * inputs.size(0)
            _, predicted = outputs.max(1)
            correct += predicted.eq(targets).sum().item()
            total += inputs.size(0)

    val_loss = running_loss / total
    val_acc = 100. * correct / total
    return val_loss, val_acc

for epoch in range(50):
    train_loss, train_acc = train()
    val_loss, val_acc = test()
    print(f'Epoch {epoch+1:02d}: Train Loss={train_loss:.3f}, Train Acc={train_acc:.2f}%, Val Loss={val_loss:.3f}, Val Acc={val_acc:.2f}%')
Added Cutout augmentation to randomly mask parts of training images.
Added CutMix augmentation to combine pairs of images and labels during training.
Kept model architecture and optimizer unchanged.
Applied augmentations only on training data.
Results Interpretation

Before: Training accuracy 85%, Validation accuracy 70%, Training loss 0.45, Validation loss 1.10

After: Training accuracy 88%, Validation accuracy 79%, Training loss 0.38, Validation loss 0.85

Using Cutout and CutMix augmentations helps the model generalize better by reducing overfitting. These techniques create more diverse training examples, improving validation accuracy and lowering validation loss.
Bonus Experiment
Try using only CutMix or only Cutout separately and compare their effects on validation accuracy and training loss.
💡 Hint
Remove one augmentation at a time and observe if validation accuracy improves or worsens compared to using both together.

Practice

(1/5)
1. What is the main purpose of the Cutout technique in computer vision?
easy
A. To remove a patch from an image to improve model robustness
B. To combine two images by overlaying one on the other
C. To increase image resolution for better detail
D. To convert images to grayscale for simpler processing

Solution

  1. Step 1: Understand Cutout's function

    Cutout removes a small patch from an image during training to force the model to learn from other parts.
  2. Step 2: Compare options

    Only To remove a patch from an image to improve model robustness describes removing a patch to improve robustness. Others describe different techniques.
  3. Final Answer:

    To remove a patch from an image to improve model robustness -> Option A
  4. Quick Check:

    Cutout = patch removal [OK]
Hint: Cutout = cut a patch out of the image [OK]
Common Mistakes:
  • Confusing Cutout with CutMix
  • Thinking Cutout increases resolution
  • Assuming Cutout converts image color
2. Which of the following is the correct way to mix labels in CutMix augmentation in Python using PyTorch?
easy
A. new_label = label1 * lambda + label2 * (1 - lambda)
B. new_label = label1 + label2
C. new_label = label1 - label2
D. new_label = label1 / label2

Solution

  1. Step 1: Recall CutMix formula

    CutMix creates a new label by mixing two labels weighted by lambda and (1 - lambda).
  2. Step 2: Match formula to options

    new_label = label1 * lambda + label2 * (1 - lambda) correctly shows weighted sum of two labels. Others are incorrect operations.
  3. Final Answer:

    new_label = label1 * lambda + label2 * (1 - lambda) -> Option A
  4. Quick Check:

    CutMix = weighted sum [OK]
Hint: CutMix blends labels weighted by lambda [OK]
Common Mistakes:
  • Using addition without weights
  • Subtracting or dividing labels incorrectly
  • Confusing CutMix with Cutout
3. Given the following code snippet applying Cutout, what will be the shape of the output image tensor?
import torch
import torchvision.transforms as T

cutout = T.RandomErasing(p=1.0, scale=(0.1, 0.1))
image = torch.randn(3, 224, 224)
output = cutout(image)
print(output.shape)
medium
A. torch.Size([3, 112, 112])
B. torch.Size([1, 224, 224])
C. torch.Size([224, 224, 3])
D. torch.Size([3, 224, 224])

Solution

  1. Step 1: Understand RandomErasing effect

    RandomErasing (Cutout) masks part of the image but does not change its shape.
  2. Step 2: Check input and output shapes

    Input is (3, 224, 224), output remains same shape after Cutout.
  3. Final Answer:

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

    Cutout keeps shape same [OK]
Hint: Cutout masks but keeps image size [OK]
Common Mistakes:
  • Thinking Cutout changes image dimensions
  • Confusing channel order with shape
  • Assuming output is batch dimension added
4. The following code attempts to apply CutMix but causes an error. What is the main issue?
def cutmix(image1, image2, lam):
    bbx1, bby1, bbx2, bby2 = rand_bbox(image1.size(), lam)
    image1[:, bbx1:bbx2, bby1:bby2] = image2[:, bbx1:bbx2, bby1:bby2]
    return image1

# image1 and image2 are tensors of shape (3, 224, 448)
# lam is a float between 0 and 1
medium
A. rand_bbox returns coordinates in (x, y) but slicing expects (height, width)
B. rand_bbox expects size as tuple (H, W) instead of tensor.size() (C, H, W)
C. The function modifies image1 in-place which is not allowed
D. The function does not return the mixed label

Solution

  1. Step 1: Analyze rand_bbox input and slicing

    rand_bbox expects size as (height, width), but image1.size() returns (channels, height, width).
  2. Step 2: Identify mismatch in size argument

    Passing image1.size() (C, H, W) instead of (H, W) causes rand_bbox to compute wrong coordinates, leading to slicing errors.
  3. Final Answer:

    rand_bbox expects size as tuple (H, W) instead of tensor.size() (C, H, W) -> Option B
  4. Quick Check:

    Size argument mismatch = error [OK]
Hint: Pass (H, W) size to rand_bbox, not (C, H, W) [OK]
Common Mistakes:
  • Ignoring coordinate order difference
  • Assuming in-place modification causes error
  • Forgetting to return mixed label
5. You want to improve your image classifier's robustness by combining Cutout and CutMix. Which approach correctly applies both augmentations during training?
hard
A. Apply Cutout to each image separately, then combine them with CutMix
B. Apply Cutout only after training with CutMix is complete
C. Apply CutMix first to combine images, then apply Cutout to mask patches on the mixed image
D. Apply CutMix only on validation data and Cutout only on training data

Solution

  1. Step 1: Understand augmentation order

    CutMix combines two images, then Cutout can mask parts of the combined image to increase robustness.
  2. Step 2: Evaluate options for correct sequence

    Apply CutMix first to combine images, then apply Cutout to mask patches on the mixed image applies CutMix first, then Cutout, which is the correct order to augment the mixed image effectively.
  3. Final Answer:

    Apply CutMix first to combine images, then apply Cutout to mask patches on the mixed image -> Option C
  4. Quick Check:

    CutMix then Cutout = best practice [OK]
Hint: Mix images first, then mask patches [OK]
Common Mistakes:
  • Applying Cutout before mixing images
  • Using augmentations only on validation data
  • Applying augmentations after training
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