Skip to content

Repository files navigation

Loan Default Risk Prediction

Temporal, leakage-safe loan charge-off risk modeling

CI Policy Python 3.11 FastAPI Streamlit scikit-learn Docker

Dự án ước lượng xác suất một khoản vay LendingClub đã được giải ngân bị Charged Off trong toàn bộ vòng đời hợp đồng. Pipeline dùng nhãn maturity-aware, chỉ sử dụng feature tại thời điểm nộp đơn, chia dữ liệu theo thời gian, Logistic Regression có calibration và đánh giá trên một tập Out-of-Time (OOT) hoàn toàn tách biệt.

Bài toán & phạm vi ứng dụng

Đầu ra chính là:

[ P(\text{Charged Off} \mid \text{Funded Loan}) \in [0, 1] ]

Phạm vi của mô hình là danh mục khoản vay lịch sử đã qua thẩm định và đã giải ngân. Mô hình không đại diện cho người nộp đơn chưa được duyệt, không tự động approve/reject, không định giá lãi suất, không tính Expected Loss và không thay thế quy trình tín dụng hoặc Adverse Action Notice.

Quy trình kỹ thuật duy nhất

Mermaid dưới đây là source of truth cho luồng code, cấu hình, artifact và báo cáo. Các node tương ứng trực tiếp với module trong src/.

flowchart TD
    RAW["data/raw/lendingclub_2007_2011.csv"] --> LOAD["src.data.load_data\nSchema + parse issue_d + term"]
    LOAD --> MATURITY["src.data.add_maturity_columns\ncontractual maturity <= dataset_as_of_date"]
    MATURITY --> TARGET["src.data.create_lifetime_target\nMATURE + Fully Paid/Charged Off"]
    TARGET --> SPLIT["src.data.temporal_split_three_blocks"]
    SPLIT --> TRAIN["Train\nissue_date < 2011-01-01"]
    SPLIT --> CAL["Calibration\n2011-01-01 <= issue_date < 2011-04-01"]
    SPLIT --> OOT["OOT Test\nissue_date >= 2011-04-01"]
    TRAIN --> TRAIN_FEATURES["build_features(train)\n16 application-time features"]
    CAL --> CAL_FEATURES["build_features(calibration)"]
    OOT --> OOT_FEATURES["build_features(oot)"]
    TRAIN_FEATURES --> CV["src.model.tune_logistic_c\nExpanding temporal CV, select C by PR-AUC"]
    TRAIN_FEATURES --> FIT["src.model.make_pipeline\nImpute + scale + one-hot + Logistic Regression"]
    CV --> FIT
    FIT --> CALIBRATE["CalibratedRiskModel.fit\nSigmoid calibration only if Brier improves"]
    CAL_FEATURES --> CALIBRATE
    OOT_FEATURES --> OOT_SCORE["Calibrated model\npredict_proba on OOT only"]
    CALIBRATE --> OOT_SCORE
    OOT_SCORE --> EVAL["src.evaluate + src.analysis\nOOT metrics, decile, drift, cohort, slices"]
    EVAL --> REPORTS["reports/*.csv + *.json"]
    CALIBRATE --> ARTIFACT["artifacts/risk_model.joblib\nModel + feature contract + policy bounds"]
    API_INPUT["API/UI application record"] --> SCORE["src.predict.predict\nProbability + local risk factors"]
    ARTIFACT --> SCORE
    SCORE --> POLICY["src.policy.get_risk_band\nLOW / MEDIUM / HIGH for display"]
    SCORE --> API["app.api\nFastAPI /predict"]
    SCORE --> UI["app.streamlit_app\nInteractive scoring"]
Loading

Luồng dữ liệu không được đảo thứ tự: dữ liệu phải qua maturity gate trước khi gán target; feature được tạo sau target nhưng chỉ từ thông tin application-time; chỉ Train được dùng để chọn C, Calibration chỉ dùng để fit/kiểm tra Sigmoid, và OOT chỉ dùng để đánh giá cuối cùng. Inference production đọc artifact đã được lưu, không chạy lại CV hoặc calibration.

Thiết kế dữ liệu và target

src.data.validate_schema yêu cầu các cột đầu vào: id, loan_status, issue_d, loan_amnt, term, emp_length, home_ownership, annual_inc, verification_status, purpose, dti, delinq_2yrs, inq_last_6mths, open_acc, pub_rec, revol_bal, revol_util, total_acc và earliest_cr_line. loan_status hợp lệ là Fully Paid, Charged Off hoặc Current; term chỉ nhận 36 hoặc 60 tháng.

Ngày maturity được tính bằng issue_date + term_months. Với dataset_as_of_date mặc định 2016-01-01, chỉ dòng thỏa cả hai điều kiện sau mới vào supervised set:

  • contractual_maturity_date <= dataset_as_of_date;
  • loan_status là trạng thái cuối: Fully Paid -> 0, Charged Off -> 1.

Khoản vay Current hoặc chưa đủ maturity bị loại khỏi target nhị phân để tránh right-censoring. Đây là maturity-based cohort filtering, không phải survival analysis liên tục.

Feature contract và chống leakage

src.features.build_features luôn trả đúng 16 cột theo đúng thứ tự sau:

loan_amnt, term_months, emp_length_years, home_ownership,
log_annual_income, verification_status, purpose, dti, delinq_2yrs,
inq_last_6mths, open_acc, pub_rec, revol_bal, revolving_utilization,
total_acc, credit_history_years

Các biến hậu quả như total_pymnt, recoveries, last_pymnt_d, out_prncp và loan_status nằm trong POST_OUTCOME_FEATURES. Các biến proxy policy/giá như grade, sub_grade, int_rate, installment, addr_state và issue_month nằm trong POLICY_PROXY_FEATURES. Hai nhóm này không được đưa vào FEATURE_COLUMNS; policy workflow kiểm tra bất biến này tự động.

Split, model và đánh giá

temporal_split_three_blocks dùng ba block theo issue_date:

Block Điều kiện Vai trò
Train < 2011-01-01 Fit và expanding-window CV
Calibration 2011-01-01 <= date < 2011-04-01 Fit Sigmoid/Platt độc lập
OOT Test >= 2011-04-01 Đánh giá cuối, không chọn model

tune_logistic_c so sánh C = (0.01, 0.1, 1.0, 10.0) bằng PR-AUC trên các fold temporal expanding hợp lệ. Pipeline mô hình gồm median imputation và standardization cho numeric, most-frequent imputation và one-hot encoding cho categorical, sau đó là Logistic Regression L2. Sigmoid calibrator chỉ được dùng nếu Brier score trên Calibration không xấu hơn raw score.

Artifact gắn policy hiển thị rủi ro: LOW nếu p < 0.15, MEDIUM nếu 0.15 <= p <= 0.25, và HIGH nếu p > 0.25. Đây chỉ là nhóm hiển thị/xếp hạng, không phải ngưỡng quyết định tín dụng.

Snapshot báo cáo hiện có trong reports/ cho OOT Test:

Metric Giá trị
PR-AUC 0.1892
ROC-AUC 0.6574
Brier score 0.0949
Capture@20% 36.00%
Lift@20% 1.80x
Default prevalence 10.98%

Đây là kết quả của artifact/report snapshot đang có, không phải cam kết hiệu năng cho dữ liệu hoặc môi trường kinh tế mới. Chi tiết phương pháp và giới hạn nằm trong MODEL_CARD.md; chính sách bảo mật nằm trong SECURITY.md.

Cấu trúc thư mục

loan-default-risk-prediction/
├── .github/workflows/
│   ├── ci.yml                    # Test, pip check, Docker build, API healthcheck
│   └── policy.yml                # Kiểm tra repo, dữ liệu và leakage policy
├── app/
│   ├── api.py                    # FastAPI: health, info, predict, coefficients
│   └── streamlit_app.py          # UI chấm điểm, xem metrics, decile và hệ số
├── artifacts/
│   ├── .gitkeep                  # Model joblib sinh cục bộ, không commit dữ liệu nhị phân
│   └── risk_model.joblib         # Sinh bởi lệnh train nếu có dữ liệu
├── data/
│   ├── raw/                      # CSV LendingClub đặt cục bộ, bị gitignore
│   └── external/                 # Dữ liệu ngoài tùy chọn, không thuộc pipeline chính
├── reports/                      # Metrics, CI, decile, drift, cohort và slice outputs
├── scripts/
│   ├── check_repo_policy.py      # Static repository policy check
│   └── prepare_data.py           # Kiểm tra schema và maturity trước khi train
├── src/
│   ├── analysis.py               # PSI, cohort performance
│   ├── data.py                   # Schema, maturity gate, target, temporal split
│   ├── evaluate.py               # Metrics, decile, bootstrap CI, slice metrics
│   ├── explain.py                # Local contributions và global coefficients
│   ├── features.py               # Feature contract 16 cột và leakage exclusion
│   ├── model.py                  # Preprocessing, Logistic Regression, calibration
│   ├── policy.py                 # Risk-band boundary dùng chung cho API/UI
│   ├── predict.py                # Load artifact và inference contract
│   └── train.py                  # Orchestrator train -> evaluate -> artifact/reports
├── tests/                        # Unit và integration tests
├── Dockerfile                    # Container FastAPI runtime
├── MODEL_CARD.md                # Model card
├── README.md                    # Tài liệu này
├── SECURITY.md                  # Security và privacy policy
├── pyproject.toml               # Package metadata và pytest config
├── requirements.txt             # Runtime pins
└── requirements-dev.txt         # Runtime pins + pytest/httpx

data/raw/*.csv, artifacts/*.joblib và cache local không được commit. Dataset LendingClub không phân phối cùng repository; người dùng phải tự tải snapshot được phép sử dụng và kiểm tra license trước khi chạy.

Cài đặt và chạy

1. Tạo môi trường

git clone https://github.com/haminhthong/Loan-Default-Risk-Prediction.git
cd Loan-Default-Risk-Prediction
python -m venv .venv
# Windows PowerShell
.\.venv\Scripts\Activate.ps1
# Linux/macOS
# source .venv/bin/activate
python -m pip install -r requirements-dev.txt

2. Chuẩn bị dữ liệu

Đặt snapshot được cấp phép tại data/raw/lendingclub_2007_2011.csv, sau đó kiểm tra schema và maturity:

python scripts/prepare_data.py

Có thể truyền đường dẫn và as-of date:

python scripts/prepare_data.py path/to/data.csv 2016-01-01

3. Train, tạo artifact và reports

python -m src.train

Các tham số CLI chính: --data, --as-of-date, --output và --report-dir. Lệnh train ghi artifacts/risk_model.joblib cùng các báo cáo model_cv.csv, test_metrics.json, bootstrap_ci.json, target_censoring.csv, decile_reliability.csv, drift_psi.csv, cohort_performance.csv, model_coefficients.csv và các slice_*.csv nếu phân khúc có tối thiểu 30 dòng hợp lệ.

4. Chạy test và policy check

python -m pytest -q
python scripts/check_repo_policy.py

5. Chạy app

Sau khi train, chạy Streamlit:

streamlit run app/streamlit_app.py

Mở http://localhost:8501. API FastAPI chạy bằng:

uvicorn app.api:app --host 0.0.0.0 --port 8000

API và Streamlit mặc định đọc artifacts/risk_model.joblib. Có thể đổi đường dẫn bằng biến môi trường LOAN_RISK_MODEL_PATH; Docker dùng cùng biến này nếu cần mount artifact ở vị trí khác.

Endpoints:

  • GET /health: liveness, trả thêm model_loaded.
  • GET /v1/info: metadata, feature contract, metrics, policy bounds và split rows.
  • GET /model/coefficients: hệ số toàn cục từ report.
  • POST /predict: batch tối đa 1.000 records.
  • POST /v1/risk/score: alias tương thích của /predict.

Nếu đặt LOAN_API_KEY, các endpoint ngoài /health yêu cầu header X-API-Key. Ví dụ request tối thiểu:

{
  "records": [
    {
      "loan_amnt": 10000,
      "term_months": 36,
      "home_ownership": "RENT",
      "annual_inc": 60000,
      "verification_status": "Verified",
      "purpose": "debt_consolidation",
      "revolving_utilization": 0.45,
      "credit_history_years": 8.0
    }
  ]
}

Schema LoanApplication bắt buộc loan_amnt, term_months, home_ownership, annual_inc, verification_status và purpose. Các trường còn lại là optional và được phép thiếu để pipeline impute. API chỉ nhận term_months là 36 hoặc 60; home_ownership là RENT, OWN, MORTGAGE hoặc OTHER; verification_status là Verified, Source Verified hoặc Not Verified. Numeric fields có validation không âm; loan_amnt và annual_inc phải lớn hơn 0; emp_length_years nằm trong [0, 10], dti trong [0, 100] và revolving_utilization trong [0, 1]. Field ngoài schema bị từ chối bởi extra="forbid".

Response chuẩn chứa lifetime_chargeoff_probability, risk_band, top_risk_factors và scored_at. Artifact phải tồn tại; nếu chưa train API vẫn có thể healthcheck nhưng /predict sẽ trả 503.

Docker và CI

Build image và chạy healthcheck:

docker build -t loan-default-risk:test .
docker run --rm -p 8000:8000 loan-default-risk:test

Vì dataset và joblib bị loại khỏi image, muốn chấm điểm bằng container cần mount artifact đã train:

docker run --rm -p 8000:8000 \
  -v "${PWD}/artifacts:/app/artifacts" \
  loan-default-risk:test

GitHub Actions chạy trên Python 3.11 và có hai workflow:

  • policy.yml: kiểm tra file bắt buộc, không commit raw CSV, đúng 16 feature và không có feature leakage/proxy.
  • ci.yml: chạy policy check, cài dependency pins, pip check, toàn bộ pytest, build Docker và smoke test GET /health trong container.

CI không train lại từ dataset riêng tư. Điều này giúp workflow tái lập và xanh trên checkout sạch mà không phân phối dữ liệu LendingClub.

Quy tắc đóng góp và an toàn

Không thêm feature hậu outcome, không đưa PII vào log, không commit secret, CSV raw hoặc joblib. Thay đổi target, feature contract, split boundary, policy hoặc output API phải cập nhật test, Model Card và README trong cùng change. Xem SECURITY.md trước khi báo cáo lỗ hổng.

Disclaimer

Đây là dự án nghiên cứu/kỹ thuật về credit-risk modeling trên dữ liệu lịch sử 2007-2011. Xác suất và factor giải thích chỉ phản ánh hành vi toán học của mô hình, không chứng minh quan hệ nhân quả và không thay thế quyết định nghiệp vụ của tổ chức tài chính.

About

Maturity-aware lifetime charge-off risk modeling with temporal validation, leakage-safe features, probability calibration, capacity-constrained manual review, FastAPI, and Streamlit.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages