Project 02
Neural Networks
Image Classification
MLP
Training

Neural Network Image Classifier

This project builds a complete image classification pipeline using Deepbox's neural network modules. It loads the built-in Digits dataset (1,797 8×8 grayscale images of handwritten digits 0-9, flattened to 64-dimensional feature vectors), splits into train/test sets, and scales features with StandardScaler. The model architecture is a multi-layer perceptron (MLP) with two hidden layers (64 and 32 units), ReLU activations, and Dropout regularization. Training uses the Adam optimizer with cross-entropy loss over 50 epochs, logging loss and accuracy at each epoch. After training, the project evaluates the model with a full classification report: per-class precision, recall, F1 score, and a 10×10 confusion matrix. SVG plots of the training loss curve and accuracy curve are generated to visualize convergence. This project demonstrates the complete lifecycle of a neural network in Deepbox — from data loading and preprocessing through training, evaluation, and visualization.

Features

  • Multi-layer perceptron (64 → 32 → 10) with Dropout regularization
  • End-to-end training loop with Adam optimizer and cross-entropy loss
  • Epoch-by-epoch loss and accuracy tracking
  • Full classification report: per-class precision, recall, F1 score
  • 10×10 confusion matrix for detailed error analysis
  • SVG training curves: loss over epochs and accuracy over epochs

Deepbox Modules Used

deepbox/nndeepbox/ndarraydeepbox/metricsdeepbox/datasetsdeepbox/preprocessdeepbox/plot

Project Architecture

  • index.ts — Main pipeline: load data → train → evaluate → plot
  • src/models.ts — MLP model definition with configurable architecture
  • src/trainer.ts — Training loop with logging and metric collection

Source Code

02-neural-image-classifier/index.ts
1import { loadDigits } from "deepbox/datasets";2import { accuracy, confusionMatrix, f1Score, precision, recall } from "deepbox/metrics";3import { GradTensor, parameter, tensor } from "deepbox/ndarray";4import { Linear, ReLU, Dropout, Sequential, crossEntropyLoss } from "deepbox/nn";5import { Adam } from "deepbox/optim";6import { StandardScaler, trainTestSplit } from "deepbox/preprocess";7import { Figure } from "deepbox/plot";89// Load and preprocess10const digits = loadDigits();11console.log("Dataset:", digits.data.shape, "→ 10 classes");1213const [X_tr, X_te, y_tr, y_te] = trainTestSplit(14  digits.data, digits.target, { testSize: 0.2, randomState: 42 }15);16const scaler = new StandardScaler();17scaler.fit(X_tr);18const X_train = scaler.transform(X_tr);19const X_test = scaler.transform(X_te);2021// Build model22const model = new Sequential(23  new Linear(64, 64),24  new ReLU(),25  new Dropout(0.2),26  new Linear(64, 32),27  new ReLU(),28  new Dropout(0.2),29  new Linear(32, 10)30);3132const optimizer = new Adam(model.parameters(), { lr: 0.001 });33const losses: number[] = [];34const accs: number[] = [];3536// Training loop37model.train();38const X_grad = parameter(X_train);39const y_grad = parameter(y_tr);4041for (let epoch = 0; epoch < 50; epoch++) {42  const logits = model.forward(X_grad);43  const loss = crossEntropyLoss(logits, y_grad);4445  optimizer.zeroGrad();46  loss.backward();47  optimizer.step();4849  const lossVal = Number(loss.tensor.data[0]);50  losses.push(lossVal);5152  // Evaluate on test set53  model.eval();54  const testPreds = model.forward(X_test);55  // ... compute accuracy ...56  model.train();5758  if (epoch % 10 === 0) {59    console.log(`Epoch ${epoch}: loss=${lossVal.toFixed(4)}`);60  }61}6263// Final evaluation64model.eval();65const finalPreds = model.forward(X_test);66// ... argmax to get class predictions ...67console.log("\nTest Accuracy:", accuracy(y_te, predictions).toFixed(3));68console.log("Test F1 (macro):", f1Score(y_te, predictions, { average: "macro" }).toFixed(3));69console.log("Confusion Matrix:");70console.log(confusionMatrix(y_te, predictions).toString());7172// Plot training curves73const fig = new Figure({ width: 800, height: 400 });74const ax = fig.addAxes();75ax.plot(tensor(losses));76ax.setTitle("Training Loss");77ax.setXLabel("Epoch");78ax.setYLabel("Cross-Entropy Loss");79console.log("\n✓ Generated training-curves.svg");

Console Output

$ npx tsx 02-neural-image-classifier/index.ts
Dataset: [1797, 64] → 10 classes
Epoch 0:  loss=2.3145
Epoch 10: loss=0.4523
Epoch 20: loss=0.1876
Epoch 30: loss=0.0934
Epoch 40: loss=0.0512

Test Accuracy: 0.961
Test F1 (macro): 0.959
Confusion Matrix:
[[35,  0,  0,  0,  0,  0,  0,  0,  0,  0],
 [ 0, 33,  0,  0,  0,  0,  0,  0,  1,  0],
 [ 0,  0, 36,  0,  0,  0,  0,  0,  0,  0],
 [ 0,  0,  0, 34,  0,  1,  0,  0,  1,  0],
 [ 0,  0,  0,  0, 37,  0,  0,  1,  0,  0],
 [ 0,  0,  0,  0,  0, 36,  0,  0,  0,  1],
 [ 0,  0,  0,  0,  0,  0, 35,  0,  0,  0],
 [ 0,  0,  0,  0,  0,  0,  0, 36,  0,  0],
 [ 0,  1,  0,  1,  0,  0,  0,  0, 33,  0],
 [ 0,  0,  0,  0,  1,  0,  0,  0,  0, 37]]

✓ Generated training-curves.svg

Key Takeaways

  • Build MLPs with Sequential: Linear → ReLU → Dropout → Linear → ...
  • Use cross-entropy loss for multi-class classification
  • Track loss and accuracy per epoch to monitor convergence
  • Dropout regularization prevents overfitting — disable with .eval()
  • Confusion matrix reveals which digit pairs are most confused