Separate training and evaluation in image classification
English translation
Create your own copy to run, edit, and record results.
- Name: ⟦Enter here⟧
- Student ID: ⟦Enter here⟧
- Run Code Cells: ▶ or
Shift+Enter, in order from the top. - Edit a Text Cell: double-click → write
your response→Shift+Enter. - Response cell: enter your own code.
- After changing values: rerun that cell and the relevant cells below it.
Local setup
- PyTorch and torchvision: install compatible versions together. Verified with Python 3.11 · PyTorch 2.12.1 · torchvision 0.27.1.
- MNIST: internet access is required on the first run. Download location:
mnist_data. - If
CERTIFICATE_VERIFY_FAILEDoccurs with python.org Python on a Mac, runInstall Certificates.commandin the corresponding Python folder under Applications and try again.
1. MNIST and execution scale
Keep QUICK_RUN=True for this exercise, using 1200 training samples and 300 evaluation samples. The first run downloads MNIST and requires internet access. When training on the full dataset, also record the sample count used.
A. Baseline example · Read and run first
import sys, numpy as np, matplotlib.pyplot as plt
import torch
from torch import nn
import torch.nn.functional as F
torch.manual_seed(42)
torch.set_num_threads(2)
print('Python:', sys.version.split()[0], '| PyTorch:', torch.__version__)
import torchvision
from torchvision import datasets,transforms
from torch.utils.data import DataLoader,Subset
QUICK_RUN=True # Change yourself: False for the full dataset
train_data=datasets.MNIST('mnist_data',train=True,download=True,transform=transforms.ToTensor())
test_data=datasets.MNIST('mnist_data',train=False,download=True,transform=transforms.ToTensor())
train_used=Subset(train_data,range(1200)) if QUICK_RUN else train_data
test_used=Subset(test_data,range(300)) if QUICK_RUN else test_data
train_loader=DataLoader(train_used,batch_size=64,shuffle=True,generator=torch.Generator().manual_seed(42))
test_loader=DataLoader(test_used,batch_size=128,shuffle=False)
print('Mode:', 'QUICK' if QUICK_RUN else 'FULL','train:',len(train_used),'test:',len(test_used))
img,label=train_data[0];print(img.shape,label);plt.imshow(img.squeeze(),cmap='gray');plt.show()
B. Hands-on activities
- Run the data preparation cell. Print the image shape and Label from
train_data[0]and describe the image's three axes. - Iterate through all of
train_loaderand collect image counts per Batch in a list. Record the first Batch size, last Batch size, and total Batch count. - Calculate the expected last Batch size from
len(train_used)and Batch Size 64 and compare it with the printed result.
Your prediction may describe a shape or an increasing/decreasing trend rather than an exact number. Use the example above to write code; answer explanation-only items in the Text Cell below.
# ✍ Write your own: add your code below this cell.
# You may add Code Cells if the code gets long.
C. Your record · Double-click this Text Cell to edit
- Conditions checked or changed: ⟦Enter your response⟧
- Prediction before execution: ⟦Enter your response⟧
- Value checked in the actual output/plot: ⟦Enter your response⟧
- Difference from prediction and explanation: ⟦Enter your response⟧
2. Train and evaluate the CNN
Evaluation does not update parameters.
A. Baseline example · Read and run first
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.layer1=nn.Sequential(nn.Conv2d(1,32,3,padding=1),nn.ReLU(),nn.MaxPool2d(2))
self.layer2=nn.Sequential(nn.Conv2d(32,64,3,padding=1),nn.ReLU(),nn.MaxPool2d(2))
self.layer3=nn.Sequential(nn.Conv2d(64,128,3,padding=1),nn.ReLU(),nn.MaxPool2d(2,padding=1))
self.fc1=nn.Linear(128*4*4,625);self.relu=nn.ReLU();self.dropout=nn.Dropout();self.fc2=nn.Linear(625,10)
def forward(self,x):
x=self.layer3(self.layer2(self.layer1(x)));x=x.flatten(1)
return self.fc2(self.dropout(self.relu(self.fc1(x))))
model=CNN();optimizer=torch.optim.Adam(model.parameters(),lr=.001)
epochs=2 if QUICK_RUN else 5
loss_history=[];accuracy_history=[]
for epoch in range(epochs):
model.train();total_loss=0.;correct=0;count=0
for xb,yb in train_loader:
logits=model(xb);loss=F.cross_entropy(logits,yb)
optimizer.zero_grad();loss.backward();optimizer.step()
total_loss+=loss.item()*len(yb);correct+=(logits.argmax(1)==yb).sum().item();count+=len(yb)
loss_history.append(total_loss/count);accuracy_history.append(correct/count)
print(epoch+1,loss_history[-1],accuracy_history[-1])
model.eval();correct=0;count=0;examples=[];mistakes=[]
with torch.no_grad():
for xb,yb in test_loader:
pred=model(xb).argmax(1);correct+=(pred==yb).sum().item();count+=len(yb)
for image,truth,guess in zip(xb,yb,pred):
item=(image.squeeze(),truth.item(),guess.item())
if len(examples)<10:examples.append(item)
if truth!=guess and len(mistakes)<5:mistakes.append(item)
print('TEST:',correct/count,'samples:',count)
B. Hands-on activities
- Run the data cell with
QUICK_RUN=True, followed by the CNN training and evaluation cells. Record the training epochs, training image count, and evaluation image count. - Find
model.train()in the training code andmodel.eval()andtorch.no_grad()in the evaluation code. Explain in one sentence each whether it controls Dropout behavior or Gradient recording.
To compare with the full dataset, change to QUICK_RUN=False and rerun from the data cell.
Your prediction may describe a shape or an increasing/decreasing trend rather than an exact number. Use the example above to write code; answer explanation-only items in the Text Cell below.
# ✍ Write your own: add your code below this cell.
# You may add Code Cells if the code gets long.
C. Your record · Double-click this Text Cell to edit
- Conditions checked or changed: ⟦Enter your response⟧
- Prediction before execution: ⟦Enter your response⟧
- Value checked in the actual output/plot: ⟦Enter your response⟧
- Difference from prediction and explanation: ⟦Enter your response⟧
3. Plots and misclassifications
Inspect actual images alongside predictions.
A. Baseline example · Read and run first
fig,axes=plt.subplots(1,2,figsize=(9,3));axes[0].plot(loss_history);axes[1].plot(accuracy_history)
for ax,yl in zip(axes,['train loss','train accuracy']):ax.set(xlabel='Epoch',ylabel=yl);ax.grid()
plt.show()
for title,items in [('test examples',examples),('misclassified',mistakes)]:
if not items:print(title,': none in evaluated subset');continue
fig,axes=plt.subplots(1,len(items),figsize=(2*len(items),2),squeeze=False)
for ax,(image,truth,guess) in zip(axes[0],items):ax.imshow(image,cmap='gray');ax.set_title(f'T:{truth} P:{guess}');ax.axis('off')
fig.suptitle(title);plt.show()
B. Hands-on activities
- Compare the actual and predicted Labels in the visualizations of
examplesandmistakesagainst each image. For correct and incorrect examples, record which digit was predicted as which, and describe noticeable features in misclassified images. - Alongside the recorded Test Accuracy, write the number of evaluation images. Distinguish evaluation on a subset under
QUICK_RUN=Truefrom evaluation on the entire Test Set.
Your prediction may describe a shape or an increasing/decreasing trend rather than an exact number. Use the example above to write code; answer explanation-only items in the Text Cell below.
# ✍ Write your own: add your code below this cell.
# You may add Code Cells if the code gets long.
C. Your record · Double-click this Text Cell to edit
- Conditions checked or changed: ⟦Enter your response⟧
- Prediction before execution: ⟦Enter your response⟧
- Value checked in the actual output/plot: ⟦Enter your response⟧
- Difference from prediction and explanation: ⟦Enter your response⟧
Practice through repetition · Reason before running
Summarize the three conditions compared in the preceding activities in this table. You do not need to repeat experiments already completed. If comparisons are insufficient, supplement them by changing just one condition.
| Changed condition | Predicted shape/value/result | Actual result | Cause of any error |
|---|---|---|---|
| ⟦Enter your response 1⟧ | ⟦Enter your response⟧ | ⟦Enter your response⟧ | ⟦Enter your response⟧ |
| ⟦Enter your response 2⟧ | ⟦Enter your response⟧ | ⟦Enter your response⟧ | ⟦Enter your response⟧ |
| ⟦Enter your response 3⟧ | ⟦Enter your response⟧ | ⟦Enter your response⟧ | ⟦Enter your response⟧ |
# ✍ Code for repeated experiments
Optional exercise · Find the cause of an error and fix the code
Evaluating the same image twice with Dropout enabled
After training and evaluating the CNN, add a Code Cell at the bottom of the notebook and run the following code.
debug_image = test_used[0][0].unsqueeze(0) for training in [True, False]: model.train(training) with torch.no_grad(): first = model(debug_image) second = model(debug_image) print("train" if training else "eval", "maximum difference:", (first - second).abs().max().item()) model.eval()In a Text Cell, record the maximum absolute difference for each mode and explain why the same image produces different outputs in terms of Dropout. This code evaluates without computing Gradients or updating weights and ends in
evalmode.
Check before saving
- Check the response fields, outputs, and plots, then run all cells in order in a fresh runtime.
- Save to your Drive, then download using
.ipynb.
Include code, actual outputs and plots, and your own interpretation. Submit through KLAS; check the semester schedule and latest announcements for the deadline.
Filename: Name_StudentID_Week10.ipynb.