Một biểu đồ có đủ số liệu nhưng gắn sai tên lớp vẫn dẫn người đọc tới kết luận sai. Khi đánh giá model phân loại ticket, hãy lưu thứ tự nhãn cùng ma trận, thay vì tạo hai danh sách tên lớp và số đếm độc lập ở backend và frontend.
Bài đối chiếu tài liệu scikit-learn 1.9.1 ngày 03/11/2026. Ví dụ dùng Python 3.9.6 và thư viện chuẩn; chưa chạy scikit-learn, huấn luyện model hay tạo biểu đồ bằng Matplotlib. Dữ liệu ticket được tự tạo để kiểm tra cách đếm, không phải kết quả của một hệ thống AI thực tế.
Chốt hợp đồng trước khi vẽ
Theo quy ước của confusion_matrix trong scikit-learn, hàng là nhãn thật, cột là nhãn dự đoán. Ô ở hàng billing, cột technical đếm những mẫu billing bị dự đoán thành technical. Một thư viện khác có thể trình bày trục khác, nên tên trục phải xuất hiện ngay trên báo cáo.
Trong ví dụ, thứ tự cố định là billing, technical, other. Cùng thứ tự được dùng để cấp index cho cả hai trục. Không lấy thứ tự từ một tập hợp không có hợp đồng sắp xếp rồi hy vọng giao diện tự khớp.
Đếm bằng fixture có thể kiểm tra từng dòng
Lưu thành demo_matrix.py, chạy python3 demo_matrix.py:
def count_matrix(truth, predicted, labels):
if len(truth) != len(predicted):
raise ValueError("Hai day phai cung do dai")
if not labels or len(set(labels)) != len(labels):
raise ValueError("Danh sach nhan phai khong rong va khong trung")
positions = {label: index for index, label in enumerate(labels)}
matrix = [[0 for _ in labels] for _ in labels]
for actual, guess in zip(truth, predicted):
if actual not in positions or guess not in positions:
raise ValueError("Nhan ngoai hop dong")
matrix[positions[actual]][positions[guess]] += 1
return matrix
labels = ["billing", "technical", "other"]
truth = ["billing", "billing", "technical", "technical", "other"]
predicted = ["billing", "technical", "technical", "other", "billing"]
matrix = count_matrix(truth, predicted, labels)
assert matrix == [[1, 1, 0], [0, 1, 1], [1, 0, 0]]
assert [sum(row) for row in matrix] == [2, 2, 1]
assert sum(map(sum, matrix)) == len(truth)
print(labels)
print(matrix)
Các assertion đã chạy qua. Hàng đầu có tổng 2 vì fixture có hai ticket billing thật. Ô [0][1] bằng 1 vì có một billing bị đoán thành technical. Đọc thử ngược ô [1][0] sẽ thấy bằng 0; hai chiều nhầm lẫn không nhất thiết đối xứng.
Hàm minh họa chủ ý từ chối nhãn ngoài hợp đồng. Đây là lựa chọn của demo, không khẳng định trùng mọi hành vi của thư viện. Nó nhận các nhãn chuỗi đã chuẩn hóa và không xử lý sample weight hoặc multilabel.
Giữ lớp vắng mặt trong báo cáo
Nếu một đợt kiểm tra nhỏ không có mẫu other, vẫn có thể giữ ma trận 3 × 3 để giao diện không đổi cấu trúc. Chạy thêm trong cùng file:
small = count_matrix(["billing"], ["technical"], labels)
assert small == [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
print(small)
Kết quả này đã được kiểm tra. Hàng toàn số 0 nghĩa là không có quan sát nhãn thật ấy trong fixture, không phải model đạt chất lượng hoàn hảo cho lớp đó. Nếu cần phần trăm theo hàng, phải quy định cách hiển thị hàng có mẫu số bằng 0, chẳng hạn “chưa có mẫu”.
Khi dùng scikit-learn thật, đối chiếu labels của phép tính và display_labels của phần hiển thị. Đừng chỉ đổi tên nhãn trên biểu đồ mà giữ số đếm theo thứ tự cũ. Đồng thời ghi rõ đang hiển thị count, chuẩn hóa theo hàng hay theo cột; các cách ấy trả lời những câu hỏi khác nhau.
Kiểm tra trước khi đưa lên dashboard
Lưu ID mẫu để ghép đúng nhãn thật với dự đoán, rồi kiểm tổng số đếm bằng số mẫu đã nhận. Thêm một case chỉ có một lỗi ngoài đường chéo để kiểm tra trục. Khi đổi thứ tự lớp, cần đổi nhất quán cả hai chiều và nhãn hiển thị.
Một tổng accuracy không chỉ ra cặp lớp nào hay bị lẫn. Ma trận bổ sung thông tin đó, nhưng vẫn phụ thuộc nhãn thật, tập kiểm tra và số mẫu từng lớp. Với báo cáo sản phẩm, đi từ một ô bất thường về các ticket cụ thể sẽ hữu ích hơn việc chỉ tô màu ô ấy đậm hơn.


