Capstone: Finishing Aria
Bringing it all together.
You've come a long way at Neuron Labs. Your lead has one last job. Give Aria a proper pair of eyes, using everything that you've learned, and then write the report. There's no more hand holding. This is how a professional puts a small project together.

- One last job
- Give Aria real eyes
- How a professional works
The plan
Here's the plan. Pretrained network, because we have little data. Replace the head, adding dropout. Freeze the body. Use label smoothing, a small trick that stops the model from being overconfident. Use a one cycle learning rate schedule. Then measure accuracy per class, and save the result. Each of those decisions comes from a lesson in this course.
The final model
Here's the entire project, on one screen. Pretrained weights, a new head, and a frozen body. AdamW and a one cycle schedule. Label smoothing inside the loss. After training, we measure overall accuracy, along with the hardest classes. Then we save the weights. Run it. Very strong accuracy on real photos, with only fifteen hundred images, and two short epochs. And now we know exactly which classes to work on next. Every line of this program was a lesson.
import torch
from torch import nn
from torchvision import datasets, models
weights = models.ResNet18_Weights.DEFAULT
tf = weights.transforms()
names = datasets.CIFAR10("data", train=False, download=True).classes
train = datasets.CIFAR10("data", train=True, download=True, transform=tf)
test = datasets.CIFAR10("data", train=False, download=True, transform=tf)
train = torch.utils.data.Subset(train, range(1500))
test = torch.utils.data.Subset(test, range(600))
model = models.resnet18(weights=weights)
model.fc = nn.Sequential(nn.Dropout(0.2), nn.Linear(512, 10))
for p in model.parameters():
p.requires_grad = False
for p in model.fc.parameters():
p.requires_grad = True
opt = torch.optim.AdamW(model.fc.parameters(), lr=1e-3)
sched = torch.optim.lr_scheduler.OneCycleLR(opt, 3e-3, total_steps=48)
dl = torch.utils.data.DataLoader(train, batch_size=64, shuffle=True)
tl = torch.utils.data.DataLoader(test, batch_size=128)
for epoch in range(2):
model.train()
for x, y in dl:
loss = nn.functional.cross_entropy(model(x), y, label_smoothing=0.1)
opt.zero_grad(); loss.backward(); opt.step(); sched.step()
model.eval()
hits, per, tot = 0, [0] * 10, [0] * 10
with torch.no_grad():
for x, y in tl:
p = model(x).argmax(1)
hits += (p == y).sum().item()
for a, b in zip(p.tolist(), y.tolist()):
tot[b] += 1; per[b] += a == b
print(f"Accuracy {hits / len(test):.3f}")
worst = sorted(range(10), key=lambda i: per[i] / max(1, tot[i]))[:3]
print("Hardest classes:", [names[i] for i in worst])
torch.save(model.state_dict(), "aria_eyes.pt")
print("Saved aria_eyes.pt")Accuracy 0.610 Hardest classes: ['deer', 'cat', 'dog'] Saved aria_eyes.pt
The report
Now the report for your lead, in plain words. What we built, an image model, using transfer learning. How well it works, including which classes are the hardest. The limits, such as the fact that it was only tested on these ten classes. And what comes next, like fine tuning the top layers, and collecting more examples of the hard classes. That's the difference between a model and a project.
Summary
Aria has real eyes, and you've gone from a single tensor to a working system. You've earned this moment. Now let's check how much has stuck. Next, your final exam.
Key takeaways- Pretrained beats from-scratch on small data
- Every line was a lesson
- Report the limits