ML Strategy, Error Analysis, and Simple ML Serving
| Part | Topic |
|---|---|
| 1 | Why ML strategy, and orthogonalization |
| 2 | Setting the goal: metrics and data splits |
| 3 | Human-level performance, bias and variance |
| 4 | Error analysis and prioritization |
| 5 | When training data and real data differ |
| 6 | From model to application: simple ML serving |
You built a cat classifier. After some training: 90% accuracy.

Not good enough for your application. What now?
Key idea
ML strategy is a set of quick, reliable ways to figure out which ideas are worth pursuing and which you can safely discard.
Old TV sets were easy to tune because each knob did one thing. Same in a car: steering for direction, pedals for speed.
Orthogonalization
Design your workflow so that each “knob” fixes one specific problem.


For a system to work in practice, four things must be true in order:

If stage 2 fails, stage 3 knobs will not help.
| Problem | Typical knobs |
|---|---|
| Poor fit on training set | Bigger model, train longer, better optimizer, better architecture |
| Poor fit on dev set | Regularization, more training data |
| Poor fit on test set | Get a bigger dev set (you over-tuned to it) |
| Poor real-world performance | Change the dev/test set or the metric (they aim at the wrong target) |
Your team’s model fits the training data almost perfectly, but performs poorly on new data.
You compare two classifiers using precision and recall:
| Classifier | Precision | Recall | Which is better? |
|---|---|---|---|
| A | 95% | 90% | ? |
| B | 98% | 85% | ? |
Two numbers: no clear winner, slow decisions. Combine them into one number, for example F1 (a balanced combination of precision and recall):
| Classifier | F1 score |
|---|---|
| A | 92.4% |
| B | 91.0% |
Applied ML is an empirical loop.

Not every metric should be maximized. Often you want:
Maximize one metric, subject to constraints on the others.
| Classifier | Accuracy | Running time |
|---|---|---|
| A | 90% | 80 ms |
| B | 92% | 95 ms |
| C | 95% | 1500 ms |
Rule: maximize accuracy (optimizing) subject to running time below 100 ms (satisficing). Winner: B.
Quick check (1 minute): for a voice assistant wake word, which metric is optimizing and which is satisficing?
| Set | Purpose | Used how often? |
|---|---|---|
| Training set | Fit the model’s parameters | Constantly |
| Dev set | Compare models, tune choices | Frequently |
| Test set | Final, honest performance estimate | Once, at the end |
Important
The dev set is the target you aim at for weeks or months. Point it at the right target.
Bad idea (true story pattern): dev set from some regions, test set from other regions.

Fix: shuffle everything together, then split dev and test from the same distribution.
Guideline
Choose dev and test sets that reflect the data you expect to get in the future and consider important to do well on.

Principle: dev set big enough to detect differences between models; test set big enough for confidence in the final number.
Metric says A; users prefer B. The data can mislead too:

Important
If doing well on your metric + dev/test set does not correspond to doing well in your application: change the metric or the dev/test set.
You are building a defect detector for a factory. You train and evaluate on sharp, well-lit photos from the vendor’s catalog. On the production line, images come from a fixed camera with glare and dust.

While the model is worse than humans, you have tools:

Bayes error: the best possible error any system could ever achieve (some noise is simply irreducible).
Medical image example: what is “human-level” error?
| Who | Error |
|---|---|
| Typical human | 3% |
| Typical doctor | 1% |
| Experienced doctor | 0.7% |
| Team of experienced doctors | 0.5% |
Use the best available human performance (0.5%) as the proxy.

Same training error (8%) and dev error (10%), opposite conclusions:

Variance = the gap between training error and dev error: the model memorized more than it generalized.


Diagnose each case (human-level error is about 0.5%):
| Case | Training error | Dev error | Your diagnosis? |
|---|---|---|---|
| A | 6.0% | 6.5% | ? |
| B | 0.7% | 5.0% | ? |
| C | 0.7% | 0.9% | ? |
| Diagnosis | What to try |
|---|---|
| High avoidable bias (human → training gap) | Bigger model; train longer; better optimizer; better architecture |
| High variance (training → dev gap) | More data; regularization; data augmentation; simpler model |
The two assumptions of supervised learning
Cat classifier: 10% error. “It keeps mistaking dogs for cats, let’s fix dogs!” Before spending months, do error analysis:

Evaluate several improvement ideas in parallel, in a spreadsheet:
| Image # | Dog | Great cat (lion, panther…) | Blurry | Comments |
|---|---|---|---|---|
| 1 | ✓ | pitbull | ||
| 2 | ✓ | |||
| 3 | ✓ | ✓ | rainy day at zoo | |
| … | ||||
| % of total | 8% | 43% | 61% |
New categories can be added mid-way (rows can tick several boxes).
Your error analysis of 100 misclassified images gives:
| Category | Share of errors |
|---|---|
| Blurry images | 60% |
| Dogs labeled as cats | 25% |
| Exotic cats | 10% |
| Wrong labels | 5% |

Error analysis does not give a rigid formula. It gives a clear sense of the best options.
Deep learning is fairly robust to random label errors in training data.
Here wrong labels can corrupt your comparisons between models.
If you clean labels: apply the same process to dev and test, so they stay from the same distribution.
Speech recognition: noisy background, accents, far microphone, children, stuttering… Which to focus on? You cannot know in advance.

Cat app: 200,000 clean web images available, but users upload 10,000 blurry mobile images. Where should the scarce real data go?

With mismatched data, a train → dev gap has two possible causes:
To separate them, carve out a training-dev set:
Now: train vs training-dev isolates variance; training-dev vs dev isolates mismatch.

Quick cases (human ≈ 0%):

Synthesis risk
Synthesizing from a small slice (one hour of car noise, a few 3D car models) makes the model overfit to that slice: perfect to human eyes, yet unrepresentative of the real world.
A team trains a vibration-anomaly detector for pumps using data from a clean laboratory test rig. Deployed in the plant, it misses real faults.

A model in a notebook helps nobody but you. It becomes useful when other people or systems can send it inputs and get predictions back.
ML serving = making your model available as a service:

API = a defined way for programs to talk to each other over the network.
Your model’s API can be one single “door”:
POST /predict
input: { "features": [5.1, 3.5, 1.4, 0.2] }
output: { "prediction": "class_A", "probability": 0.97 }
from fastapi import FastAPI
import joblib
app = FastAPI()
model = joblib.load("model.pkl") # step 2: the saved model file
@app.post("/predict")
def predict(features: list[float]):
prediction = model.predict([features])
return {"prediction": prediction[0]}About 10 lines: your model is now a web service.
Problem: “it works on my machine” (but not on the server, or your teammate’s laptop).
Docker packages your code plus its whole environment into one shippable unit:

# Build the image (package the app + model + environment)
docker build -t ml-api .
# Run it as a container, exposed on port 8000
docker run -p 8000:8000 ml-apiThen, from anywhere that can reach the machine:
POST http://localhost:8000/predict → { "prediction": ... }
Note
You do not need to master Docker in this course. You only need the idea: model → API → container → anyone can use it. We will revisit this hands-on in the practical weeks.

STAI 2026 | Lecture 2: ML Strategy, Error Analysis, and Simple ML Serving