您的模型輸出 0.87。有人把它放在儀表板上標示為「成功機率 87%」。客戶根據它做出決策。

在這個數字真正有意義之前,您實際上需要多少個已解析的結果?

有一個閉合形式的答案,它比我們所有人都還要古老,而大多數團隊從未執行過它。

這個數字

對於您想以 95% 信心水準在 ±ε 範圍內引用的比例:

n ≥ p̂(1 − p̂) · (z / ε)²

進入全螢幕模式 退出全螢幕模式

z = 1.96 表示 95%。就是這樣。執行它:

from math import ceil

def min_n(p_hat: float, eps: float, z: float = 1.96) -> int:
    """引用 p_hat 需在 ±eps 範圍內所需的已解析結果數。"""
    return ceil(p_hat * (1 - p_hat) * (z / eps) ** 2)

for p in (0.5, 0.8, 0.9):
    print(p, min_n(p, 0.05))
# 0.5 385
# 0.8 246
# 0.9 139

進入全螢幕模式 退出全螢幕模式

要說「50%,誤差在 5 個百分點以內」,需要 385 個已解析的結果。這不是您訓練集中的 385 筆資料,而是您實際找出發生結果的 385 個案例。

令人痛苦的部分

精確度是平方關係。將 ε 收緊 5 倍需要 25 倍的資料:

for eps in (0.10, 0.05, 0.02, 0.01):
    print(eps, min_n(0.5, eps))
# 0.1   97
# 0.05  385
# 0.02  2401
# 0.01  9604

進入全螢幕模式 退出全螢幕模式

從「大約一半」到「50% ± 1 個百分點」需要大約 9,500 個結果。如果您所在的領域需要 90 天才能解析結果,那不是一個衝刺階段,而是好幾年的時間。

反向思考

更有用的方向是反轉它。不要問您需要多少資料,而是問您目前的資料允許您說什麼:

def widest_claim(p_hat: float, n: int, z: float = 1.96) -> float:
    """您的證據實際支持的 ± 範圍。"""
    return z * (p_hat * (1 - p_hat) / n) ** 0.5

print(round(widest_claim(0.5, 50), 3))   # 0.139

進入全螢幕模式 退出全螢幕模式

在 n=50 時,您誠實的說法是 ±14 個百分點。所以「50%」實際上是「介於 36% 和 64% 之間的某個值」。這不是您應該印在金額旁邊的機率。

這重新定義了整個問題。您沒有一個精確度的問題,而有一個用詞的問題:在 n=50 時,您被允許說範圍,而不是百分比。說出範圍。

Wilson,而非樸素區間

一個修正:上面的公式是常態近似,當 n 很小或 p̂ 很極端時,它會嚴重失效。它會很樂意給您一個延伸超過 1.0 的區間。請改用 Wilson 區間:

def wilson(successes: int, n: int, z: float = 1.96) -> tuple[float, float]:
    if n == 0:
        return (0.0, 1.0)
    p = successes / n
    d = 1 + z**2 / n
    centre = (p + z**2 / (2 * n)) / d
    half = z * ((p * (1 - p) / n + z**2 / (4 * n**2)) ** 0.5) / d
    return (centre - half, centre + half)

print(wilson(9, 10))   # (0.5958, 0.9821)

進入全螢幕模式 退出全螢幕模式

十次成功中有九次並不是「90% 可靠」。而是「介於 60% 和 98% 之間的某個值」,這是放在客戶面前的完全不同的說法。

將其設為約束,而非指引

這裡是生產環境中重要的部分:沒有人執行的規則是在截止期限壓力下會被侵蝕的規則。文件不會拯救您。把關卡放在程式碼路徑中。

MIN_N = 385

def render_score(p_hat: float, n_resolved: int) -> str:
    if n_resolved < MIN_N:
        lo, hi = wilson(round(p_hat * n_resolved), n_resolved)
        return f"band: {lo:.0%}{hi:.0%} (n={n_resolved})"
    return f"{p_hat:.0%} (n={n_resolved})"

進入全螢幕模式 退出全螢幕模式

現在誠實的版本是預設路徑,而過度宣稱需要有人在經過審核的 PR 中刪除程式碼。這與維基頁面說「使用機率要小心」是完全不同的組織事實。

一個陷阱:哪個 n?

n已解析的結果,而解析正是偏差潛入的地方。

如果您的成功是從下游資料自動解析,但您的失敗需要有人手動標記,那麼您的已解析集合就不是現實的隨機樣本。它偏向成功,而您在上面計算的每個區間都是自信地錯誤。非隨機缺失資料不會自我宣告,它只是讓您的校準看起來很好。

在您相信這個數字之前,請檢查產生結果的機制是否因結果類型而異。這個檢查比任何模型診斷都抓到更多壞掉的儀表板。

為什麼我關心這個

我開發計費軟體。當我們的系統說一項索賠有可能被收回時,就會有人根據這個數字安排人員,而一家診所就會做出薪資決策。一個自信地錯誤的百分比的代價就是某人的一個月收入。

所以我們在數量達到標準之前出貨範圍,而產品在架構上被禁止在此之前說「機率」。這讓我們的示範看起來沒那麼漂亮,但它從未失去過客戶。


如果您想要更長的論證——關於一個系統在結構上被禁止過度宣稱意味著什麼,以及為什麼做出宣稱的實體絕不能是決定它是否正確的那個——我在這裡寫了: The Uncapturable Judge

您的團隊對於分數何時可以稱自己為機率有什麼規則?