← writing

2026-07-05

Accurate Offline, Useless In-App: The Tradeoff Nobody Grades You On

sources: Bluesense AI internship (28 Oct 2025 – 27 Feb 2026), internship certificate · personal engineering notes from the internship

For most of university, "a good model" meant one number: the score on the held-out set. Higher is better, ship the higher one, defend it in the report. Then I spent four months as a volunteer intern at Bluesense AI — integrating pretrained face-analysis models into a real product and building signal-processing pipelines for physiological data — and learned that the benchmark score is the number you optimize in a notebook, not the number the user feels. The number the user feels is latency, and nobody grades you on it in a course.

This post is the general version of that lesson. I'm deliberately not quoting product internals or invented metrics — just the shape of the problem, which I think is the same everywhere a model has to run in front of a person instead of on a leaderboard.

The two numbers are not the same number

Take a pretrained face-analysis model off Hugging Face. Its model card gives you accuracy on some standard benchmark. That number is real, and it is also almost irrelevant to whether the feature is good, because it was measured under conditions your product will never reproduce: clean images, a fast machine, no one waiting.

The moment that model sits behind a "analyze my skin" button, a second number appears that the model card never mentions: how long the user stares at a spinner. And that number is a function of things the accuracy benchmark ignored entirely — image size, preprocessing, whether inference runs on a GPU or a tired CPU, cold-start on the first request, the round-trip to wherever the model lives.

You can have a model that is more accurate and worse, because the extra accuracy cost two seconds and the user had already decided the feature was broken. Accuracy is measured in a vacuum; usefulness is measured with a clock running.

Latency is a product decision wearing an engineering costume

The reflex is to treat latency as a purely technical problem — quantize the model, batch the requests, throw hardware at it. Those help. But the real move is upstream: deciding how good the answer actually needs to be for this feature to do its job.

A skin-analysis feature does not need forensic precision. It needs to feel responsive and be right enough that the user trusts it. Once you accept that, options open up that a pure accuracy-maximizer would never consider: a smaller model, a lower input resolution, a cheaper preprocessing path. Each one trades a little accuracy the user can't perceive for a lot of latency the user very much can. That's not cutting corners — it's spending your accuracy budget where it's visible instead of where it flatters a benchmark.

The hard part is that this decision doesn't belong to the model. It belongs to the product. And you can't make it from inside the notebook, because the notebook doesn't have a spinner.

Signals taught me the same thing from the other side

I also worked on HRV and ECG pipelines — heart-rate-variability and cardiac signals, which are a different animal from images. Here the temptation runs the opposite way: signal data is noisy, so you want to clean it aggressively, filter hard, smooth everything. And you can absolutely filter a physiological signal until it's beautiful and meaningless — you've removed the noise and the information along with it.

So the tradeoff is the same shape, just mirrored. With the face model, more computation buys accuracy you might not need. With the signal pipeline, more cleaning buys smoothness that can cost you the very variability you were trying to measure. Both are the same question in different clothes: how much are you willing to distort the thing to make it convenient? The honest answer is never "as much as possible" and never "none" — it's a number you have to justify.

Integration is where the real bugs live

Nothing in a model card prepares you for integration. The model works; the model in the pipeline, behind an API, called from the app, under CI, does not — and the gap between those two is where the actual engineering is. A color channel in the wrong order, a preprocessing step that silently disagrees between training and serving, an input the model was never shown and quietly mishandles: none of these show up in accuracy numbers. They show up as "it's wrong sometimes and I don't know why."

Working inside the team's CI/CD flow changed how I think about this. A model isn't done when the score is good; it's done when it survives the pipeline that pushes it, repeatably, without a human babysitting each release. That's a much higher and much less glamorous bar than a leaderboard, and it's the one that actually protects users.

What I'd tell my earlier self

The version of me that thought a model was a single number wasn't wrong so much as incomplete. A model in production is at least three numbers — how accurate, how fast, how reliably it ships — and they trade against each other. Optimizing only the first one is how you build something that demos well and helps no one.

I still care about the accuracy score. I just no longer believe it's the score. The number I now watch is the one the user experiences with a clock running, and the discipline is refusing to pretend the benchmark already answered that question. It didn't. It never does.