Behind the scenes.
The same loop that trained the model, and the few lines that turn an uploaded photo into Cat or Dog.
How it trains
This run used 500 cat photos and 500 dog photos. After a shuffle, 800 taught the model and 200 were held out to measure accuracy. The model never updates itself on those 200.
An epoch is one full pass through the 800 training photos. We ran 3 epochs, so it saw that set three times. A batch is 32 photos at a time. 800 divided by 32 is 25, so each epoch is 25 updates.
After epoch 1, validation accuracy was 86%. After epochs 2 and 3 it was 94%. The saved file keeps the weights from the best of those checks, which is the 94% version.
for epoch in range(1, args.epochs + 1):
train_loss, train_acc = run_epoch(
model, train_loader, criterion, optimizer, device, train=True
)
if val_loader is not None:
val_loss, val_acc = run_epoch(
model, val_loader, criterion, None, device, train=False
)
if val_acc >= best_val_acc:
best_val_acc = val_acc
best_state = {
k: v.detach().cpu().clone()
for k, v in model.state_dict().items()
}Inside one batch, the photos go through the network, the loss says how wrong the guess was, and only the training pass changes the weights. Validation runs the same loop with learning turned off, so it can count correct labels without memorizing those photos.
for images, labels in loader:
logits = model(images)
loss = criterion(logits, labels)
if train:
optimizer.zero_grad()
loss.backward()
optimizer.step()
preds = logits.argmax(dim=1)
correct += (preds == labels).sum()How a photo gets a label
On the Identify page, the classifier runs in the browser. The photo is resized to 224 by 224 pixels and scaled with the same color numbers used in training. The network returns two scores. Softmax turns those scores into percentages that add up to 100%. A smaller copy of the photo is then sent to Claude Haiku 4.5 for the short written report.
const MEAN = [0.485, 0.456, 0.406];
const STD = [0.229, 0.224, 0.225];
function softmax(logits) {
const max = Math.max(logits[0], logits[1]);
const exps = logits.map((value) => Math.exp(value - max));
const total = exps[0] + exps[1];
return exps.map((value) => value / total);
}