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/plotProject 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
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
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.svgKey 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