第 44 課 · 實戰 · 19 分鐘

Python 模型評估與準確率

只有知道指標測量了什麼,分數才有意義。

訓練/測試劃分準確率精準率召回率混淆矩陣

分類器報告 92% 的準確率,可它漏掉的 8% 全是投訴。準確率把失敗藏起來了。哪個指標能把它露出來?

模型在記住的資料上表現好,並不能證明什麼。評估需要獨立的測試集、明確的正類,以及與錯誤代價比對的指標。準確率只是起點,不是完整報告。 你還會用 F1 綜合精準率和召回率,並檢查分組切片。

留出測試集

在配適或調參之前先劃分樣本。測試集不應影響特徵選擇、縮放或門檻值選擇。資料很小時,應使用多折交叉驗證並報告波動,而不是相信一次幸運劃分。

rows = [("a", 1), ("b", 0), ("c", 1), ("d", 0), ("e", 1)]
cut = 3
train = rows[:cut]
test = rows[cut:]
print(len(train), len(test))
print(test)

明確、固定的劃分

統計四種結果

二分類預測會落入真正例、真負例、假正例或假負例。要顯式構造混淆矩陣。類別名稱取決於你把哪一類稱為正類,因此報告裡要寫清楚。

actual = [1, 1, 0, 0]
predicted = [1, 0, 0, 1]
tp = sum(a == 1 and p == 1 for a, p in zip(actual, predicted))
tn = sum(a == 0 and p == 0 for a, p in zip(actual, predicted))
fp = sum(a == 0 and p == 1 for a, p in zip(actual, predicted))
fn = sum(a == 1 and p == 0 for a, p in zip(actual, predicted))
print(tp, tn, fp, fn)

四個計數,而不是一個

計算精準率與召回率

精準率問的是預測為正的樣本中有多少正確;召回率問的是真實正例中找到了多少。兩者都可能被退化分類器操縱,因此要同時報告類別比例和判定門檻值。

tp, fp, fn = 8, 2, 4
precision = tp / (tp + fp)
recall = tp / (tp + fn)
print(round(precision, 2))
print(round(recall, 2))

兩個不同的問題

警惕不平衡資料上的準確率

如果一百個樣本裡只有五個正例,全部預測為負會得到 95% 準確率,卻一個正例也沒找到。任何亮眼數字都應先與簡單基線比較,並檢查每個類別的結果。

actual = [0] * 95 + [1] * 5
predicted = [0] * 100
accuracy = sum(a == p for a, p in zip(actual, predicted)) / len(actual)
print(round(accuracy, 2))
print(sum(1 for a, p in zip(actual, predicted) if a == 1 and p == 1))

高準確率,零召回

F1 平衡精準率與召回率

F1 是精準率和召回率的調和平均。只有兩者都高時 F1 才高,因此當偽陽性和偽陰性都不能忽視時很有用。也要報告組成指標;F1 會掩蓋到底是哪一側失敗。

tp, fp, fn = 8, 2, 4
precision = tp / (tp + fp)
recall = tp / (tp + fn)
f1 = 2 * precision * recall / (precision + recall)
print(round(precision, 2), round(recall, 2), round(f1, 2))

一個數字,兩個成分

檢查分組切片,而不只看總分

總體指標看起來尚可時,某個群體可能表現很差。按相關切片分組統計,但每組要有足夠樣本,避免過度解讀雜訊。切片是診斷手段,不是收集不必要個人資料的許可。

rows = [
    ("new", 1, 1), ("new", 1, 0), ("new", 0, 1),
    ("returning", 1, 1), ("returning", 0, 0), ("returning", 0, 0),
]
for group in sorted({row[0] for row in rows}):
    subset = [row for row in rows if row[0] == group]
    correct = sum(actual == predicted for _, actual, predicted in subset)
    print(group, correct, "/", len(subset), round(correct / len(subset), 2))

切片會藏在漂亮的平平均值裡

本課要點

  • 在訓練和調參都沒有使用過的資料上評估。
  • 混淆矩陣展示四種可能結果。
  • 精準率衡量預測為正的準確程度,召回率衡量正例被找到的比例。
  • 類別不平衡時,準確率可能看起來很高。
  • 報告分數時同時說明類別比例、門檻值和簡單基線。
  • F1 概括精準率與召回率的權衡;分組檢查能暴露隱藏的失敗。

常見錯誤

  • 反覆在測試集上調參,直到它不再是測試集。
  • 報告準確率卻不說明哪一類是正類。
  • 漏掉一個正例代價很高,卻忽略假負例。
  • 只報告一個總體準確率,忽視一個雖小但重要的失敗群體。
輪到你

給定真實和預測的二分類標籤,計算混淆矩陣、準確率、精準率和召回率,並處理除零情況。

動手試試

改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。

瀏覽器內 Python本機執行 · 無需帳號
首次執行會從 CDN 下載 Pyodide。修改後點擊「執行程式碼」。

練習本課

12 道題,作答後立即看到解釋。

查看 12 道題答案開始練習
← 最近鄰分類提示詞驗證 →