Hai bộ phân loại có cùng accuracy vẫn có thể đưa ra xác suất rất khác nhau. Với một mẫu mang nhãn 1, dự đoán 0.1 và 0.01 đều sai nếu dùng ngưỡng 0.5. Tuy nhiên, trường hợp thứ hai gán ít xác suất hơn nhiều cho sự kiện thực sự xảy ra. Bài này dùng log loss để nhìn rõ sự khác biệt ấy bằng vài phép tính nhỏ.
Nội dung đối chiếu ngày 08/11/2026 với tài liệu scikit-learn 1.9.1. Ví dụ chạy bằng Python 3.9.6 và thư viện chuẩn, không gọi scikit-learn, huấn luyện mô hình hay đo chất lượng trên dữ liệu sản phẩm. Phiên bản Python nêu ở đây mô tả môi trường kiểm tra, không phải khuyến nghị triển khai phiên bản cũ.
Xác định xác suất đang nói về nhãn nào
Quy ước p là xác suất nhãn 1. Khi nhãn thật bằng 1, lấy xác suất được gán cho nhãn đúng là p; khi nhãn thật bằng 0, lấy 1 - p. Gọi giá trị đó là q, loss của một mẫu bằng -ln(q). Logarithm ở đây có cơ số e.
Đây là cách đọc công thức nhị phân trong tài liệu log_loss. Loss càng nhỏ càng tốt trên cùng bài toán và tập đánh giá. Đừng đưa nhãn dự đoán 0/1 vào vị trí xác suất chỉ vì chúng cũng nằm trong đoạn từ 0 đến 1.
Viết một hàm minh họa có hợp đồng rõ ràng
import math
def binary_log_loss(y, p):
if type(y) is not int or y not in (0, 1):
raise ValueError("y must be integer 0 or 1")
if type(p) not in (int, float) or not math.isfinite(p):
raise ValueError("p must be a finite number")
if not 0 <= p <= 1:
raise ValueError("p must be in [0, 1]")
q = p if y == 1 else 1 - p
return math.inf if q == 0 else -math.log(q)
for p in (0.9, 0.1, 0.01):
print(p, round(binary_log_loss(1, p), 6))
Kết quả lần lượt là 0.105361, 2.302585, 4.60517. Hai dự đoán sai vẫn cùng một nhãn sau khi áp ngưỡng, nhưng loss đã tăng gấp đôi khi xác suất cho nhãn đúng giảm từ 0.1 xuống 0.01. Đây là phép tính trên ba đầu vào tự đặt, không phải số đo từ một mô hình thực tế.
Hàm chủ động từ chối boolean và chuỗi số để lỗi dữ liệu không bị che bởi ép kiểu. Python math mô tả log và isfinite; phần kiểm tra miền giá trị là hợp đồng riêng của ví dụ.
Cùng accuracy nhưng loss khác nhau
Chạy tiếp trong cùng file:
labels = [1, 0]
scores_a = [0.6, 0.4]
scores_b = [0.99, 0.01]
for scores in (scores_a, scores_b):
accuracy = sum(int(p >= 0.5) == y for y, p in zip(labels, scores)) / len(labels)
loss = sum(binary_log_loss(y, p) for y, p in zip(labels, scores)) / len(labels)
print(accuracy, round(loss, 6))
Hai dòng đều có accuracy 1.0; loss tương ứng 0.510826 và 0.01005. Trong đúng hai mẫu này, bộ B gán nhiều xác suất hơn cho cả hai nhãn đúng. Không thể từ ví dụ suy ra rằng tăng độ tự tin luôn cải thiện mô hình: đổi nhãn thật của một mẫu rồi tính lại sẽ thấy mặt còn lại của mức phạt.
Kiểm tra các điểm biên trước khi nối vào báo cáo
Hãy thêm kiểm tra cho (y=1, p=1) và (y=0, p=0): cả hai cho loss bằng 0. Với (1, 0) hoặc (0, 1), hàm minh họa trả vô cực. Không thay bằng số 0 khi xuất báo cáo vì thao tác đó sẽ làm một sai lầm cực đoan trông như dự đoán hoàn hảo.
scikit-learn có clipping theo độ chính xác của dtype; vì vậy điểm biên có thể cho giá trị hữu hạn khác hàm ở trên. Khi so sánh hai hệ thống, ghi cả quy ước clipping, loại số và cách lấy trung bình. Đừng tuyên bố hàm giáo khoa này tái hiện toàn bộ API của thư viện.
Lỗi thường gặp và cách dùng tiếp
- Đảo
pcủa lớp 0 thành lớp 1: lưu rõ tên lớp cạnh cột xác suất. - Lấy trung bình khi số nhãn và số điểm khác nhau: kiểm tra chiều dài trước khi dùng
zip, vì ví dụ hiện chỉ dùng hai mảng cố định bằng nhau. - Chỉ xem loss trung bình: đọc thêm các mẫu có loss lớn và xác nhận nhãn nguồn.
- So loss của hai tập khác phân bố rồi kết luận mô hình tốt hơn: giữ cùng bộ đánh giá và cùng cách tính.
Bạn có thể bắt đầu bằng bảng gồm nhãn thật, xác suất nhãn 1 và loss từng mẫu. Bảng này giúp truy ngược một con số tổng về những dự đoán cụ thể, trước khi quyết định có cần điều chỉnh mô hình hay quy trình gán nhãn.

