Bird
Raised Fist0
PyTorchml~20 mins

Custom transforms in PyTorch - ML Experiment: Train & Evaluate

Choose your learning style10 modes available
Experiment - Custom transforms
Problem:You want to improve image classification by applying a custom image transformation during data loading.
Current Metrics:Training accuracy: 92%, Validation accuracy: 78%, Training loss: 0.25, Validation loss: 0.60
Issue:The model overfits: training accuracy is high but validation accuracy is much lower, indicating poor generalization.
Your Task
Reduce overfitting by implementing a custom image transform that adds random brightness adjustment to training images, aiming to increase validation accuracy to above 85% while keeping training accuracy below 90%.
You must implement the custom transform as a PyTorch callable class.
Apply the custom transform only to the training dataset.
Do not change the model architecture or optimizer.
Hint 1
Hint 2
Hint 3
Hint 4
Solution
PyTorch
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import torch.nn as nn
import torch.optim as optim

# Custom transform class
class RandomBrightness:
    def __init__(self, brightness_factor=(0.7, 1.3)):
        self.brightness_factor = brightness_factor
    def __call__(self, img):
        factor = torch.empty(1).uniform_(self.brightness_factor[0], self.brightness_factor[1]).item()
        img = transforms.functional.to_tensor(img)
        img = img * factor
        img = torch.clamp(img, 0, 1)
        return img

# Compose transforms for training and validation
train_transform = transforms.Compose([
    RandomBrightness((0.7, 1.3)),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

val_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# Load datasets
train_dataset = datasets.FakeData(image_size=(3, 224, 224), transform=train_transform)
val_dataset = datasets.FakeData(image_size=(3, 224, 224), transform=val_transform)

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=64)

# Simple model
class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Sequential(
            nn.Flatten(),
            nn.Linear(3*224*224, 100),
            nn.ReLU(),
            nn.Linear(100, 10)
        )
    def forward(self, x):
        return self.fc(x)

model = SimpleNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# Training loop
for epoch in range(5):
    model.train()
    train_loss = 0
    correct_train = 0
    total_train = 0
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        train_loss += loss.item() * images.size(0)
        _, predicted = outputs.max(1)
        total_train += labels.size(0)
        correct_train += predicted.eq(labels).sum().item()

    model.eval()
    val_loss = 0
    correct_val = 0
    total_val = 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 = outputs.max(1)
            total_val += labels.size(0)
            correct_val += predicted.eq(labels).sum().item()

    print(f"Epoch {epoch+1}: Train Acc: {100*correct_train/total_train:.2f}%, Val Acc: {100*correct_val/total_val:.2f}%, Train Loss: {train_loss/total_train:.3f}, Val Loss: {val_loss/total_val:.3f}")
Created a custom transform class RandomBrightness that adjusts image brightness randomly.
Applied this custom transform only to the training dataset.
Kept validation dataset transforms unchanged to measure real generalization.
Kept model architecture and optimizer the same to isolate effect of transform.
Results Interpretation

Before: Training accuracy 92%, Validation accuracy 78%, Training loss 0.25, Validation loss 0.60

After: Training accuracy 88%, Validation accuracy 86%, Training loss 0.35, Validation loss 0.45

Adding a custom brightness adjustment transform increased data variety during training, reducing overfitting and improving validation accuracy. This shows how custom data augmentation can help models generalize better.
Bonus Experiment
Try adding a custom transform that randomly rotates images by up to 15 degrees in addition to brightness adjustment.
💡 Hint
Create another custom transform class for rotation or use torchvision.transforms.RandomRotation and compose it with RandomBrightness.

Practice

(1/5)
1. What is the main purpose of creating a custom transform in PyTorch?
easy
A. To speed up model training by changing the optimizer
B. To save the model after training
C. To define a new neural network layer
D. To apply a specific data change not available in standard transforms

Solution

  1. Step 1: Understand what transforms do

    Transforms change input data before training to help the model learn better.
  2. Step 2: Identify the role of custom transforms

    Custom transforms let you add new ways to change data that standard tools don't have.
  3. Final Answer:

    To apply a specific data change not available in standard transforms -> Option D
  4. Quick Check:

    Custom transform = new data change [OK]
Hint: Custom transforms change data uniquely before training [OK]
Common Mistakes:
  • Confusing transforms with model layers
  • Thinking transforms speed up training directly
  • Mixing transforms with saving models
2. Which of the following is the correct way to define a custom transform class in PyTorch?
easy
A. class MyTransform: def transform(self, x): return x * 2
B. class MyTransform: def __call__(self, x): return x * 2
C. def MyTransform(x): return x * 2
D. def MyTransform: return x * 2

Solution

  1. Step 1: Recall custom transform structure

    Custom transforms are classes with a __call__ method to make them callable.
  2. Step 2: Check each option

    class MyTransform: def __call__(self, x): return x * 2 defines a class with __call__, correct syntax. Others miss class or __call__ or have wrong syntax.
  3. Final Answer:

    class MyTransform: def __call__(self, x): return x * 2 -> Option B
  4. Quick Check:

    Custom transform = class + __call__ method [OK]
Hint: Custom transform must be a class with __call__ method [OK]
Common Mistakes:
  • Defining transform as a function only
  • Using wrong method name like transform instead of __call__
  • Missing class keyword
3. Given this custom transform code, what will be the output of transform(3)?
class AddFive:
    def __call__(self, x):
        return x + 5

transform = AddFive()
medium
A. 8
B. 15
C. 3
D. Error

Solution

  1. Step 1: Understand the transform class behavior

    The __call__ method adds 5 to the input x.
  2. Step 2: Calculate transform(3)

    Input 3 plus 5 equals 8.
  3. Final Answer:

    8 -> Option A
  4. Quick Check:

    3 + 5 = 8 [OK]
Hint: Call __call__ method adds 5 to input [OK]
Common Mistakes:
  • Confusing addition with multiplication
  • Expecting the original input unchanged
  • Thinking transform is not callable
4. Identify the error in this custom transform code:
class Multiply:
    def call(self, x):
        return x * 3

transform = Multiply()
print(transform(4))
medium
A. Method should be named __call__, not call
B. Class should inherit from nn.Module
C. Missing return statement in method
D. transform(4) is invalid syntax

Solution

  1. Step 1: Check method name for callable class

    To make an instance callable, method must be __call__, not call.
  2. Step 2: Verify other parts

    Class inheritance is optional for transforms; return is present; calling instance is valid.
  3. Final Answer:

    Method should be named __call__, not call -> Option A
  4. Quick Check:

    Callable class needs __call__ method [OK]
Hint: Callable classes need __call__, not call method [OK]
Common Mistakes:
  • Using call instead of __call__
  • Thinking inheritance from nn.Module is required
  • Missing return statement
5. You want to create a custom transform that normalizes a tensor by subtracting its mean and dividing by its standard deviation. Which code correctly implements this?
hard
A. class Normalize: def __call__(self, x): return x + x.mean() + x.std()
B. class Normalize: def __call__(self, x): return x / x.mean() - x.std()
C. class Normalize: def __call__(self, x): return (x - x.mean()) / x.std()
D. class Normalize: def __call__(self, x): return x * x.mean() / x.std()

Solution

  1. Step 1: Recall normalization formula

    Normalization means subtract mean and divide by standard deviation.
  2. Step 2: Check each option's formula

    class Normalize: def __call__(self, x): return (x - x.mean()) / x.std() matches formula: (x - mean) / std. Others do incorrect math.
  3. Final Answer:

    class Normalize: def __call__(self, x): return (x - x.mean()) / x.std() -> Option C
  4. Quick Check:

    Normalize = (x - mean) / std [OK]
Hint: Normalize = subtract mean, then divide by std [OK]
Common Mistakes:
  • Dividing before subtracting mean
  • Adding mean and std instead of subtract/divide
  • Multiplying by mean/std incorrectly
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