2026-04-09
Banking DomainTừ con số đến quyết định: Khi dữ liệu bắt đầu 'nói chuyện' với tiền
Scorecard hay Boosting chỉ là khởi đầu. Khám phá cách những con số biến thành quyết định duyệt vay thực tế qua lớp Policy, Champion-Challenger, và hệ thống giám sát chặt chẽ.
Tóm lược
- Con số điểm (Score) không phải là tất cả. Chính Policy (Chính sách) và các ngưỡng (Cutoff) mới là thứ thực sự quyết định tiền của ngân hàng sẽ đi đâu.
- Thay đổi một con số ngưỡng (Cutoff) cũng quan trọng y như việc thay đổi cả một Model AI — hãy đánh số phiên bản và log
policy_versionsong song vớimodel_version. - Champion-Challenger là framework để thử nghiệm policy mới an toàn: split 5% traffic cho Challenger, so sánh performance trước khi promote.
- Đừng chỉ nhìn vào AUC. Hãy theo dõi: phân phối score, tỷ lệ override, vintage bad rate theo cohort.
Giới thiệu
Disclaimer: Bài viết này tập trung vào khía cạnh kỹ thuật và quản trị (governance) đằng sau quá trình từ điểm số tới quyết định. Không đưa ra ngưỡng cụ thể hay lời khuyên đầu tư cho bất kỳ dự án nào nhé.
Dành cho các bạn Data Scientist đang làm mô hình chấm điểm (Scorecard / Boosting): Bạn đã bao giờ cảm thấy hụt hẫng khi Model của mình đạt AUC cực cao nhưng khi triển khai thực tế, tỷ lệ duyệt vay lại không như ý? Đó là vì giữa Model và Quyết định còn một lớp màn mang tên Policy. Hãy cùng vén bức màn đó lên.
Khái niệm cốt lõi: Cỗ máy ra quyết định
1. Điểm số đầu ra (Score) và Calibration
Model trả về xác suất nợ xấu (PD — Probability of Default) hoặc một điểm số đã được scaling. Vấn đề: raw output từ XGBoost thường không "well-calibrated" — tức là predict_proba = 0.3 không có nghĩa là 30% khách hàng trong nhóm này thực sự sẽ nợ xấu.
Hai kỹ thuật calibration phổ biến:
- Platt Scaling: Fit một logistic regression trên top của model predictions
- Isotonic Regression: Non-parametric, tốt hơn khi calibration curve phức tạp
from sklearn.calibration import CalibratedClassifierCV
from sklearn.isotonic import IsotonicRegression
# Platt scaling
calibrated = CalibratedClassifierCV(base_model, method='sigmoid', cv='prefit')
calibrated.fit(X_val, y_val)
# Check calibration: mean predicted PD vs actual default rate per bucket
buckets = pd.cut(calibrated.predict_proba(X_val)[:, 1], bins=10)
calib_check = pd.DataFrame({
'predicted_pd': calibrated.predict_proba(X_val)[:, 1],
'actual_default': y_val
}).groupby(buckets)['actual_default'].agg(['mean', 'count'])
2. Lớp Chính sách (Policy Layer): Decision Matrix
Score chỉ là đầu vào. Policy Layer mới là nơi ra quyết định thực sự, thường có dạng:
| Band | Score Range | Điều kiện bổ sung | Quyết định |
|---|---|---|---|
| A | > 700 | Income ≥ 10M | AUTO_APPROVE |
| B | 550-700 | Income ≥ 7M | AUTO_APPROVE |
| C | 400-549 | — | REFER (manual review) |
| D | < 400 | — | HARD_DECLINE |
| — | Any | Tuổi < 20 hoặc CIC blacklist | HARD_DECLINE |
Trong code, policy layer thường là một SQL rule engine hoặc decision table:
SELECT
application_id,
score,
CASE
-- Hard cutoff rules (override score)
WHEN age < 20 OR cic_flag = 'BLACKLIST' THEN 'HARD_DECLINE'
WHEN score < 400 THEN 'HARD_DECLINE'
-- Score + income matrix
WHEN score >= 700 AND monthly_income >= 10000000 THEN 'AUTO_APPROVE'
WHEN score BETWEEN 550 AND 699
AND monthly_income >= 7000000 THEN 'AUTO_APPROVE'
WHEN score BETWEEN 400 AND 549 THEN 'REFER'
ELSE 'HARD_DECLINE'
END AS decision,
'v2.3.1' AS policy_version,
'xgb_ntb_v4' AS model_version,
CURRENT_TIMESTAMP AS decision_ts
FROM scoring_results
3. Champion-Challenger (C/C): Thử nghiệm an toàn
Thay vì deploy policy mới ngay lập tức cho toàn bộ traffic, C/C framework cho phép thử nghiệm song song:
- Champion: Policy hiện tại (95% traffic)
- Challenger: Policy mới muốn thử (5% traffic)
Sau 4-8 tuần (tùy loan cycle), so sánh: approval rate, expected loss, vintage bad rate của hai nhóm. Nếu Challenger tốt hơn → promote lên Champion.
import random
def assign_variant(application_id: str, challenger_pct: float = 0.05) -> str:
"""Deterministic assignment based on application_id hash."""
hash_val = int(hashlib.md5(application_id.encode()).hexdigest(), 16)
return "CHALLENGER" if (hash_val % 100) < (challenger_pct * 100) else "CHAMPION"
4. Giám sát thực địa (Monitoring)
Khi hệ thống chạy, cần theo dõi 3 lớp:
| Tín hiệu | Metric | Alert threshold |
|---|---|---|
| Population shift | PSI trên score distribution | > 0.20 |
| Approval rate | Tỷ lệ AUTO_APPROVE / tổng | ± 5% so với baseline |
| Override rate | % REFER được approve thủ công | > 15% |
| Vintage bad rate | Bad rate của cohort T-3M | > expected + 1.5σ |
Chi tiết vận hành: Logging và Audit Trail
Trong mọi bảng quyết định, không được chỉ lưu score. Cần log đầy đủ để audit và học từ exceptions:
# Decision log schema
decision_log = {
"application_id": "APP_20260409_001",
"customer_id": "CUST_123456",
"model_version": "xgb_ntb_v4",
"policy_version": "v2.3.1",
"raw_score": 487,
"calibrated_pd": 0.142,
"decision": "REFER",
"hard_decline_reason": None,
"decision_ts": "2026-04-09T10:23:45Z",
"experiment_variant": "CHAMPION",
}
# Override log (khi có manual review)
override_log = {
"application_id": "APP_20260409_001",
"original_decision": "REFER",
"override_decision": "APPROVE",
"override_reason_code": "RC_04", # e.g., RC_04 = "Customer has government salary"
"override_reason_text": "Verified government employee, salary slip attached",
"reviewer_id": "RM_0045",
"override_ts": "2026-04-09T14:10:00Z",
}
Override reason codes quan trọng vì chúng là tín hiệu để cải thiện model và policy ở iteration sau. Nếu RC_04 xuất hiện > 20% overrides, đó là hint để bổ sung is_government_employee vào policy matrix.
Những rủi ro "chết người"
- Thờ phụng AUC: Mải mê tối ưu AUC trong lab mà quên mất các ngưỡng Cutoff đang drift tự do ngoài production.
- Policy drift ngầm: Ai đó thay đổi income threshold mà không update
policy_version→ mất khả năng audit. - Ghi đè tùy tiện: Override không có reason code = mất dữ liệu học. Sau 6 tháng bạn không còn biết tại sao override rate tăng đột biến.
- Champion-Challenger bias: Quên kiểm tra xem assignment có truly random không — systematic bias trong hash function dẫn đến kết quả C/C vô nghĩa.
Kết luận
Vận hành một hệ thống chấm điểm an toàn giống như lái một chiếc máy bay: Model là động cơ, Policy là hệ thống điều hướng, và Monitoring là bảng đồng hồ buồng lái. Champion-Challenger là wind tunnel để test cánh mới trước khi gắn lên máy bay thật. Thiếu bất kỳ thành phần nào, bạn đang bay mù.