Mục lục
- Recap confusion matrix
- Accuracy — tỉ lệ predict đúng
- Pitfall accuracy với imbalanced data
- Precision — độ tin cậy khi nói YES
- Recall — bắt được bao nhiêu positive thực
- Specificity — đối xứng cho class 0
- Trade-off Precision vs Recall theo threshold
- Khi nào dùng metric nào
- sklearn — accuracy_score, precision_score, recall_score
- Multi-class — macro, micro, weighted
- classification_report
- Phân tích industry-specific
- Common mistake khi báo cáo metric
- Code Python — Breast cancer + đổi threshold
- Bài tập thực hành
- Bài tiếp theo
Recap confusion matrix
Bài 23 đã xây dựng confusion matrix cho binary classification với 4 ô:
- TP (True Positive) — thực tế là 1, model dự đoán 1.
- TN (True Negative) — thực tế là 0, model dự đoán 0.
- FP (False Positive) — thực tế là 0, model dự đoán 1 (false alarm).
- FN (False Negative) — thực tế là 1, model dự đoán 0 (bỏ sót).
Bốn ô này là nguyên liệu thô. Một mình bốn số rất khó so sánh giữa các model hay giải thích cho stakeholder, nên trong thực hành ta thường derive ra các metric tỉ lệ. Ba metric quan trọng nhất là Accuracy, Precision, Recall — bài này đi qua từng cái, làm rõ ý nghĩa, công thức và khi nào nên dùng.
Sau bài này, bạn sẽ:
- Viết được công thức Accuracy, Precision, Recall, Specificity từ TP/TN/FP/FN.
- Giải thích được vì sao accuracy gây hiểu nhầm trên imbalanced data.
- Phân biệt được khi nào ưu tiên precision, khi nào ưu tiên recall theo cost của FP vs FN.
- Đọc được
classification_reportcủa sklearn cho cả binary và multi-class.
Accuracy — tỉ lệ predict đúng
Accuracy là tỉ lệ sample được phân loại đúng trên tổng số sample — bất kể class nào:
\[ \text{Accuracy} = \frac{TP + TN}{TP + TN + FP + FN} \]Range \( [0, 1] \), hoặc viết theo phần trăm. Tử số là số dự đoán đúng (đúng class 1 + đúng class 0); mẫu số là tổng số sample.
Ví dụ confusion matrix:
\[ \begin{bmatrix} TN & FP \\ FN & TP \end{bmatrix} = \begin{bmatrix} 80 & 5 \\ 10 & 5 \end{bmatrix} \]\( \text{Accuracy} = (80 + 5) / (80 + 5 + 10 + 5) = 85 / 100 = 0.85 \). Model đúng 85% trên test set.
Accuracy có hai tính chất:
- Đối xứng giữa hai class: đổi nhãn 0 ↔ 1 không đổi giá trị accuracy.
- Coi mọi loại lỗi như nhau: 1 FP và 1 FN trừ accuracy bằng nhau, dù trong nhiều domain cost của chúng rất khác.
Pitfall accuracy với imbalanced data
Đây là cạm bẫy phổ biến nhất khi mới dùng metric. Giả sử bài toán phát hiện gian lận thẻ tín dụng: trong 10,000 giao dịch chỉ có 50 gian lận (0.5%). Một model "lười" predict tất cả giao dịch đều là không gian lận:
- \( TN = 9{,}950 \), \( TP = 0 \), \( FP = 0 \), \( FN = 50 \).
- \( \text{Accuracy} = 9{,}950 / 10{,}000 = 0.995 \) — 99.5%.
Trên giấy tờ model "99.5% accuracy". Trên thực tế model không bắt được giao dịch gian lận nào — vô dụng hoàn toàn cho mục đích kinh doanh.
Quy tắc: khi class minority chiếm dưới 30% (hoặc khi cost FP và FN khác nhau rõ rệt), accuracy không phải metric để tối ưu hay để so sánh model. Phải dùng precision, recall, F1, hoặc các metric cost-sensitive khác.
Một cách nhanh để phát hiện vấn đề: tính baseline accuracy của classifier "always predict majority class". Nếu accuracy model của bạn không cao hơn baseline này đáng kể, accuracy đang đánh lừa.
Precision — độ tin cậy khi nói YES
Precision trả lời câu hỏi: trong số tất cả sample model gán nhãn "positive", bao nhiêu thực sự là positive?
\[ \text{Precision} = \frac{TP}{TP + FP} \]Mẫu số \( TP + FP \) là tổng số sample model dự đoán = 1 (cả đúng và sai). Tử số \( TP \) là phần thực sự đúng. Range \( [0, 1] \). Khi mẫu số = 0 (model không predict positive nào), precision không xác định — sklearn mặc định trả về 0 và in warning.
Cách hiểu thực dụng: precision đo độ tin cậy khi model nói "YES". Precision = 0.9 nghĩa là cứ 10 lần model bảo "đây là spam", 9 lần đúng và 1 lần model nhầm email tốt thành spam.
Với confusion matrix ví dụ ở bước 2 (\( TP=5, FP=5 \)):
\[ \text{Precision} = 5 / (5 + 5) = 0.5 \]Một nửa số dự đoán "1" của model là sai. Nếu mỗi false positive gây hậu quả tốn kém (chặn nhầm email khách hàng, đề xuất sai sản phẩm), precision thấp là vấn đề lớn ngay cả khi accuracy tổng thể 85%.
Cao precision → ít false alarm. Use case ưu tiên precision:
- Spam detection: chặn nhầm email của khách hàng có cost cao (mất giao dịch, mất khách).
- Recommendation: gợi ý sai làm user mất niềm tin vào hệ thống.
- Quảng cáo CTR: hiển thị quảng cáo không phù hợp lãng phí impression.
Recall — bắt được bao nhiêu positive thực
Recall (còn gọi Sensitivity, True Positive Rate) trả lời câu hỏi: trong số tất cả sample thực sự là positive, model bắt được bao nhiêu?
\[ \text{Recall} = \frac{TP}{TP + FN} \]Mẫu số \( TP + FN \) là tổng số sample thực tế = 1 (cả model bắt được lẫn bỏ sót). Tử số \( TP \) là phần model bắt được. Range \( [0, 1] \). Khi mẫu số = 0 (không có sample positive nào trong tập đánh giá), recall không xác định.
Lưu ý quan trọng: precision và recall có cùng tử số \( TP \) nhưng mẫu số khác nhau. Precision chia cho số model nói "1"; recall chia cho số thực tế "1". Đừng nhầm hai mẫu số.
Với confusion matrix ví dụ (\( TP=5, FN=10 \)):
\[ \text{Recall} = 5 / (5 + 10) = 0.333 \]Model chỉ bắt được 1/3 số sample positive thực sự. Hai phần ba còn lại bị bỏ sót. Nếu mỗi FN gây hậu quả nghiêm trọng (bệnh nhân ung thư không được chẩn đoán), recall thấp là vấn đề chính bất kể precision tốt đến đâu.
Cao recall → ít bỏ sót. Use case ưu tiên recall:
- Y tế: tầm soát ung thư, sàng lọc bệnh truyền nhiễm — bỏ sót có thể chết người. Cost FN rất cao.
- Phát hiện gian lận: bỏ sót giao dịch gian lận làm mất tiền và mất niềm tin.
- An toàn: phát hiện vật thể nguy hiểm trong x-ray sân bay, phát hiện lỗi sản phẩm trong dây chuyền sản xuất.
Specificity — đối xứng cho class 0
Recall là tỉ lệ bắt đúng class positive. Đối xứng cho class negative gọi là Specificity (còn gọi True Negative Rate):
\[ \text{Specificity} = \frac{TN}{TN + FP} \]Tử số là số sample thực tế negative được phân loại đúng là negative. Mẫu số là tổng số sample thực tế negative.
Với ví dụ (\( TN=80, FP=5 \)):
\[ \text{Specificity} = 80 / (80 + 5) \approx 0.941 \]94% sample thực tế negative được model phân loại đúng.
Specificity hay xuất hiện trong y học (đi cặp với sensitivity = recall), và trong ROC curve (Bài 26): trục tung là TPR = recall, trục hoành là FPR = \( 1 - \text{Specificity} \). Trong sklearn không có hàm specificity_score riêng; muốn tính, dùng recall_score(y_true, y_pred, pos_label=0) hoặc compute trực tiếp từ confusion matrix.
Trade-off Precision vs Recall theo threshold
Hầu hết classifier output không phải nhãn cứng mà là xác suất \( p(y=1 \mid x) \). Để ra nhãn, ta so sánh với một threshold (mặc định 0.5): nếu \( p \ge t \) → predict 1, ngược lại 0.
Threshold điều khiển trực tiếp precision và recall:
- Threshold thấp (vd \( t = 0.3 \)): model dễ nói "1", nên dự đoán nhiều "1" hơn. \( TP \) thường tăng (bắt được thêm positive) → recall tăng; nhưng \( FP \) cũng tăng (gán "1" cho cả sample negative) → precision giảm.
- Threshold cao (vd \( t = 0.7 \)): model chỉ nói "1" khi rất chắc, nên dự đoán ít "1" hơn. \( FP \) giảm mạnh → precision tăng; nhưng \( FN \) tăng (bỏ sót case positive độ tự tin thấp) → recall giảm.
Đây là trade-off đặc trưng: với cùng một model, không có cách nào tăng đồng thời cả precision và recall chỉ bằng cách đổi threshold. Muốn cải thiện cả hai cần model tốt hơn (feature tốt hơn, thuật toán tốt hơn, nhiều data hơn), không phải đổi threshold.
Hệ quả thực hành: chọn threshold theo metric cần ưu tiên trong domain, không mặc nhiên dùng 0.5.
- Cần ưu tiên recall (y tế) → chọn threshold thấp, chấp nhận precision thấp hơn.
- Cần ưu tiên precision (spam) → chọn threshold cao, chấp nhận bỏ sót một số spam.
- Cần balance → dùng F1-score (Bài 25 đi sâu).
Cách chọn threshold quy củ: vẽ Precision–Recall curve hoặc F1 curve trên validation set, chọn điểm phù hợp với yêu cầu kinh doanh.
Khi nào dùng metric nào
Bảng tóm tắt lựa chọn metric theo bài toán:
- Accuracy: chỉ dùng khi data BALANCED và cost FP \( \approx \) cost FN. Ví dụ: phân loại ảnh chó/mèo với 50/50.
- Precision: dùng khi cost FP cao. Spam, recommendation, ads — false alarm gây bực mình user hoặc tốn nguồn lực.
- Recall: dùng khi cost FN cao. Medical screening, fraud detection, safety — bỏ sót gây hậu quả nghiêm trọng.
- F1-score (Bài 25): dùng khi muốn balance precision và recall, không favor bên nào. Phù hợp khi data imbalanced và cả hai loại lỗi đều quan trọng.
- ROC-AUC (Bài 26): dùng khi cần metric threshold-independent, hoặc khi so sánh các model với threshold khác nhau. Đặc biệt phù hợp khi muốn đánh giá khả năng phân biệt class tổng thể.
Nguyên tắc gốc: đầu tiên hãy ước lượng cost FP và cost FN trong domain của bạn (tính ra tiền hoặc tác động nếu được), sau đó mới chọn metric phù hợp. Đừng đảo ngược thứ tự — đừng chọn metric theo thói quen rồi mới biện minh.
sklearn — accuracy_score, precision_score, recall_score
Ba hàm cốt lõi trong sklearn.metrics:
from sklearn.metrics import accuracy_score, precision_score, recall_score
acc = accuracy_score(y_true, y_pred)
prec = precision_score(y_true, y_pred) # default: binary, pos_label=1
rec = recall_score(y_true, y_pred)
Tham số quan trọng:
pos_label=1(mặc định) — class được coi là "positive" khi tính precision/recall. Nếu nhãn của bạn là 0/1 và positive là class 0, phải setpos_label=0.average="binary"(mặc định cho binary) — trả về 1 số duy nhất cho positive class. Với multi-class, dùng "macro", "micro", hoặc "weighted" (bước 10).zero_division=0(warn mặc định) — quyết định trả về gì khi mẫu số = 0. Đặtzero_division=0hoặc1để tắt warning.
Một quy ước hay quên: tham số là (y_true, y_pred) theo thứ tự đó. Đảo thứ tự thường không báo lỗi nhưng cho kết quả sai (accuracy thì đối xứng nên trùng, precision/recall thì không).
Multi-class — macro, micro, weighted
Với multi-class (\( C \) class), precision và recall tính per-class rồi gộp lại theo một trong ba cách:
- Macro: tính precision (hoặc recall) cho từng class độc lập, sau đó trung bình cộng không trọng số: \[ \text{Precision}_{\text{macro}} = \frac{1}{C} \sum_{c=1}^{C} \text{Precision}_c \] Mỗi class đóng góp như nhau, bất kể support (số sample) khác nhau. Phù hợp khi muốn coi mọi class quan trọng ngang nhau.
- Micro: gộp \( TP, FP, FN \) qua toàn bộ class trước rồi mới tính tỉ số: \[ \text{Precision}_{\text{micro}} = \frac{\sum_c TP_c}{\sum_c (TP_c + FP_c)} \] Với multi-class single-label (mỗi sample có 1 nhãn), micro-precision = micro-recall = accuracy. Phù hợp khi muốn metric tổng thể bị chi phối bởi class lớn.
- Weighted: trung bình per-class có trọng số theo support (số sample thực tế của class): \[ \text{Precision}_{\text{weighted}} = \sum_{c=1}^{C} \frac{n_c}{N} \cdot \text{Precision}_c \] Phù hợp khi data imbalanced và muốn metric phản ánh hiệu suất "trung bình mỗi sample".
Cách chọn:
- Mọi class quan trọng như nhau (vd nhận diện 10 loại bệnh hiếm gặp) → macro.
- Muốn metric phản ánh trải nghiệm trung bình của user → weighted.
- Muốn cảm nhận hiệu suất tổng thể, biết rõ class lớn sẽ chi phối → micro (hoặc tương đương: accuracy).
Trong sklearn: precision_score(y_true, y_pred, average="macro"), đổi average theo nhu cầu.
classification_report
Thay vì gọi từng hàm, sklearn có sẵn báo cáo tổng hợp:
from sklearn.metrics import classification_report
print(classification_report(y_true, y_pred))
Output mẫu (binary classification):
precision recall f1-score support
0 0.94 0.93 0.94 108
1 0.92 0.94 0.93 90
accuracy 0.93 198
macro avg 0.93 0.93 0.93 198
weighted avg 0.93 0.93 0.93 198
Cách đọc:
- Mỗi dòng class (0, 1, ...): precision, recall, f1 tính khi class đó được coi là positive; support là số sample thực tế.
- Dòng
accuracy: tỉ lệ predict đúng tổng thể (1 số duy nhất). - Dòng
macro avg: trung bình không trọng số các metric per-class. - Dòng
weighted avg: trung bình có trọng số theo support.
Tham số hữu ích: target_names=["class_a", "class_b"] để đổi tên class trong output; digits=4 để in nhiều số thập phân hơn; output_dict=True để trả về dict thay vì string (tiện log vào JSON).
Phân tích industry-specific
Một số bài toán điển hình và metric ưu tiên:
- Email spam: cost FP (chặn email tốt) thường lớn hơn cost FN (cho qua spam). User mất email công việc gây hậu quả nghiêm trọng; spam lọt qua chỉ phiền. → ưu tiên precision.
- Tầm soát ung thư: cost FN (bỏ sót bệnh nhân) cực lớn — chậm điều trị có thể tử vong. Cost FP (báo nhầm) chỉ là thêm 1 xét nghiệm xác minh, ít nghiêm trọng. → ưu tiên recall.
- CTR prediction (quảng cáo): cả FP (hiện quảng cáo không click) và FN (bỏ qua user có thể click) đều ảnh hưởng doanh thu. Thường tối ưu F1 hoặc log-loss, kèm theo các metric kinh doanh trực tiếp (eCPM, ROAS).
- Search engine: thường dùng precision @ top-K (precision trong K kết quả đầu) hoặc NDCG. User thường chỉ nhìn 10 kết quả đầu, nên recall toàn corpus không quan trọng bằng độ chính xác của top results.
- Phát hiện gian lận thẻ tín dụng: cost FN (bỏ sót giao dịch gian lận) thường vài chục đến hàng nghìn USD; cost FP (chặn giao dịch hợp lệ) gây phiền cho khách. Cân bằng cụ thể tuỳ ngân hàng — thường F1 hoặc cost-weighted precision-recall.
Nguyên tắc thiết kế: ngồi với product/business team, ước lượng số tiền hoặc tác động cho mỗi loại lỗi, rồi chọn metric. Không có metric "đúng" tuyệt đối — chỉ có metric phù hợp với bài toán cụ thể.
Common mistake khi báo cáo metric
Ba lỗi thường gặp khi báo cáo:
Mistake 1 — Chỉ báo cáo accuracy mà không note class imbalance. "Model của tôi đạt 98% accuracy" trên dataset 98% là class 0 — về cơ bản chưa làm gì hơn baseline. Khi báo cáo accuracy, luôn kèm class distribution và baseline accuracy (always predict majority).
Mistake 2 — Tối ưu precision mà quên recall. Push threshold lên 0.95 để precision ~ 0.99, nhưng recall còn 0.02 — model chỉ predict "1" cho vài sample chắc chắn nhất, bỏ sót 98% positive thực. Trên giấy tờ đẹp, trên thực tế vô dụng.
Mistake 3 — Tối ưu recall mà quên precision. Hạ threshold xuống 0.05 để recall ~ 0.99, nhưng model gần như predict "1" cho mọi sample — precision rớt về tỉ lệ class positive (vd 5%). Bắt được mọi positive nhưng cũng false alarm cho mọi negative.
Luôn báo cả cặp precision–recall (hoặc dùng F1 / classification_report), không bao giờ báo lẻ. Và luôn kèm cách thực hành: trên test set độc lập, với threshold đã được fix từ validation set, không phải threshold được chọn sau khi nhìn test.
Code Python — Breast cancer + đổi threshold
Train Logistic Regression trên dataset breast cancer của sklearn, compute các metric và quan sát trade-off khi đổi threshold.
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (
accuracy_score, precision_score, recall_score,
confusion_matrix, classification_report
)
# 1. Load data
data = load_breast_cancer()
X, y = data.data, data.target
# Quy uoc sklearn: 1 = benign, 0 = malignant
# Doi nhan de positive = malignant (truong hop ung thu)
y = 1 - y
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 2. Scale + train Logistic Regression
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
model = LogisticRegression(max_iter=1000)
model.fit(X_train_s, y_train)
# 3. Default threshold = 0.5
y_pred = model.predict(X_test_s)
print("== Default threshold = 0.5 ==")
print(f"Accuracy : {accuracy_score(y_test, y_pred):.4f}")
print(f"Precision: {precision_score(y_test, y_pred):.4f}")
print(f"Recall : {recall_score(y_test, y_pred):.4f}")
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred, target_names=["benign", "malignant"]))
# 4. Doi threshold, quan sat trade-off
y_proba = model.predict_proba(X_test_s)[:, 1] # xac suat malignant
for t in [0.3, 0.5, 0.7]:
y_t = (y_proba >= t).astype(int)
prec = precision_score(y_test, y_t, zero_division=0)
rec = recall_score(y_test, y_t)
acc = accuracy_score(y_test, y_t)
print(f"threshold={t:.1f} | acc={acc:.3f} | prec={prec:.3f} | rec={rec:.3f}")
Output điển hình (số có thể dao động vài phần nghìn):
== Default threshold = 0.5 ==
Accuracy : 0.9737
Precision: 0.9762
Recall : 0.9535
...
threshold=0.3 | acc=0.965 | prec=0.932 | rec=0.977
threshold=0.5 | acc=0.974 | prec=0.976 | rec=0.953
threshold=0.7 | acc=0.965 | prec=1.000 | rec=0.907
Quan sát:
- Threshold 0.3 → recall cao nhất (0.977) nhưng precision thấp nhất (0.932). Bắt được nhiều ca ung thư, đổi lại nhiều false alarm.
- Threshold 0.7 → precision tuyệt đối (1.0) nhưng recall thấp nhất (0.907). Mỗi lần nói "ung thư" đều đúng, nhưng bỏ sót ~9% ca thực sự.
- Threshold 0.5 nằm giữa — không tự động là lựa chọn tối ưu.
Với bài toán tầm soát ung thư, threshold 0.3 (ưu tiên recall) thường hợp lý hơn — chấp nhận xét nghiệm thêm vài ca không bệnh để không bỏ sót ca thật.
Bài tập thực hành
Bài 1 — Tính bằng tay từ confusion matrix. Cho:
\[ \begin{bmatrix} TN & FP \\ FN & TP \end{bmatrix} = \begin{bmatrix} 80 & 5 \\ 10 & 5 \end{bmatrix} \]Tính accuracy, precision, recall, specificity cho class 1. Sau đó verify bằng sklearn:
import numpy as np
from sklearn.metrics import accuracy_score, precision_score, recall_score
# Tao y_true, y_pred khop voi confusion matrix tren
y_true = np.array([0]*85 + [1]*15)
y_pred = np.array([0]*80 + [1]*5 + [0]*10 + [1]*5)
print(accuracy_score(y_true, y_pred))
print(precision_score(y_true, y_pred))
print(recall_score(y_true, y_pred))
Bài 2 — classification_report trên breast cancer. Lặp lại code ở bước 14 nhưng dùng cả 3 model: LogisticRegression, RandomForestClassifier, SVC(probability=True). In classification_report cho cả 3. So sánh precision, recall, f1 của class "malignant". Model nào phù hợp nhất cho tầm soát ung thư? Giải thích lựa chọn.
Bài 3 — Argument metric cho spam detector. Bạn xây spam detector cho dịch vụ email doanh nghiệp. Phân tích:
- Cost của 1 FP (email tốt bị filter vào spam) là gì? Ước lượng (mất hợp đồng, mất thông tin quan trọng)?
- Cost của 1 FN (spam lọt vào inbox) là gì? (mất 5 giây user xoá)?
- Metric nào nên ưu tiên — precision, recall, hay F1?
- Threshold mặc định 0.5 có hợp lý không? Nên cao hơn hay thấp hơn?
Viết 1 đoạn ngắn (5–10 câu) bảo vệ lựa chọn của bạn dựa trên cost analysis, không dựa trên cảm tính.
Gợi ý đáp án Bài 1:
- Accuracy = \( (80 + 5) / 100 = 0.85 \).
- Precision = \( 5 / (5 + 5) = 0.5 \).
- Recall = \( 5 / (5 + 10) \approx 0.333 \).
- Specificity = \( 80 / (80 + 5) \approx 0.941 \).
Nhận xét: accuracy 85% nhìn ổn nhưng precision 0.5 và recall 0.33 cho thấy model dở với class minority — đúng ví dụ kinh điển vì sao accuracy gây hiểu nhầm.
Bài tiếp theo
Bài 25: F1-Score và trade-off Precision/Recall — gộp precision và recall thành một số duy nhất bằng harmonic mean: vì sao harmonic mean mà không phải arithmetic mean, F-beta tổng quát (\( F_2 \) ưu tiên recall, \( F_{0.5} \) ưu tiên precision), cách vẽ Precision–Recall curve và chọn threshold theo F1 trên validation set.
Tài liệu tham khảo
- scikit-learn — Classification metrics
- scikit-learn — precision_score API reference
- scikit-learn — recall_score API reference
- scikit-learn — classification_report API reference
- Wikipedia — Precision and recall
- Wikipedia — Sensitivity and specificity
- Google ML Crash Course — Accuracy, Precision, Recall
- Gareth James et al. — An Introduction to Statistical Learning (Chương 4: Classification)
