Credit scoring
A credit model estimates the probability that a borrower will fail to repay, and it is one of the most heavily regulated uses of machine learning that exists.
- 10 min read
- 3 reading levels
- Published
Read these first
On this page 5
One lesson, three depths. Pick the one that fits you today — you can switch any time.
Beginner — No maths. Plain English.
A credit score estimates how likely a borrower is to fail to repay a loan, as a single number.
Think about a neighbourhood kirana shop. It lets regular customers buy groceries on credit (udhaar), and settle the bill at month end. The shopkeeper does not do this for a stranger walking in for the first time. He remembers who has paid back on time for years. He remembers who has been late twice, and who never returned. That memory is a credit score, kept in a notebook instead of a computer.
A bank does the same thing at a far larger scale, using a model instead of memory.
Why it exists
Lending money is a bet. Lend to someone who repays, and the bank earns interest. Lend to someone who does not, and the bank loses the principal. Before computers, loan officers judged this by interview and gut feeling. That was fast to do, but inconsistent. It was open to unconscious bias about who "looks" trustworthy.
A credit scoring model replaces gut feeling with a number. That number comes from measurable facts: income, existing debt, repayment history, how long accounts have been open. It outputs a probability, typically between 0 (certain to repay) and 1 (certain to default). The bank then decides: approve, deny, or approve at a higher interest rate to cover the added risk.
This is a regulated activity, not a free-standing engineering choice. In most countries, laws require lenders to explain why an application was denied. Lenders must avoid using race, gender or other protected characteristics — even indirectly. The model's fairness and accuracy must be independently reviewed before it touches real applicants. A credit model built as a class project must never be treated as ready for real lending decisions. That gap is not a technicality. It is the law, and it exists because a wrong decision here can cost someone a home or a business.
How it works
Applicant's data
income, existing debt, repayment history, employment length
|
v
[ credit model ]
|
v
probability of default, e.g. 0.07 (7 out of 100 similar
applicants would fail to repay)
|
v
Bank policy applies a THRESHOLD
below 0.10 -> approve
0.10-0.25 -> approve at a higher interest rate
above 0.25 -> declineThe model does not make the final decision by itself. It produces a number. A human-set business policy turns that number into approve, price-up, or decline.
A real example you have seen
In India, when you apply for a credit card or a personal loan, the bank checks your CIBIL score. It is a number from a credit bureau, summarising your repayment history across all your existing loans and cards. Banks combine that bureau score with their own model, built on their own customers, before deciding.
Remember this
- A credit model outputs a probability of default, between 0 and 1 — not a yes/no decision by itself.
- Credit scoring is one of the most tightly regulated uses of AI. A toy model is a learning exercise, never something to deploy on real borrowers without regulatory approval and independent review.
- Business policy, not the model, turns the probability into an approve/decline/reprice decision.
What to learn next
- Reject inference — the strange bias problem created by only ever seeing repayment data for people who were approved.
- Explaining a credit decision to a regulator — what happens after the model gives its number.
- Logistic regression — the model family this lesson's code is built on.
Developer — Code and libraries.
Setup
pip install scikit-learn numpyMinimal runnable code
A small, entirely synthetic dataset — no real applicant data, ever, for a toy example like this.
import numpy as np
from sklearn.linear_model import LogisticRegression
# Each row: [monthly_income_thousands, existing_debt_thousands, missed_payments_last_year]
X = np.array([
[80, 5, 0], [65, 10, 1], [120, 0, 0], [30, 20, 3], [45, 15, 2],
[90, 8, 0], [25, 25, 4], [110, 2, 0], [55, 12, 1], [35, 18, 3],
[70, 6, 0], [40, 22, 3], [100, 4, 0], [50, 14, 2], [60, 9, 1],
[28, 24, 4], [95, 3, 0], [38, 19, 2], [75, 7, 0], [48, 16, 2],
])
# 1 = defaulted on a loan within 12 months, 0 = repaid on time (synthetic labels)
y = np.array([0,1,0,1,1, 0,1,0,0,1, 0,1,0,1,0, 1,0,1,0,1])
model = LogisticRegression()
model.fit(X, y)
new_applicants = np.array([
[85, 6, 0], # steady income, low debt, clean history
[32, 21, 3], # low income, high debt, missed payments
])
probs = model.predict_proba(new_applicants)[:, 1]
for app, p in zip(new_applicants, probs):
print(f"income={app[0]}k debt={app[1]}k missed={app[2]} -> default probability={p:.2f}")
print()
print("coefficients (income, debt, missed_payments):", np.round(model.coef_[0], 3))income=85k debt=6k missed=0 -> default probability=0.02 income=32k debt=21k missed=3 -> default probability=1.00 coefficients (income, debt, missed_payments): [-0.004 0.615 0.396]
What actually happened
predict_proba returns two columns — probability of class 0 and class 1. Column [:, 1] is the probability of default, the number a lender actually cares about.
The two synthetic applicants sit near opposite ends of the training data, so the model separates them cleanly — 0.02 versus effectively 1.00. Real applicant populations overlap far more, and real probabilities cluster much closer to the decision threshold, which is exactly where credit decisions get hard and where regulation matters most.
Read the coefficients carefully: missed_payments has a bigger effect (0.396) than the raw coefficient on debt might suggest relative to its scale, and income's coefficient is small in this tiny dataset because income, debt and missed payments move together here — a common mistake covered below.
- Logistic regression is a standard first choice for credit scoring, not because it is the most accurate model available, but because its coefficients are directly readable, which regulation in most countries effectively requires.
LogisticRegression()with default settings applies L2 regularisation automatically — a detail worth knowing before comparing coefficients across a real project.
Common mistakes
Reading raw coefficients as "feature importance" when features are correlated. In this toy data, low income, high debt and missed payments all move together. The model can shift weight between correlated features almost arbitrarily. See multicollinearity and VIF before trusting a coefficient's size.
Using predict() instead of predict_proba(). predict() applies a hidden 0.5 threshold, hiding the actual number a lending policy needs to set its own threshold on.
Training on approved customers only. A bank's historical data only has repayment outcomes for people who were approved for a loan in the past — nobody knows how a declined applicant would have behaved. This blind spot is serious enough to need its own lesson, next.
Treating 20 synthetic rows as evidence of anything. This dataset exists to show the mechanics of .fit() and .predict_proba(). A real credit model is trained, validated and audited on thousands of real, carefully governed records, under regulatory oversight.
Try it yourself
Add an applicant with income=60, debt=10, missed=2 — a genuinely ambiguous case, between the two examples shown. Print its probability, and notice it lands much closer to the 0.10–0.25 "reprice" band in the beginner section's example policy than either training example did.
What to learn next
- Reject inference — the missing-data problem hiding in every credit dataset.
- Multicollinearity and VIF — why coefficients can mislead when features are correlated.
- Explaining a credit decision to a regulator — turning a probability into a defensible written reason.
Researcher — Mathematics and papers.
The modelling target
Credit scoring is typically framed as binary classification with target y in {0, 1} — default within a fixed horizon, commonly 12 or 24 months — and the model estimates:
p_hat(x) = P( default | features x )Logistic regression remains an industry default because its log-odds form is additive and directly interpretable per feature, which matters for the regulatory requirement (discussed below) to produce specific, individualised reasons for an adverse decision:
log( p / (1 - p) ) = b_0 + b_1 x_1 + ... + b_k x_kp— predicted probability of defaultb_0— intercept (baseline log-odds)b_i— the change in log-odds per unit change in featurex_i, holding other features fixed
Tree ensembles (gradient-boosted trees, random forests) generally achieve higher discrimination on the same data, measured by AUC or the Kolmogorov-Smirnov statistic, at the cost of coefficients that no longer map cleanly to individual reasons — pushing the interpretability burden onto a post-hoc explainer, covered in the next lesson.
Discrimination and calibration are separate questions
A credit model needs both properties, and they can fail independently:
- Discrimination — does the model rank good and bad borrowers correctly, regardless of the absolute numbers? Measured with AUC-ROC or the Gini coefficient (
Gini = 2*AUC - 1in this context). - Calibration — when the model says "7% probability of default," do roughly 7 out of 100 such borrowers actually default? Measured with reliability diagrams and the Brier score.
A model can rank borrowers perfectly while being badly miscalibrated (systematically over- or under-stating risk), which directly changes how much capital a regulated lender must hold against a loan book. See reliability diagrams and calibration error.
Regulatory structure (jurisdiction-dependent, illustrative)
Requirements vary by country and change over time — always check current local regulation before building a production system — but common threads across major jurisdictions include:
- A right to a specific, individualised reason when an application is denied (in the US, this traces to the Equal Credit Opportunity Act and Regulation B's adverse-action-notice requirement).
- A prohibition on using protected characteristics directly, and increasingly on proxies that reproduce their effect (disparate impact), which is why fairness testing on a credit model is a compliance requirement, not an optional extra.
- Independent model validation, separate from the team that built the model, before deployment — a standard formalised for large US banks under Federal Reserve SR 11-7 and mirrored in various forms by regulators elsewhere.
Cost
Interpretable models (logistic regression, shallow trees, scorecards built by binning features into points, called WOE/scorecard modelling) dominate production credit scoring not for accuracy but for auditability — every score decomposes into named, reviewable contributions. This is a case where the regulatory environment actively selects for simpler models over marginally more accurate complex ones.
Key references
- Federal Reserve & OCC. SR 11-7: Supervisory Guidance on Model Risk Management (2011) — the reference framework for independent model validation, discussed further in model risk management.
- Thomas, L. C., Edelman, D. B., & Crook, J. N. (2002). Credit Scoring and Its Applications. SIAM — the standard textbook treatment of scorecard modelling.
- Siddiqi, N. (2017). Intelligent Credit Scoring (2nd ed.). Wiley — practitioner-oriented treatment of WOE binning and scorecard construction.
Current state
Gradient-boosted trees with post-hoc explanation (SHAP) are increasingly used behind the scenes for risk ranking, with a simpler, fully transparent model or a distilled scorecard used for the customer-facing decision and reason codes — a two-model pattern driven directly by the interpretability requirement above, not by a technical limitation of tree models.
What to learn next
- Reliability diagrams and calibration error — checking whether predicted probabilities are trustworthy, not only well-ranked.
- SHAP and LIME — the explanation methods behind the reason codes discussed above.
- Model risk management — the governance process a real credit model must pass through.