Chuyển đến nội dung chính

Mô hình dự báo vỡ nợ với German Credit bằng Python

Mô hình dự báo vỡ nợ với bộ dữ liệu German Credit

Mô hình dự báo vỡ nợ sử dụng bộ German Credit là một bài toán phân loại rủi ro tín dụng: từ thông tin khoản vay và người đi vay, mô hình ước lượng khả năng hồ sơ thuộc nhóm rủi ro xấu. Bài toán phù hợp để học cách xử lý dữ liệu bảng, xây dựng mô hình có thể giải thích và lựa chọn ngưỡng quyết định khi hai loại sai lầm có chi phí khác nhau.

Tóm tắt nội dung
  • German Credit có 1.000 hồ sơ và 20 đặc trưng; nhãn gốc 1 là tốt, 2 là xấu [1].
  • Pipeline cơ bản: chia tập → mã hóa biến danh mục → chuẩn hóa biến số → Logistic Regression → xác suất rủi ro.
  • ROC-AUC đo khả năng xếp hạng; Brier score hỗ trợ kiểm tra xác suất; ma trận chi phí định hướng ngưỡng phân loại [1], [2].
  • Đánh giá phải tách rõ train, validation và test, tránh chọn ngưỡng bằng dữ liệu kiểm thử.

1. German Credit thực sự dự báo điều gì?

Bộ Statlog (German Credit Data) do Hans Hofmann cung cấp và được lưu trữ tại UCI Machine Learning Repository. Bộ dữ liệu có 1.000 quan sát, 20 thuộc tính đầu vào và hai nhãn good credit risk (1), bad credit risk (2) [1]. Theo mô tả liên quan tại UCI, có 700 hồ sơ tốt và 300 hồ sơ xấu [3]. Vì vậy, nếu quy ước lớp dương là hồ sơ xấu, tỷ lệ lớp dương là 30% trong bộ dữ liệu, không phải một ước lượng tỷ lệ vỡ nợ của thị trường.

Điểm cần phân biệt: bad credit risk không phải định nghĩa đo lường chính thức của biến cố vỡ nợ như quá hạn 90 ngày, mất khả năng trả nợ hay một thời hạn quan sát cụ thể. Tên “dự báo vỡ nợ” trong bài là cách gọi thông dụng để mô tả bài toán phân loại rủi ro; đầu ra thực tế của thực nghiệm là xác suất thuộc nhóm bad theo nhãn của UCI. Không nên gọi xác suất này là xác suất vỡ nợ 12 tháng (PD 12-month) khi chưa có căn cứ về định nghĩa nhãn và kỳ quan sát.

Các thuộc tính gồm thời hạn vay, số tiền vay, lịch sử tín dụng, tình trạng tài khoản, việc làm, độ tuổi, mục đích vay và những biến danh mục khác. UCI cung cấp bản gốc german.data với nhiều biến dạng mã ký hiệu, và bản german.data-numeric đã biến đổi để phục vụ thuật toán số [1]. Trong thực hành dưới đây, dùng bản do API UCI trả về và mã hóa các biến danh mục bằng one-hot encoding; không coi các mã A11, A12,... là con số có quan hệ tuyến tính.

NhómVí dụXử lý
Biến sốDuration, credit amount, ageStandardScaler
Biến danh mụcCredit history, purpose, housingOneHotEncoder
Nhãn mục tiêu1 = good, 2 = badĐổi thành 0 = good, 1 = bad

2. Pipeline từ hồ sơ tín dụng đến điểm rủi ro

Ta xây dựng mô hình nền (baseline) bằng Logistic Regression. Mô hình nhận các đặc trưng đã biến đổi và ước lượng một xác suất trong khoảng 0 đến 1 cho lớp bad. Đây là lựa chọn dễ tái lập và có các hệ số có thể phân tích; không hàm ý Logistic Regression luôn là thuật toán tốt nhất cho German Credit.

Pipeline dự báo rủi ro tín dụng German CreditGerman Credit20 đặc trưng1.000 hồ sơTiền xử lýOne-hotChuẩn hóaLogisticRegressionP(bad | x)So với ngưỡng t→ Quyết địnhChia train / validation / test trước khi fit tiền xử lý
Hình 1. Pipeline minh họa do tác giả tự xây dựng; không phải hình từ một paper.

Với vector đầu vào đã mã hóa x, Logistic Regression áp dụng hàm sigmoid lên tổng tuyến tính. Công thức sau biểu diễn mô hình nhị phân, không phải công thức đặc thù do bộ German Credit đề xuất:

P(y = 1 | x) = 1 / (1 + exp(−(β₀ + β₁x₁ + … + βₚxₚ)))

Trong đó y = 1 là nhãn bad sau khi đổi mã, xⱼ là đặc trưng sau tiền xử lý, còn βⱼ là hệ số được học. Hệ số dương thường làm tăng log-odds của lớp bad khi giữ nguyên các biến khác, nhưng cách giải thích còn phụ thuộc chuẩn hóa, mã hóa danh mục và tương quan giữa các biến; một hệ số không chứng minh quan hệ nhân quả.

Việc đặt cả OneHotEncoder và StandardScaler trong cùng ColumnTransformer của Pipeline giúp các phép biến đổi được học trên đúng tập huấn luyện [2]. Nếu chuẩn hóa toàn bộ dữ liệu trước khi chia tập, thông tin thống kê từ test có thể rò rỉ vào quá trình học. Tham khảo khái niệm phân loại máy học trong mục Classification trên The CS Note.

3. Chạy Logistic Regression bằng Python

Mã sử dụng ucimlrepo để tải bộ dữ liệu UCI và scikit-learn để huấn luyện [1], [2]. Cài đặt một lần bằng lệnh pip install ucimlrepo pandas numpy scikit-learn. Kết quả phụ thuộc phiên bản thư viện và dữ liệu tải về; không có số ROC-AUC hay độ chính xác nào được gán sẵn cho thí nghiệm.

Python · Logistic Regression
from ucimlrepo import fetch_ucirepo
import numpy as np
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, average_precision_score, brier_score_loss
from sklearn.metrics import confusion_matrix, balanced_accuracy_score

# UCI: nhãn gốc 1=good, 2=bad
credit = fetch_ucirepo(id=144)
X = credit.data.features.copy()
y = (credit.data.targets.iloc[:, 0] == 2).astype(int)

# Biến mã Axx được xem là danh mục, không phải số thứ tự
categorical = make_column_selector(dtype_include=object)
numerical = make_column_selector(dtype_include=np.number)
preprocess = ColumnTransformer([
    ("cat", OneHotEncoder(handle_unknown="ignore"), categorical),
    ("num", StandardScaler(), numerical),
])
model = Pipeline([
    ("prepare", preprocess),
    ("classifier", LogisticRegression(max_iter=2000, random_state=42)),
])
X_dev, X_test, y_dev, y_test = train_test_split(
    X, y, test_size=0.20, stratify=y, random_state=42
)
X_train, X_val, y_train, y_val = train_test_split(
    X_dev, y_dev, test_size=0.25, stratify=y_dev, random_state=42
)
model.fit(X_train, y_train)
p_val = model.predict_proba(X_val)[:, 1]
p_bad = model.predict_proba(X_test)[:, 1]

# Chi phí minh họa theo ma trận của UCI: FN=5, FP=1
thresholds = np.linspace(0, 1, 1001)
costs = []
for threshold in thresholds:
    predicted_bad = (p_val >= threshold).astype(int)
    tn, fp, fn, tp = confusion_matrix(
        y_val, predicted_bad, labels=[0, 1]
    ).ravel()
    costs.append(fp + 5 * fn)

best_idx = int(np.argmin(costs))
threshold = float(thresholds[best_idx])
predicted_bad = (p_bad >= threshold).astype(int)
print("ROC-AUC:", round(roc_auc_score(y_test, p_bad), 3))
print("PR-AUC (AP):", round(average_precision_score(y_test, p_bad), 3))
print("Brier:", round(brier_score_loss(y_test, p_bad), 3))
print("Balanced accuracy:", round(balanced_accuracy_score(y_test, predicted_bad), 3))
print("Ngưỡng chọn từ validation:", threshold)
print("Ma trận nhầm lẫn [good, bad]:")
print(confusion_matrix(y_test, predicted_bad, labels=[0, 1]))

Chương trình dùng tỷ lệ train/validation/test là 60%/20%/20% với chia lớp phân tầng. Điểm dự đoán của validation chỉ dùng để chọn ngưỡng có chi phí nhỏ nhất; các chỉ số báo cáo cuối cùng được tính trên test. Cách chọn một ngưỡng bằng validation ở đây mang tính hướng dẫn, vẫn có thể chưa ổn định do dữ liệu ít; khi làm nghiên cứu nghiêm túc nên dùng kiểm định chéo lồng nhau hoặc nhiều lần chia dữ liệu.

Trong kết quả, ROC-AUC thể hiện khả năng xếp hạng hồ sơ bad cao hơn hồ sơ good, average_precision_score là diện tích trung bình chính xác dựa trên đường precision–recall theo định nghĩa của scikit-learn, còn Brier score là sai số bình phương trung bình giữa xác suất dự báo và nhãn 0/1 [2]. ROC-AUC cao không có nghĩa xác suất 0,7 chắc chắn tương ứng 70% tỷ lệ bad quan sát được; cần kiểm tra thêm hiệu chuẩn xác suất (calibration).

4. Vì sao ngưỡng 0,5 không nhất thiết phù hợp?

Bộ German Credit kèm ma trận chi phí, trong đó dự báo good cho hồ sơ thực tế bad có chi phí 5 đơn vị, còn dự báo bad cho hồ sơ thực tế good có chi phí 1 đơn vị [1]. Ta đổi nhãn để bad = 1. Khi đó một hồ sơ bad bị dự đoán good là false negative (FN), còn hồ sơ good bị dự đoán bad là false positive (FP).

Thực tế / Dự đoánGood (0)Bad (1)
Good (0)01 (FP)
Bad (1)5 (FN)0
Tổng chi phí = 1 × FP + 5 × FN

Ví dụ giả định hai ngưỡng cho cùng một validation set tạo ra: phương án A có FP = 20, FN = 10 nên chi phí 70; phương án B có FP = 35, FN = 5 nên chi phí 60. Phương án B có nhiều cảnh báo nhầm hơn nhưng tổng chi phí nhỏ hơn theo ma trận UCI. Đây là ví dụ tính toán, không phải kết quả chạy German Credit.

Nếu xác suất bad được hiệu chuẩn chính xác và chi phí sai lầm đúng như ma trận nêu trên, quy tắc tối ưu theo kỳ vọng chi phí đối với từng hồ sơ là gắn nhãn bad khi p(bad | x) > 1 / (5 + 1) ≈ 0,167. Con số này là hệ quả toán học của giả định chi phí cố định và xác suất chuẩn xác, không phải ngưỡng mặc định được UCI khuyến nghị và cũng không thay thế việc kiểm tra trên tập validation. Trong hoạt động cấp tín dụng, chi phí thực thường còn phụ thuộc dư nợ, tỷ lệ tổn thất, lợi nhuận và chính sách phê duyệt.

5. Đánh giá đúng và những giới hạn của benchmark

Accuracy dễ gây hiểu nhầm: với phân bố 700 good và 300 bad, bộ phân loại luôn chọn good sẽ đạt accuracy 70% trên toàn bộ tập dữ liệu, nhưng không phát hiện hồ sơ bad nào. Nên đọc đồng thời confusion matrix, recall cho bad, precision, balanced accuracy, ROC-AUC và chỉ số xác suất. Khi điều chỉnh ngưỡng, precision, recall và tổng chi phí thường biến đổi theo những hướng khác nhau; không thể tối ưu tất cả chỉ số bằng cùng một ngưỡng.

Phân biệt xếp hạng và hiệu chuẩn: ROC-AUC không đánh giá trực tiếp mức độ gần đúng của xác suất; Brier score và biểu đồ calibration giúp khảo sát khía cạnh khác [2]. Với quy mô 1.000 quan sát, ước lượng hiệu chuẩn cũng có bất định đáng kể. Việc huấn luyện trên cùng bộ dữ liệu, thử nhiều mô hình rồi chỉ công bố mô hình cho điểm cao nhất trên test sẽ dẫn đến đánh giá lạc quan do lựa chọn mô hình trên tập kiểm thử.

Giới hạn về dữ liệu và đạo đức: UCI mô tả bộ South German Credit như một bản chỉnh sửa những vấn đề mã hóa và cung cấp thêm bối cảnh cho Statlog German Credit [3]. Vì vậy các phân tích phụ thuộc vào định nghĩa thuộc tính cũ cần được rà soát, không nên tự diễn giải mọi mã thành đặc trưng kinh tế chính xác. German Credit còn chứa các biến liên quan đến tuổi và tình trạng cá nhân; khi xây dựng hệ thống thật cần kiểm tra tính phù hợp pháp lý, sai lệch giữa các nhóm, dữ liệu đại diện theo thời gian và tính công bằng, thay vì lấy benchmark lịch sử làm quy tắc ra quyết định thực tế.

Đối chiếu mô hình: có thể thử Decision Tree, Random Forest, Gradient Boosting hoặc SVM trên cùng sơ đồ chia tập và cùng thước đo, nhưng chỉ có kết quả đánh giá hợp lệ mới cho phép kết luận mô hình nào tốt hơn trong một thiết lập cụ thể. Nếu muốn xem trực giác về biên phân loại, bài Mô phỏng SVM: Margin và Support Vector trên The CS Note là tài liệu bổ trợ phù hợp; nội dung đó không được dùng làm bằng chứng benchmark cho German Credit.

6. Từ bài tập đến mô hình rủi ro có thể sử dụng

Logistic Regression trên German Credit cung cấp khởi đầu rõ ràng: xây dựng tập đặc trưng, ước lượng điểm rủi ro, đánh giá khả năng phân biệt rồi điều chỉnh ngưỡng theo chi phí. Để áp dụng thực tế cần thêm định nghĩa biến cố vỡ nợ, khung thời gian dự báo, dữ liệu mới đại diện, tách tập theo thời gian, kiểm tra hiệu chuẩn, chính sách xử lý dữ liệu nhạy cảm và giám sát sai lệch sau triển khai. Không nên triển khai một mô hình quyết định cho vay chỉ dựa vào độ chính xác trên bộ dữ liệu giáo dục này.

Tóm lại, điều quan trọng nhất không phải một con số accuracy duy nhất mà là đầu ra xác suất có ý nghĩa gì, cách kiểm định nó và chi phí của mỗi quyết định. German Credit là môi trường nhỏ, dễ tiếp cận để thực hành trọn quy trình ấy, đồng thời cho thấy khoảng cách giữa một bài toán phân loại học máy và một hệ thống quản trị rủi ro tín dụng.

References

  1. H. Hofmann, “Statlog (German Credit Data),” UCI Machine Learning Repository, 1994. DOI: 10.24432/C5NC77. Dataset: UCI Statlog German Credit.
  2. scikit-learn developers, “ColumnTransformer,” “roc_auc_score,” and “brier_score_loss,” scikit-learn documentation.
  3. UCI Machine Learning Repository, “South German Credit,” dataset description and background notes.
© The CS Note · THE CS NOTE

Nội dung gốc thuộc tác giả. Vui lòng không đăng lại toàn văn khi chưa được phép, trừ trường hợp pháp luật hoặc giấy phép áp dụng cho phép. Nội dung của bên thứ ba tuân theo quyền và giấy phép tương ứng.

Nguồn gốc: Mô hình dự báo vỡ nợ với German Credit bằng Python

Về trang chủ Về đầu bài

Bài Đăng Phổ Biến

Bài toán tháp Hà nội

Trải nghiệm Tháp Hà Nội Kéo thả đĩa hoặc chạm vào các nút bên dưới để di chuyển. Bạn phải chuyển toàn bộ đĩa sang cột ngoài cùng bên phải (Cột 3) mà không đặt đĩa to lên đĩa nhỏ. Cột 1 Cột 2 Cột 3 Điều khiển & Chế độ Số lượng đĩa 4 Tốc độ máy giải ↪ Undo ↻ Chơi lại 🤖 Máy tự giải Thống kê ván chơi Nước đã đi 0 Thời gian 00:00 Tối thiểu cần 15 Kỷ lục của bạn - Lịch sử và Nguồn gốc Truyền thuyết Bài toán Tháp Hà Nội (hay Tháp Brahma) là một trò chơi xếp hình toán học kinh điển được giới thiệu lần đầu vào năm 1883 bởi nhà toán học người Pháp Édouard L...

Bài toán phân tích cảm xúc trong văn bản

Bài toán phân tích cảm xúc (sentiment analysis) là một bài toán NLP nhằm xác định thái độ hoặc cảm xúc được thể hiện trong văn bản. Ví dụ, từ một đánh giá như “Điện thoại đẹp, chạy nhanh nhưng pin hơi yếu”, hệ thống có thể nhận biết rằng người dùng có cả nhận xét tích cực và tiêu cực. Điểm thú vị của bài toán nằm ở khoảng cách giữa cách con người hiểu ngôn ngữ và cách máy tính xử lý dữ liệu. Một hệ thống phân tích cảm xúc cần biến văn bản tự nhiên thành dữ liệu có cấu trúc, học được các mẫu ngôn ngữ từ dữ liệu huấn luyện và đưa ra dự đoán hợp lý trên những câu chưa từng gặp. 1. Bài toán phân tích cảm xúc bắt đầu từ đâu? Hãy tưởng tượng một cửa hàng trực tuyến có hàng nghìn đánh giá mỗi ngày. Con người có thể đọc từng đánh giá để biết khách hàng hài lòng hay không, nhưng việc này nhanh chóng trở nên tốn thời gian khi dữ liệu tăng lên. NLP có thể biến bài toán đó thành một bài toán phân loại. Với mỗi văn bản, hệ thống nhận vào một chuỗi ký...

KAN cho phân đoạn khối u não với bộ BraTS 2020

Kolmogorov-Arnold Network (KAN) mở ra một hướng khác với MLP truyền thống: thay vì chỉ học các trọng số vô hướng trên mỗi kết nối, KAN có thể học những hàm phi tuyến một biến trên các cạnh . Khi đưa ý tưởng này vào kiến trúc encoder-decoder như U-Net, ta có thể sử dụng convolution để giữ cấu trúc không gian của ảnh MRI và dùng KAN để thực hiện các phép biến đổi phi tuyến mạnh hơn trên representation trung gian. Một bài toán phù hợp để nghiên cứu hướng này là phân đoạn u thần kinh đệm trên BraTS 2020 . Dataset cung cấp MRI đa mô thức và yêu cầu xác định các vùng Whole Tumor (WT), Tumor Core (TC) và Enhancing Tumor (ET). Đây là bài toán 3D có sự mất cân bằng voxel lớn, hình dạng khối u rất đa dạng và ranh giới giữa các mô bệnh lý không đơn giản. Lưu ý học thuật quan trọng: U-KAN gốc được công bố cho nhiều benchmark phân đoạn ảnh y tế nhưng không báo cáo kết quả BraTS 2020. Một công trình sau đó đã mở rộng U-KAN sang 3D MRI brain tumor segmentation , nhưng đánh giá trên BraTS 2...

Tách từ trong tiếng Việt Word Segmentation

Tách từ tiếng Việt là bài toán xác định ranh giới giữa các từ trong một chuỗi văn bản. Khó khăn nằm ở chỗ khoảng trắng trong tiếng Việt thường phân cách các âm tiết , nhưng một từ hoàn chỉnh có thể gồm nhiều âm tiết như “sinh viên” , “đại học” , “công nghệ” hay “tự nhiên” . Tách từ đúng giúp các hệ thống NLP dựa trên từ biểu diễn văn bản hợp lý hơn, đặc biệt trong tìm kiếm, TF-IDF, phân loại văn bản, gán nhãn từ loại và một số mô hình ngôn ngữ tiếng Việt. Tuy nhiên, với các Transformer sử dụng tokenizer dưới mức từ như subword hoặc SentencePiece, tách từ thủ công không phải lúc nào cũng là bước bắt buộc . Việc có cần tách từ trước hay không phụ thuộc vào mô hình và pipeline cụ thể. Tóm tắt nội dung Khoảng trắng trong tiếng Việt không phải lúc nào cũng là ranh giới từ ; nhiều từ gồm hai hoặc nhiều âm tiết. Word segmentation và tokenization có liên quan nhưng không hoàn toàn đồng nghĩa. Ba hướng chính gồm dựa trên từ điển, mô hình thống kê và mô hình học sâu . Tron...

KAN là gì? Mạng Kolmogorov-Arnold khác MLP thế nào?

Kolmogorov-Arnold Network (KAN) là một kiến trúc mạng nơ-ron trong đó mỗi cạnh không chỉ chứa một trọng số vô hướng mà mang một hàm một biến có thể học được . Đây là khác biệt cốt lõi so với Multi-Layer Perceptron (MLP), nơi các cạnh chủ yếu thực hiện phép nhân với trọng số còn hàm kích hoạt thường được áp dụng tại các node. KAN được lấy cảm hứng từ định lý biểu diễn Kolmogorov-Arnold và đặc biệt đáng chú ý trong các bài toán xấp xỉ hàm, mô hình hóa khoa học và symbolic regression. Tuy nhiên, KAN không nên được hiểu là kiến trúc đã thay thế MLP trong mọi bài toán Deep Learning; khả năng biểu diễn, chi phí tính toán và mức độ phù hợp của hai kiến trúc khác nhau theo từng nhiệm vụ. Tóm tắt nội dung MLP học chủ yếu trọng số trên các cạnh , còn KAN đặt hàm một biến học được trên từng cạnh . Một KAN layer cộng kết quả của nhiều hàm cạnh để tạo activation của node tiếp theo. KAN gốc sử dụng B-spline để tham số hóa các hàm phi tuyến trên cạnh. KAN có lợi thế đáng chú ý về trực...