221 KiB
221 KiB
In [ ]:
points = 64In [1]:
### DO NOT EDIT THIS CELL ###
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.transforms as transforms
import torchvision
from torch.utils.data import Subset, DataLoader, TensorDataset
import numpy as np
import random
import matplotlib.pyplot as plt
from tqdm import tqdm
# Set random seed
def set_seed(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)
set_seed(0)
g = torch.Generator().manual_seed(42)
device = torch.device("cpu")
#############################In [22]:
class NeuralNetworkBroken(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.model = nn.Sequential(
nn.Linear(input_dim, hidden_dim), # 2) input_din -> input_dim, 3) add hidden_dim
nn.Linear(hidden_dim, hidden_dim), # 1) add comma, 6) correct output dimension
nn.Linear(hidden_dim, output_dim),
)
def forward(self, x):
return self.model(x) # 5) change from sigmoid -> x
device = device
model = NeuralNetworkBroken(input_dim=10, hidden_dim=8, output_dim=2)
model.to(device) # 4) move to deviceOut [22]:
NeuralNetworkBroken(
(model): Sequential(
(0): Linear(in_features=10, out_features=8, bias=True)
(1): Linear(in_features=8, out_features=8, bias=True)
(2): Linear(in_features=8, out_features=2, bias=True)
)
)In [23]:
### DO NOT EDIT THIS CELL ###
X = torch.randn(32, 10)
y = torch.randint(0, 2, (32,))
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=32)
# Model setup
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
# Minimal training loop to trigger crash
model.train()
for inputs, targets in dataloader:
outputs = model(inputs)
loss = loss_fn(outputs, targets)
break
print('✅ Success!')
#############################✅ Success!
In [ ]:
## DO NOT EDIT THIS CELL
# number of samples
n_train = 100
n_val = 100
# define random training images (28x28) and labels (between 0-9)
X_train = torch.randn(n_train, 1, 28, 28)
y_train = torch.randint(0, 10, (n_train,))
# define random validation images (28x28) and labels (between 0-9)
X_val = torch.randn(n_val, 1, 28, 28)
y_val = torch.randint(0, 10, (n_val,))
# create datasets
train_dataset = TensorDataset(X_train, y_train)
val_dataset = TensorDataset(X_train, y_train)
# create data loaders
train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=10, shuffle=False)In [ ]:
class MLP(nn.Module):
def __init__(self):
super().__init__()
self.model = nn.Sequential(
nn.Flatten(),
nn.Linear(28*28, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 10)
)
def forward(self, x):
return self.model(x)In [ ]:
model = MLP()
model.to(device)
print(model)MLP(
(model): Sequential(
(0): Flatten(start_dim=1, end_dim=-1)
(1): Linear(in_features=784, out_features=128, bias=True)
(2): ReLU()
(3): Linear(in_features=128, out_features=64, bias=True)
(4): ReLU()
(5): Linear(in_features=64, out_features=10, bias=True)
)
)
In [ ]:
### GRADING TOOL ###
# paste printed output here
stu_model="""
CNNModel(
(features): Sequential(
(0): Conv2d(1, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2))
(5): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU()
(7): Flatten(start_dim=1, end_dim=-1)
)
(classifier): Sequential(
(0): LazyLinear(in_features=0, out_features=128, bias=True)
(1): ReLU()
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
)
)
"""
# reference to compare to
ref_model = """
MLP(
(model): Sequential(
(0): Flatten(start_dim=1, end_dim=-1)
(1): Linear(in_features=784, out_features=128, bias=True)
(2): ReLU()
(3): Linear(in_features=128, out_features=64, bias=True)
(4): ReLU()
(5): Linear(in_features=64, out_features=10, bias=True)
)
)
"""
def compare_model_strings(stu, ref):
if ref.strip() == stu.strip():
print('✅ Correct')
else:
print('❌ Incorrect')
# Optional: show the first point of difference
import difflib
diff = difflib.unified_diff(
ref.strip().splitlines(),
stu.strip().splitlines(),
fromfile='Reference',
tofile='Student',
lineterm=''
)
print("\n".join(diff))
# Run the comparison
compare_model_strings(stu_model, ref_model)In [ ]:
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
n_epochs = 20In [ ]:
### DO NOT EDIT THIS CELL ###
def train_epoch(model, train_dataloader, optimizer, loss_fn):
losses = []
correct_predictions = 0
# Iterate mini batches over training dataset
for features, labels in tqdm(train_dataloader):
features = features.to(device)
labels = labels.to(device)
# Run predictions
output = model(features)
# Set gradients to zero
optimizer.zero_grad()
# Compute loss
loss = loss_fn(output, labels)
# Backpropagate (compute gradients)
loss.backward()
# Make an optimization step (update parameters)
optimizer.step()
# Log metrics
losses.append(loss.item())
predicted_labels = output.argmax(dim=1)
correct_predictions += (predicted_labels == labels).sum().item()
accuracy = 100.0 * correct_predictions / len(train_dataloader.dataset)
# Return loss values for each iteration and accuracy
mean_loss = np.array(losses).mean()
return mean_loss, accuracy
def evaluate(model, dataloader, loss_fn):
losses = []
correct_predictions = 0
with torch.no_grad():
for features, labels in dataloader:
features = features.to(device)
labels = labels.to(device)
# Run predictions
output = model(features)
# Compute loss
loss = loss_fn(output, labels)
# Save metrics
predicted_labels = output.argmax(dim=1)
correct_predictions += (predicted_labels == labels).sum().item()
losses.append(loss.item())
mean_loss = np.array(losses).mean()
accuracy = 100.0 * correct_predictions / len(dataloader.dataset)
# Return mean loss and accuracy
return mean_loss, accuracy
def train(model, train_dataloader, val_dataloader, optimizer, n_epochs, loss_fn):
# We will monitor loss functions as the training progresses
train_losses = []
val_losses = []
train_accuracies = []
val_accuracies = []
for epoch in range(n_epochs):
model.train()
train_loss, train_accuracy = train_epoch(model, train_dataloader, optimizer, loss_fn)
model.eval()
val_loss, val_accuracy = evaluate(model, val_dataloader, loss_fn)
train_losses.append(train_loss)
val_losses.append(val_loss)
train_accuracies.append(train_accuracy)
val_accuracies.append(val_accuracy)
print('Epoch {}/{}: train_loss: {:.4f}, train_accuracy: {:.4f}, val_loss: {:.4f}, val_accuracy: {:.4f}'.format(epoch+1, n_epochs,
train_losses[-1],
train_accuracies[-1],
val_losses[-1],
val_accuracies[-1]))
return train_losses, val_losses, train_accuracies, val_accuracies
def plot(train_losses, val_losses, train_accuracies, val_accuracies, title):
plt.figure()
plt.plot(np.arange(len(train_losses)), train_losses)
plt.plot(np.arange(len(val_losses)), val_losses)
plt.legend(['train_loss', 'val_loss'])
plt.xlabel('epoch')
plt.xticks(np.arange(len(train_losses)), np.arange(1, len(train_losses)+1))
plt.ylabel('loss value')
plt.title('{}: Train/val loss'.format(title));
plt.figure()
plt.plot(np.arange(len(train_accuracies)), train_accuracies)
plt.plot(np.arange(len(val_accuracies)), val_accuracies)
plt.legend(['train_acc', 'val_acc'])
plt.xlabel('epoch')
plt.xticks(np.arange(len(train_losses)), np.arange(1, len(train_losses)+1))
plt.ylabel('accuracy')
plt.title('{}: Train/val accuracy'.format(title));
In [ ]:
# train model
train_losses, val_losses, train_acc, val_acc = train(model, train_loader, val_loader, optimizer, n_epochs, criterion)100%|██████████| 10/10 [00:00<00:00, 48.54it/s]
Epoch 1/20: train_loss: 2.3117, train_accuracy: 10.0000, val_loss: 2.0470, val_accuracy: 55.0000
100%|██████████| 10/10 [00:00<00:00, 361.37it/s]
Epoch 2/20: train_loss: 1.9682, train_accuracy: 61.0000, val_loss: 1.7530, val_accuracy: 83.0000
100%|██████████| 10/10 [00:00<00:00, 527.82it/s]
Epoch 3/20: train_loss: 1.6262, train_accuracy: 83.0000, val_loss: 1.3396, val_accuracy: 92.0000
100%|██████████| 10/10 [00:00<00:00, 481.36it/s]
Epoch 4/20: train_loss: 1.1762, train_accuracy: 95.0000, val_loss: 0.8571, val_accuracy: 99.0000
100%|██████████| 10/10 [00:00<00:00, 560.92it/s]
Epoch 5/20: train_loss: 0.7054, train_accuracy: 99.0000, val_loss: 0.4589, val_accuracy: 99.0000
100%|██████████| 10/10 [00:00<00:00, 547.37it/s]
Epoch 6/20: train_loss: 0.3450, train_accuracy: 100.0000, val_loss: 0.2015, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 512.14it/s]
Epoch 7/20: train_loss: 0.1437, train_accuracy: 100.0000, val_loss: 0.0839, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 501.06it/s]
Epoch 8/20: train_loss: 0.0645, train_accuracy: 100.0000, val_loss: 0.0363, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 498.08it/s]
Epoch 9/20: train_loss: 0.0279, train_accuracy: 100.0000, val_loss: 0.0191, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 444.17it/s]
Epoch 10/20: train_loss: 0.0159, train_accuracy: 100.0000, val_loss: 0.0121, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 526.41it/s]
Epoch 11/20: train_loss: 0.0109, train_accuracy: 100.0000, val_loss: 0.0090, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 550.59it/s]
Epoch 12/20: train_loss: 0.0083, train_accuracy: 100.0000, val_loss: 0.0073, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 573.20it/s]
Epoch 13/20: train_loss: 0.0069, train_accuracy: 100.0000, val_loss: 0.0062, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 556.41it/s]
Epoch 14/20: train_loss: 0.0059, train_accuracy: 100.0000, val_loss: 0.0053, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 569.38it/s]
Epoch 15/20: train_loss: 0.0051, train_accuracy: 100.0000, val_loss: 0.0047, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 521.82it/s]
Epoch 16/20: train_loss: 0.0045, train_accuracy: 100.0000, val_loss: 0.0042, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 603.51it/s]
Epoch 17/20: train_loss: 0.0040, train_accuracy: 100.0000, val_loss: 0.0037, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 515.83it/s]
Epoch 18/20: train_loss: 0.0036, train_accuracy: 100.0000, val_loss: 0.0033, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 449.32it/s]
Epoch 19/20: train_loss: 0.0032, train_accuracy: 100.0000, val_loss: 0.0030, val_accuracy: 100.0000
100%|██████████| 10/10 [00:00<00:00, 431.87it/s]
Epoch 20/20: train_loss: 0.0029, train_accuracy: 100.0000, val_loss: 0.0028, val_accuracy: 100.0000
In [ ]:
# visualize results
plot(train_losses, val_losses, train_acc, val_acc, title='MLP')
In [ ]:
# evaluate on last model
val_loss, val_accuracy = evaluate(model, val_loader, criterion)
print('MLP. Validation loss: {:.2f}, validation accuracy: {:.2f}'.format(val_loss, val_accuracy))
MLP. Validation loss: 0.00, validation accuracy: 100.00
In [ ]:
# number of samples
n_train = 100
n_val = 100
# define random training images (28x28) and labels (between 0-9)
X_train = torch.randn(n_train, 1, 28, 28)
y_train = torch.randint(0, 10, (n_train,))
# define random validation images (28x28) and labels (between 0-9)
X_val = torch.randn(n_val, 1, 28, 28)
y_val = torch.randint(0, 10, (n_val,))
# create datasets
train_dataset = TensorDataset(X_train, y_train)
val_dataset = TensorDataset(X_val, y_val) # data leakage fixed!
# create data loaders
train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=10, shuffle=False)In [ ]:
# train model
train_losses, val_losses, train_acc, val_acc = train(model, train_loader, val_loader, optimizer, n_epochs, criterion)
# visualize results
plot(train_losses, val_losses, train_acc, val_acc, title='MLP')100%|██████████| 10/10 [00:00<00:00, 131.55it/s]
Epoch 1/20: train_loss: 0.0008, train_accuracy: 100.0000, val_loss: 2.9849, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 189.46it/s]
Epoch 2/20: train_loss: 0.0008, train_accuracy: 100.0000, val_loss: 2.9878, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 191.51it/s]
Epoch 3/20: train_loss: 0.0007, train_accuracy: 100.0000, val_loss: 2.9906, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 247.30it/s]
Epoch 4/20: train_loss: 0.0007, train_accuracy: 100.0000, val_loss: 2.9933, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 314.87it/s]
Epoch 5/20: train_loss: 0.0007, train_accuracy: 100.0000, val_loss: 2.9960, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 253.38it/s]
Epoch 6/20: train_loss: 0.0007, train_accuracy: 100.0000, val_loss: 2.9987, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 231.38it/s]
Epoch 7/20: train_loss: 0.0006, train_accuracy: 100.0000, val_loss: 3.0012, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 315.04it/s]
Epoch 8/20: train_loss: 0.0006, train_accuracy: 100.0000, val_loss: 3.0037, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 351.10it/s]
Epoch 9/20: train_loss: 0.0006, train_accuracy: 100.0000, val_loss: 3.0063, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 527.42it/s]
Epoch 10/20: train_loss: 0.0006, train_accuracy: 100.0000, val_loss: 3.0089, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 521.58it/s]
Epoch 11/20: train_loss: 0.0006, train_accuracy: 100.0000, val_loss: 3.0113, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 525.58it/s]
Epoch 12/20: train_loss: 0.0006, train_accuracy: 100.0000, val_loss: 3.0139, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 498.42it/s]
Epoch 13/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0164, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 512.28it/s]
Epoch 14/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0188, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 508.19it/s]
Epoch 15/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0211, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 511.06it/s]
Epoch 16/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0233, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 420.79it/s]
Epoch 17/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0257, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 531.41it/s]
Epoch 18/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0280, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 552.12it/s]
Epoch 19/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0302, val_accuracy: 11.0000
100%|██████████| 10/10 [00:00<00:00, 403.98it/s]
Epoch 20/20: train_loss: 0.0005, train_accuracy: 100.0000, val_loss: 3.0325, val_accuracy: 11.0000
In [ ]:
# evaluate on last model
val_loss, val_accuracy = evaluate(model, val_loader, criterion)
print('MLP. Validation loss: {:.2f}, validation accuracy: {:.2f}'.format(val_loss, val_accuracy))
MLP. Validation loss: 3.03, validation accuracy: 11.00
In [ ]:
## DO NOT EDIT THIS CELL
points64