bureau-to-workout: credit-scoring and debt-workout models trained on simulated data

Trained on synthetic data only. These models were fitted to the paper's simulators, not to NCB or SAM data. They reproduce the paper's experiments and serve as baselines for the dataset. They are not credit models to score real people. They have not been validated on real data, and the simulators' group effects are arbitrary design choices.

Models for the preprint From Bureau to Workout: Credit Scoring and Causal Debt Resolution under Policy-Shaped Labels by Thanatchon Chongmankhong (read online). Code: https://github.com/thanatchon36/bureau-to-workout; archived on Zenodo: 10.5281/zenodo.24102251.

Independent personal research. Does not represent the views of any employer or institution.

Files

All models come from the paper's first seed (bureau seeds 7 and 1007, workouts seed 107), fitted by the same code as make experiments. scikit-learn models are saved with skops (no pickle); the scorecard is plain JSON.

path model paper
bureau/woe_scorecard.json, bureau/woe_points_table.csv weight-of-evidence scorecard: monotone bins, WoE, logistic fit, points (600 at odds 50:1, 20 points to double) E1, E5
bureau/monotone_gbm.skops gradient-boosted classifier with monotone constraints on the 15 bureau attributes that vary without a moratorium E1
bureau/ssl_encoder.skops masked self-supervised encoder of 24-month bureau sequences (scaler + MLP; the embedding is the bottleneck layer) E1
bureau/ssl_linear_probe.skops, bureau/gbm_ssl.skops logistic probe on the embedding alone; GBM on attributes + embedding E1
bureau/e2_{approved_only,approved_only_ipw,fuzzy,bureau_augmented,bureau_augmented_ipw,oracle}.skops monotone GBMs for reject inference at lender 0: approved applicants only, with IPW, classic fuzzy augmentation, bureau-augmented labels (rejects whose outcome the bureau sees), with IPW for unobserved rejects, and the oracle on every applicant's true outcome E2
bureau/e3_{naive,exclude,delayed,oracle}.skops logistic models trained through a moratorium on reported labels, without deferred windows, on delayed labels, and on the no-deferral truth E3
bureau/features.json feature lists, monotone constraints, embedding columns
workout/outcome_models.skops expected recovery under each of the four offers (list of 4 regressors; T-learner) R1
workout/propensity.skops multinomial propensity model of the logged offers (scaler + logistic) R1, R4
workout/dr_learner.skops cross-fitted DR-learner: effect of offers 1–3 against standard collection (dict of 3 regressors) R1–R3
workout/predictive_score.skops P(recovery β‰₯ 60% under standard collection), the predictive-targeting baseline R2
metrics.json, versions.json the metrics below in full; library versions

Usage

import json
import pandas as pd
import skops.io as sio
from huggingface_hub import snapshot_download
from bureau_to_workout.bureau import WoEScorecard   # pip install git+https://github.com/thanatchon36/bureau-to-workout

path = snapshot_download("thanatchon36/bureau-to-workout")
oot = pd.read_parquet(
    snapshot_download("thanatchon36/bureau-to-workout", repo_type="dataset") + "/scoring/e1_oot.parquet")
feats = json.load(open(f"{path}/bureau/features.json"))["e1_features"]

def load(file):
    untrusted = sio.get_untrusted_types(file=file)   # inspect this list before trusting it
    return sio.load(file, trusted=untrusted)

gbm = load(f"{path}/bureau/monotone_gbm.skops")
pd_gbm = gbm.predict_proba(oot[feats])[:, 1]

e2 = pd.read_parquet(
    snapshot_download("thanatchon36/bureau-to-workout", repo_type="dataset") + "/scoring/e2_test.parquet")
ba = load(f"{path}/bureau/e2_bureau_augmented.skops")
pd_ba = ba.predict_proba(e2[json.load(open(f"{path}/bureau/features.json"))["e2_features"]])[:, 1]

scorecard = WoEScorecard.from_dict(json.load(open(f"{path}/bureau/woe_scorecard.json")))
points = scorecard.score(oot[feats])
reasons = scorecard.reason_codes(oot[feats].head(3))

dr = load(f"{path}/workout/dr_learner.skops")        # {"1": ..., "2": ..., "3": ...}

Metrics of these exact models

Out-of-time test (E1): months 36–45, after the shock. The paper column is the paper's seed-7 result. These uploads were refitted with newer library versions (python 3.12.15, numpy 2.5.4, pandas 3.1.1, scikit-learn 1.9.1, skops 0.16.0, bureau-to-workout 0.1.0), which moves some numbers in the third decimal.

model OOT AUC paper, seed 7 PSI dev β†’ OOT
WoE scorecard 0.806 0.806 0.12
Monotone GBM 0.813 0.815 0.12
SSL embedding only (linear probe) 0.773 0.772 0.59
GBM + SSL embedding 0.819 0.818 0.37

Reject inference (E2), test half of lender 0's applicants. Predicted Γ· observed default among rejected applicants shows how far each model under-predicts the risk it never saw (ideal 1):

training labels AUC, all applicants predicted Γ· observed, rejected paper, seed 7
Approved-only (AO) 0.690 0.58 0.58
AO + IPW on observables 0.683 0.56 0.55
Fuzzy augmentation 0.693 0.58 0.58
Bureau-augmented (BA) 0.706 0.92 0.92
BA + IPW for unobserved rejects 0.704 0.91 0.91
Oracle (all applicants, true Y) 0.710 0.96 0.96

After a moratorium (E3), test months 39–45:

labels used in training OOT AUC relative calibration of deferred borrowers (ideal 1)
Naive: reported labels 0.638 0.17
Exclude deferred windows 0.827 0.74
Delayed labels (+6m after exit) 0.824 0.70
Oracle: no-deferral counterfactual 0.829 0.93

Workout policies at the status-quo write-down budget (R2), true values on the held-out half:

policy recovery, % of balance write-downs, % of balance
Status quo (logged offers) 55.53 10.6
All standard 56.59 0.0
All term extension 55.39 0.0
Predictive targeting 56.42 10.6
Causal targeting (T-learner) 57.17 7.2
Causal targeting (DR-learner) 57.25 2.8
Oracle 57.99 3.0

Limitations

The simulators decide what these models can learn: in the bureau, repayment at other lenders is a perfect proxy for the new loan; in the workouts, unconfoundedness and overlap hold by construction. The SSL encoder is a small CPU stand-in for sequence models trained at bureau scale. See Section 8 of the paper.

Citation

@misc{chongmankhong2026bureau,
  author = {Chongmankhong, Thanatchon},
  title  = {From Bureau to Workout: Credit Scoring and Causal Debt Resolution under Policy-Shaped Labels},
  year   = {2026},
  doi    = {10.5281/zenodo.24102251},
  url    = {https://thanatchon36.github.io/bureau-to-workout/paper/},
  note   = {Preprint; models trained on synthetic data}
}
Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Dataset used to train thanatchon36/bureau-to-workout