2026-04-25
Banking DomainChampion-Challenger: Cách prove model mới mà không cần đặt cược toàn bộ
Model mới backtest AUC cao hơn 3%. Risk committee hỏi: 'Trên production thật thì sao?' Champion-Challenger là cách trả lời câu hỏi đó mà không cần gamble.
Tóm tắt
Champion-Challenger (C/C) là framework thử nghiệm model mới trên traffic production thật, với tỷ lệ nhỏ, trong một khoảng thời gian đủ dài để có outcome. Champion là model đang chạy. Challenger là ứng viên thay thế. Sau khi có đủ dữ liệu, so sánh bằng bằng chứng — không phải bằng niềm tin.
Tại sao backtest không đủ
Giả sử bạn vừa train xong model mới. Trên tập OOT, AUC tăng từ 0.72 lên 0.75. Bạn mang kết quả đó vào risk committee và đề xuất deploy.
Risk committee hỏi: "OOT của bạn là data từ 6 tháng trước. Population hiện tại có giống không? Policy có thay đổi không? Nếu model mới cho approval rate thấp hơn, chúng ta mất bao nhiêu doanh thu?"
Đây không phải câu hỏi khó chịu — đây là câu hỏi đúng. Backtest trả lời "model này đã hoạt động tốt trong quá khứ." C/C trả lời "model này đang hoạt động tốt trong hiện tại, trên khách hàng thật, với policy hiện tại."
Metaphor: Tuyển thủ dự bị
Không có HLV nào đưa tân binh vào sân chính từ phút đầu mà không có thời gian warm-up thực chiến. Tân binh được cho vào sân ở những trận ít áp lực hơn — hoặc trong 15 phút cuối khi tỷ số đã được định đoạt.
C/C hoạt động y hệt: Challenger được "vào sân" với một phần nhỏ traffic, trong điều kiện thật, nhưng rủi ro được kiểm soát. Nếu tốt, lên sân chính. Nếu không, bench lại không ai bị thiệt hại nặng.
Framework 4 bước
Bước 1: Define success criteria TRƯỚC khi test
Đây là bước quan trọng nhất — và bị bỏ qua nhiều nhất.
Trước khi split traffic, viết rõ: "Challenger win khi..."
- Gini trên real traffic cao hơn Champion ≥ 2 percentage points
- Approval rate không giảm hơn 1%
- DPD30 rate (early delinquency) trong 3 tháng đầu không tệ hơn Champion
Không define sau khi thấy kết quả. Nếu bạn define criteria sau, bạn đang tìm kiếm confirmation, không phải evidence.
Bước 2: Traffic split
Tỷ lệ split phụ thuộc vào risk appetite và volume:
| Volume | Split tiêu biểu | Lý do |
|---|---|---|
| Cao (>5,000 app/tháng) | 90/10 | Đủ sample cho Challenger |
| Trung bình (1,000–5,000) | 95/5 | Giảm exposure của Challenger |
| Thấp (<1,000) | Cân nhắc lại | Cần sample size đủ lớn để có ý nghĩa thống kê |
Quan trọng: Split phải ngẫu nhiên và stratified. Không để Challenger nhận toàn bộ applicant từ một channel cụ thể, một region, hay một loan officer — vì đó không còn là fair comparison nữa. Randomize theo application ID hoặc customer ID.
Bước 3: Outcome window
Credit không phải là e-commerce — bạn không biết "kết quả" trong vài ngày. Cần đủ thời gian để outcome materialise:
| Metric | Minimum window |
|---|---|
| DPD30 (early signal) | 1 tháng |
| DPD90 (default signal) | 3 tháng |
| Charge-off rate | 6–12 tháng |
| Vintage Gini | 6–12 tháng |
Đừng rút kết luận sau 3 tuần vì "trông có vẻ tốt." Early delinquency rates trong 3 tuần đầu không đại diện cho long-term performance. Kiên nhẫn là kỷ luật quan trọng nhất trong C/C.
Bước 4: Decision framework
Sau khi có đủ outcome:
| Kết quả | Hành động |
|---|---|
| Challenger win rõ ràng (all criteria met) | Promote Challenger 100%, archive Champion |
| Challenger win nhẹ (đa số criteria met) | Tăng traffic Challenger lên 30–50%, extend test |
| Challenger neutral (mixed signals) | Giữ Champion, deep-dive vào segment nào Challenger tệ |
| Challenger thua (at least 1 critical criterion failed) | Discard, post-mortem, không deploy |
Edge cases thực tế
Seasonality làm lệch kết quả. Nếu test vào mùa Tết, applicant profile thay đổi hoàn toàn — kết quả không generalize ra ngoài mùa đó. Tốt nhất là test đủ dài để bao phủ ít nhất 1 cycle seasonal, hoặc explicitly document limitation.
Policy thay đổi trong khi test. Đây là killer của mọi C/C. Nếu risk appetite thay đổi (nới lỏng hay thắt chặt) trong khi đang test, Champion và Challenger không còn được test trong cùng điều kiện nữa. Phải lock policy trong suốt C/C period, hoặc restart test.
Selection bias trong routing. Nếu loan officers biết đơn nào đang chạy Challenger và đơn nào đang chạy Champion, họ có thể (vô tình) xử lý khác nhau. C/C nên blind với người xử lý hồ sơ.
Không đủ volume cho statistical significance. Một C/C 95/5 split với 500 đơn/tháng có nghĩa là chỉ 25 đơn vào Challenger mỗi tháng. 3 tháng = 75 đơn. Không đủ để kết luận bất cứ điều gì. Tính sample size requirement trước khi commit vào split ratio.
Code thực tế: Tính sample size và kiểm tra kết quả C/C
import math
from scipy.stats import proportions_ztest, norm
def required_sample_size(
p_champion: float,
p_challenger: float,
alpha: float = 0.05,
power: float = 0.80
) -> int:
"""
Tính số lượng application tối thiểu cho mỗi arm của C/C test.
p_champion : bad rate kỳ vọng của Champion (baseline)
p_challenger: bad rate kỳ vọng của Challenger (alternative)
"""
z_alpha = norm.ppf(1 - alpha / 2) # two-sided
z_beta = norm.ppf(power)
p_bar = (p_champion + p_challenger) / 2
numerator = (z_alpha * math.sqrt(2 * p_bar * (1 - p_bar))
+ z_beta * math.sqrt(p_champion * (1 - p_champion)
+ p_challenger * (1 - p_challenger))) ** 2
denominator = (p_champion - p_challenger) ** 2
return math.ceil(numerator / denominator)
# Ví dụ: Champion bad rate 8%, Challenger kỳ vọng giảm xuống 6.5%
n_per_arm = required_sample_size(p_champion=0.08, p_challenger=0.065)
print(f"Mỗi arm cần: {n_per_arm} applications")
# → ~1,024 applications per arm; với 95/5 split: Challenger arm cần ~21,000 total/month
# Sau khi có outcome — kiểm tra statistical significance
champion_bad, champion_n = 192, 2400 # 8.0% bad rate
challenger_bad, challenger_n = 52, 800 # 6.5% bad rate
stat, pvalue = proportions_ztest(
count=[champion_bad, challenger_bad],
nobs=[champion_n, challenger_n],
alternative='larger' # H1: Champion bad rate > Challenger bad rate
)
print(f"z-statistic: {stat:.3f}")
print(f"p-value : {pvalue:.4f}")
print("Conclusion :", "Challenger significantly better ✅" if pvalue < 0.05 else "Not conclusive ⚠️")
Giá trị governance
C/C không chỉ là kỹ thuật — nó là ngôn ngữ của risk governance.
Risk committee không muốn nghe "model mới tốt hơn theo thuật toán." Họ muốn nghe "model mới đã chứng minh tốt hơn trên 2,400 đơn thực trong 4 tháng, với Gini cao hơn 3.2pt và DPD30 thấp hơn 0.8%, với statistical significance p < 0.05."
Câu sau có thể đưa vào meeting minutes, vào model validation report, vào regulatory submission. Câu trước thì không.
C/C tạo ra audit trail cho mọi model transition: ai approve, khi nào, trên evidence gì. Đó là thứ bảo vệ bạn 2 năm sau khi model đó có vấn đề và ai đó hỏi "tại sao anh deploy cái này?"
Pitfalls phổ biến
Define success criteria sau khi thấy kết quả. Đây là dạng p-hacking trong credit modeling. "Model mới có approval rate cao hơn 2%, vậy ta dùng approval rate làm primary metric." Không. Primary metric phải được định nghĩa trước khi split.
Test window quá ngắn. 30 ngày không phải C/C — đó là smoke test. Credit outcome cần thời gian để trưởng thành.
Không document assumptions. Sau 6 tháng, không ai nhớ tại sao split là 95/5 thay vì 90/10, tại sao dùng DPD30 thay vì DPD60. Document everything.
Celebrate uplift mà bỏ qua cost. Challenger có Gini cao hơn 2pt nhưng approval rate thấp hơn 3%. Tùy vào margin của ngân hàng, revenue loss từ 3% approval rate có thể outweigh benefit của 2pt Gini. Luôn tính full business case.
Takeaway
Champion-Challenger không phải best practice tùy chọn cho các team nào muốn "làm kỹ." Nó là yêu cầu governance cho bất kỳ ngân hàng nào có risk committee và model validation function.
Nếu bạn không có C/C, bạn không có bằng chứng — bạn chỉ có niềm tin. Và niềm tin không phải là thứ risk committee chấp nhận khi NPL tăng và ai đó hỏi "tại sao anh deploy model đó?"