False fires on UI elements: 44% → 22%, without adding a single outer if
Don't let an outer if do the model's job — how I changed the definition of the ground truth from "anywhere on the screen" to "inside the subtitle region"
Killing a model’s mistakes with an outer if is easy. Drop things by position. Don’t let it fire again for a while. Add an exception. The numbers improve right away, too.
But if that distinction is something you could make from the input, the outer if may just be hiding the model’s weakness.
In LYR’s subtitle gate, I was suppressing false reactions to UI with rules. When I traced the cause, what was wrong wasn’t the model but the definition of “correct.” And fixing the labels wasn’t enough on its own either — because the problem was decided by position, and I was never handing the model any position.
This article is a case study in the manifesto’s fourth principle, Eliminate, Don’t Accelerate. The point being that the
ifyou add on the outside usually never gets counted as “work you could eliminate.”
Last time I wrote about putting a cheap, always-on gate (the Awake Layer) in front of OCR to cut how often it gets called (Part 1). It’s a layer that looks at “is there text on screen worth reading right now?” and decides whether to wake the expensive work.
Once it was running, a problem showed up immediately. This layer reacts to text that isn’t subtitles.
The column of on-screen controls down the right side of a video app. A browser’s URL bar. The ”⋮” menu in the top right. A game’s HUD. A human can tell at a glance that none of it is a subtitle, but to the gate it was all the same “something that looks like text.” React, and OCR fires. You pay the power, and on top of that fragments of UI end up mixed into the translation.
Note: this figure reproduces the same phenomenon as the 44%→22% discussed in the article using data that can be published; both the corpus and the implementation differ, so the numbers don't match. The account name and icon are blurred.
Killing it on the outside was easy
My first fix was, of course, a filter on the outside.
Drop by position. Drop anything too short. Don’t fire again for a fixed interval after a fire. Suppress when the same thing repeats — every one of them is a few lines of code, and every one of them works. In fact, a one-line change that lengthened the firing-suppression interval cut redundant firing by -32 to -78% across all five recordings, and the power proxy by -27 to -31%.
On the numbers alone, that’s a win. But it came with a price. The harder I suppressed, the later the response when a subtitle got swapped out — up to 2 seconds later — and misses ticked up slightly. So this wasn’t an improvement, it was a dial. All it did was shift the balance between wasted firing on one side and lag and misses on the other.
If you cut redundant firing and added latency, that’s not an improvement — you just moved the operating point on a trade-off.
Then it hit me. Every one of these ifs can be described by the same sentence.
I was taking it as given that the model gets it wrong, and buying the cleanup on the outside.
The premium is paid every frame, on the device. You can’t cancel the policy until you fix the cause, and every new exception stacks another policy on top. Position filters, suppression intervals, merge conditions, exceptions to exceptions — outer logic keeps growing while the weakness inside stays hidden.
I changed the definition of the ground truth
So I stopped touching the outside and went to look at what the model had actually been taught was “correct.”
The labels were defined like this — “if text is readable anywhere on the screen, a subtitle is present.”
Under that definition, firing on UI isn’t a malfunction. It’s doing exactly what it was taught. The text in the URL bar and the on-screen controls are both “text that was readable somewhere on the screen.” The model was right; my label definition was wrong.
The fix wasn’t adding one more outer if. It was rewriting the definition.
“readable anywhere on the screen” → “readable inside the subtitle region”
That alone turns UI text into a negative example (something it must not fire on) as far as the model is concerned. The distinction I’d been enforcing on the outside moves inside the training.
The result: false fires on text outside the subtitle region were corrected from “present” to “absent” on 20% of frames in game footage and 23% in drama. Without adding a single outer if.
I was training a position problem without position
To be honest, my first attempt here was a dud.
Retraining with nothing but the label definition fixed had zero effect (no change). The reason is obvious once you think about it: the input I was handing the model was only what the screen looked like, and the information “where on the screen that text sits” was never being passed at all. I was trying to teach a distinction that’s decided by position without ever showing it position.
Only once I added position information to the input did false firing on decorations and UI halve, from about 44% to about 22%.
That, I think, is the most practically important lesson here.
If you’ve decided the model should solve it, hand it what it needs to solve it. Withholding that and telling yourself “training will sort it out” is the same sloppiness as an outer
if.
When outer logic is justified
That said, “outer logic is always bad” isn’t right either. Around the same time there was a case where solving it on the outside was clearly correct.
In the mode for reading manga in a browser, the ”⋮” menu in the top right was being misrecognized as a single character and mixed into the translation. Nothing in the character itself distinguishes it from a subtitle. The move I took here wasn’t guessing at a position threshold, and it wasn’t training the model — it was asking the OS for the actual drawing region. The OS holds exact coordinates for the content area. Just discarding everything outside it made the ”⋮” and the URL bar disappear on all three runs on a real device, leaving only the body text translated.
The difference between these two is clear-cut.
| Problem | Where it’s solved | Why |
|---|---|---|
| Browser UI regions | Outside | Only the OS has the exact content boundary. Information the model cannot see in principle |
| Telling subtitles from UI on screen | The model side | It can be told apart from the image and the position |
| Rate-controlling repeated fires (deduplication) | The system side | ”Don’t reprocess what hasn’t changed” is a product requirement over time |
| Cleaning up after the model’s false fires | The model side, as a rule | It can be decided from the input, yet it’s being patched on the outside |
Here’s where the line falls.
Outer logic earns its place when you’re injecting information the model cannot possibly know, or a constraint that has to be applied deterministically. If you’re handling something on the outside that the model could decide from its input, that’s debt hiding the model’s weakness.
To be clear: deduplication itself is not debt. Keeping state so that the same subtitle isn’t reprocessed while it hasn’t changed is a product spec, not cleanup after the model. What was debt in this article was lengthening that suppression interval in order to hide false fires. The same code means something different depending on what it’s hiding.
And a position heuristic built on guesswork and the real region the OS hands you are completely different things, however similar they look. The first breaks every time the device or the screen orientation changes; the second doesn’t.
And still, I haven’t won this outright
I’d like to end it cleanly, but let me put the facts down.
Fixing the gate’s signal definition did not win across every genre. Compared against ground truth I built by hand, subtitle capture on lecture footage went 35% → 63% (statistically clear, p≈1e-4) — a real win. But on news footage, just raising the existing model’s threshold matched it or beat it — which means that win wasn’t retraining’s doing, it was confounding. The retrained model didn’t win; the operating points of the two things being compared simply weren’t held constant. Until I put in a control with either recall or the threshold held constant, I can’t say “the training worked” (Don’t talk about “why” until you’ve taken a control). No significant difference on the other genres. As of now there is still no basis that justifies adopting it across production.
Even so, I’m not going back to stacking ifs on the outside. The reason is simple.
Outer patches accumulate; they never go away. Fix the model, and the outer patches disappear. The first keeps adding work, the second reduces it. Both count as “it worked,” but the balance moves in opposite directions.
Lessons
- When you see a malfunction, read the definition of “correct” first. The model may not have been wrong at all — it may be correctly learning the wrong objective. Before you go beat on it from the outside, suspect the definition.
- If the model is going to solve it, hand it the information it needs. You cannot train a problem that’s decided by position without position. Performance is set by label definition × input information × model capability.
- Justify outer rules by where the information lives. Information only the OS has, you inject from the outside. Take over a distinction the model could make from its own input, and work and exceptions pile up.
This is the manifesto’s fourth principle — Eliminate, Don’t Accelerate — showing itself from slightly around the back. An outer if looks like a “fix” at the moment you add it. It only became visible to me as work I could eliminate once I could put into words what it was hiding.
Appendix: raw data
| Experiment | Improvement | Price paid & caveats |
|---|---|---|
| Firing suppression 500→2000ms (one-line change) | redundant firing -32 to -78% (all five recordings), power proxy -27 to -31% | subtitle-swap lag up to 2 seconds, slight rise in misses = a dial |
| Label redefinition (anywhere on screen → inside the subtitle region) | corrected 20% of game frames / 23% of drama frames from “present” to “absent” | the region approximated as a fixed rectangle (moving subtitles can be over-counted as “absent”); visual spot-checks only on part of the drama set |
| Retraining with only the labels fixed | no change (wash) | the input carried no position information |
| Adding the position channel | false firing on decorations and UI about 44% → about 22% | compared with recall held constant |
| Exclusion via the OS display region | UI contamination gone in 3 of 3 runs on a real device | only where the display region can be obtained (otherwise a safety-net band filter) |
| Retraining the signal definition (hand-built ground truth, one recording held out at a time) | lecture capture 35% → 63% (p≈1.2e-4) | news is confounded (threshold tuning matches or beats it); no significant difference on other genres. Not yet enough to justify adoption across the board |
Note: part of the power proxy is a relative value of firings × cost (accumulated on the harness), not a measured value from a power meter on a real device.