<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dys5315</title>
    <description>The latest articles on DEV Community by dys5315 (@dys5315).</description>
    <link>https://dev.to/dys5315</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4050221%2F15619e83-2900-4d70-8c7e-34ab66d7aebc.png</url>
      <title>DEV Community: dys5315</title>
      <link>https://dev.to/dys5315</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dys5315"/>
    <language>en</language>
    <item>
      <title>Your model can't grade its own homework</title>
      <dc:creator>dys5315</dc:creator>
      <pubDate>Wed, 29 Jul 2026 03:27:50 +0000</pubDate>
      <link>https://dev.to/dys5315/your-model-cant-grade-its-own-homework-3c65</link>
      <guid>https://dev.to/dys5315/your-model-cant-grade-its-own-homework-3c65</guid>
      <description>&lt;p&gt;Every team I've watched ship a broken measurement system broke it the same way. Not with bad math — with an org chart problem that happened to live in code.&lt;/p&gt;

&lt;p&gt;The entity making the claim ended up being the entity that decided whether the claim was right.&lt;/p&gt;

&lt;p&gt;Once you have the shape in your head you start seeing it everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three roles, not two
&lt;/h2&gt;

&lt;p&gt;Most engineers think about measurement as two roles: the thing that acts, and the thing that grades it. That's one role short. There are three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Player&lt;/strong&gt; — makes the claim. Your model, your service, your PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scorer&lt;/strong&gt; — applies the rubric. Your eval harness, your test suite, your metrics dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settler&lt;/strong&gt; — determines what actually happened. Production outcomes. Reality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scorer is a proxy. The settler is the thing the proxy is trying to approximate.&lt;/p&gt;

&lt;p&gt;The rule: &lt;strong&gt;be the scorer, never the settler.&lt;/strong&gt; When the player captures the settler, the loop closes on itself and the system can no longer be wrong — which sounds like success and is actually the failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like in code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tuning on the test set.&lt;/strong&gt; You check test accuracy, adjust hyperparameters, check again. Twenty iterations later the test set is training data with extra steps. The player is now selecting its own settler. That's what overfitting &lt;em&gt;is&lt;/em&gt;, structurally — not a math failure, a role-collapse failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM-as-judge from the same family.&lt;/strong&gt; Your generator is GPT-flavored and your judge is GPT-flavored. They share pretraining data, failure modes, and blind spots. The judge doesn't rate quality — it rates similarity to what it would have produced. Correlated error is invisible to averaging; running it 1,000 times makes you &lt;em&gt;more&lt;/em&gt; confident of the same wrong answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benchmark contamination.&lt;/strong&gt; The model scores 94% on the benchmark that's in its training data. Nobody lied. The settler just quietly moved inside the player.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-reported health.&lt;/strong&gt; A service that returns its own health check is a claimant ruling on its own claim. If the process is wedged, the check is wedged too, and your dashboard is green through the outage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backtests without decision-time reconstruction.&lt;/strong&gt; You score a historical decision using a feature computed after the outcome was known. The past gets graded with information from the future. Your Sharpe ratio is beautiful and entirely fictional.&lt;/p&gt;

&lt;p&gt;Same failure, five costumes: the claimant captured the settler.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix is structural, not moral
&lt;/h2&gt;

&lt;p&gt;Nobody in the above list was dishonest. That's the point — this doesn't get solved with care or seniority, because it isn't a discipline problem. Independence has to be &lt;em&gt;engineered&lt;/em&gt; or it decays under ordinary pressure.&lt;/p&gt;

&lt;p&gt;What that means concretely:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the settler expensive to reach.&lt;/strong&gt; A holdout you can query freely is not a holdout. Rate-limit it, log every access, treat a query as a spend. If the number of times you've looked at the test set isn't recorded anywhere, you've already lost track of how contaminated it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick judges from a different failure family.&lt;/strong&gt; If a human can't review it, at minimum use a different model lineage. You want errors that are uncorrelated, not errors that agree with each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let production be the settler.&lt;/strong&gt; Ship the prediction, capture the realized outcome, compare. Anything upstream of reality is a proxy, and every proxy is a scorer wearing a settler's coat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reconstruct decision-time state.&lt;/strong&gt; Before scoring a historical decision, rebuild exactly what was knowable at that moment. If that's hard to do, that difficulty is the finding — it means your current backtest is leaking and you didn't know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch who owns the metric.&lt;/strong&gt; If the team that ships the feature also defines success and reports the number, you don't have a measurement — you have a press release with error bars.&lt;/p&gt;

&lt;h2&gt;
  
  
  The check that catches it
&lt;/h2&gt;

&lt;p&gt;One question, applied to any number your system produces:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If this claim were wrong, what would tell us — and is that thing under our control?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If nothing would tell you, the claim isn't false, it's &lt;em&gt;unfalsifiable&lt;/em&gt;, which is worse. And if the thing that would tell you is something you own, tune, or can silence, you're the settler and the loop is closed.&lt;/p&gt;

&lt;p&gt;A closed loop doesn't fail loudly. It drifts, confidently, until reality collects — and reality runs on its own clock, not your sprint schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I keep thinking about it
&lt;/h2&gt;

&lt;p&gt;I build software that tells medical practices which claims are worth appealing. The settler is the payer's actual remittance. Not our model's confidence, not our internal rubric, not what our dashboard says. If we ever let those become the settler, we'd have a product that looks excellent and is quietly lying to people who make payroll decisions with it.&lt;/p&gt;

&lt;p&gt;I ended up writing this up properly — the same structure turns out to show up in market bubbles, in overfitting, and in a few places I didn't expect: &lt;a href="https://ssrn.com/abstract=7183639" rel="noopener noreferrer"&gt;The Uncapturable Judge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Where's the closed loop in your stack? Mine was the health check, and it took an outage to find it.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>testing</category>
      <category>datascience</category>
    </item>
    <item>
      <title>How many outcomes before you can quote a percentage?</title>
      <dc:creator>dys5315</dc:creator>
      <pubDate>Wed, 29 Jul 2026 03:26:50 +0000</pubDate>
      <link>https://dev.to/dys5315/how-many-outcomes-before-you-can-quote-a-percentage-1jd0</link>
      <guid>https://dev.to/dys5315/how-many-outcomes-before-you-can-quote-a-percentage-1jd0</guid>
      <description>&lt;p&gt;Your model outputs &lt;code&gt;0.87&lt;/code&gt;. Someone puts it on a dashboard labeled "87% likely to succeed." A customer makes a decision with it.&lt;/p&gt;

&lt;p&gt;How many resolved outcomes did you actually need before that number meant anything?&lt;/p&gt;

&lt;p&gt;There's a closed-form answer, it's older than all of us, and most teams never run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number
&lt;/h2&gt;

&lt;p&gt;For a proportion you want to quote within ±ε at 95% confidence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n ≥ p̂(1 − p̂) · (z / ε)²
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;z = 1.96&lt;/code&gt; for 95%. That's it. Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ceil&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;min_n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_hat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.96&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Resolved outcomes needed to quote p_hat within ±eps.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_hat&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p_hat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min_n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# 0.5 385
# 0.8 246
# 0.9 139
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;385 resolved outcomes to say "50%, give or take 5 points." Not 385 rows in your training set — 385 cases where you found out what actually happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that hurts
&lt;/h2&gt;

&lt;p&gt;Precision is quadratic. Tightening ε by 5× costs 25× the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min_n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# 0.1   97
# 0.05  385
# 0.02  2401
# 0.01  9604
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going from "roughly half" to "50% ± 1 point" costs about 9,500 outcomes. If you're in a domain where outcomes take 90 days to resolve, that's not a sprint — that's years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read it backwards
&lt;/h2&gt;

&lt;p&gt;The more useful direction is inverting it. Instead of asking how much data you need, ask what your current data licenses you to say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;widest_claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_hat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.96&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The ± band your evidence actually supports.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_hat&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p_hat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;widest_claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# 0.139
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At n=50, your honest claim is &lt;strong&gt;±14 points&lt;/strong&gt;. So "50%" is really "somewhere between 36% and 64%." That's not a probability you should be printing next to a dollar amount.&lt;/p&gt;

&lt;p&gt;This reframes the whole thing. You don't have a precision problem, you have a &lt;em&gt;vocabulary&lt;/em&gt; problem: at n=50 you're licensed to say bands, not percentages. Say the band.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wilson, not the naive interval
&lt;/h2&gt;

&lt;p&gt;One correction: the formula above is the normal approximation, and it degrades badly at small n or extreme p̂. It'll happily hand you an interval that extends past 1.0. Use Wilson instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wilson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;successes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.96&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;successes&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="n"&gt;centre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;centre&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;centre&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;wilson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# (0.5958, 0.9821)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nine out of ten successes is not "90% reliable." It's "somewhere between 60% and 98%," which is a very different sentence to put in front of a customer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it a constraint, not a guideline
&lt;/h2&gt;

&lt;p&gt;Here's the part that matters in production: &lt;strong&gt;a rule nobody enforces is a rule that erodes under deadline pressure.&lt;/strong&gt; Documentation won't save you. Put the gate in the code path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MIN_N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;385&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_hat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_resolved&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n_resolved&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MIN_N&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wilson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_hat&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n_resolved&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;n_resolved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;band: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;–&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (n=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n_resolved&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;p_hat&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (n=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n_resolved&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the honest version is the &lt;em&gt;default path&lt;/em&gt;, and overclaiming requires someone to delete code in a reviewed PR. That's a very different organizational fact than a wiki page saying "be careful with probabilities."&lt;/p&gt;

&lt;h2&gt;
  
  
  One trap: which n?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;n&lt;/code&gt; is &lt;strong&gt;resolved outcomes&lt;/strong&gt;, and resolution is where the bias sneaks in.&lt;/p&gt;

&lt;p&gt;If your wins auto-resolve from downstream data but your losses require somebody to manually mark them, your resolved set is not a random sample of reality. It's skewed toward wins, and every interval you compute on it is confidently wrong. Missing-not-at-random data doesn't announce itself — it just makes your calibration look great.&lt;/p&gt;

&lt;p&gt;Before you trust the number, check whether the &lt;em&gt;mechanism that produces outcomes&lt;/em&gt; differs by outcome type. That check has caught more bad dashboards for me than any model diagnostic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I care about this
&lt;/h2&gt;

&lt;p&gt;I build billing software. When our system says a claim is likely to be recovered, somebody staffs against that number and a practice makes a payroll decision. The cost of a confidently wrong percentage is somebody's month.&lt;/p&gt;

&lt;p&gt;So we ship bands until the volume clears, and the product is architecturally forbidden from saying "probability" before then. It costs us a nicer-looking demo. It has never once cost us a customer.&lt;/p&gt;




&lt;p&gt;If you want the longer argument — what it means for a system to be structurally forbidden from overclaiming, and why the entity that makes a claim must never be the one that settles whether it was right — I wrote it up here: &lt;a href="https://ssrn.com/abstract=7183639" rel="noopener noreferrer"&gt;The Uncapturable Judge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What's your team's rule for when a score gets to call itself a probability?&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>python</category>
      <category>statistics</category>
    </item>
  </channel>
</rss>
