Xếp hạn tín dụng. Xếp hạn tín dụng (Credit Classificatin hay Credit Scoring) đóng một vai trò quan trọng đối với lợi nhuận và phát triển bền vững của ngân hàng nói riêng cũng như các tổ chức tài chính cho vay khác. Sự kiện cuộc khủng hoảng tài chính dẫn đến sự sụp đổ của một loạt các định chế tài chính nói chung và ngân hàng nói riêng đã thức tỉnh các tổ chức này chú trọng hơn đến vai trò của thẩm định tín dụng trong hoạt động của mình. Hầu hết lợi nhuận của các ngân hàng đến từ hoạt động cấp tín dụng và cho vay. Cấp tín dụng là một trong những hoạt động tạo ra một tỉ trọng lớn về doanh thu và lợi nhuận cho ngân hàng nhưng cũng tiềm ẩn rất nhiều rủi ro (Zakrzewska, 2007). Theo Thomas et al. (2002), các ngân hàng cần một phương pháp xếp hạng tín dụng mà thỏa mãn những đòi hỏi sau: (1) chi phí rẻ và dễ vận hành, (2) nhanh chóng và ổn định, (3) đưa ra những quyết định nhất quán dựa trên các thông tin khách quan không phụ thuộc vào cảm xúc và tình cảm chủ quan của con người, và (4) hiệu quả của hệ phương pháp xếp hạng tín dụng có thể dễ dàng kiểm tra, điều chỉnh ở bất kì thời điểm nào nhằm điều chỉnh kịp thời với những thay đổi về chính sách hoặc điều kiện của nền kinh tế.
Cách tiếp cận thường thấy. Xếp hạng tín dụng là một bài toán mà tổ chức tín dụng - ngân hàng đã xây dựng và áp dụng. Tuy vậy hầu hết hướng đi hoặc triển khai - áp dụng chỉ mới tập trung vào việc so sánh mức độ chính xác chung (Accuracy) hoặc AUC/Gini giữa các mô hình rồi đưa ra khuyến cáo lựa chọn và triển khai sử dụng mô hình. Đây là một hướng tiếp cận chưa đúng vì nó bỏ qua mục tiêu cơ bản của một tổ chức hoạt động vì lợi nhuận. Điều này được giải thích ở mục ngay dưới đây.
Cách tiếp cận mới: Hướng đến lợi nhuận. Một mô hình hoặc cách tiếp cận có Accuracy, thậm chí là AUC/Gini cao lại là mô hình mang lại nhiều thiệt hại về tiền cho tổ chức sử dụng. Nguyên nhân là các mô hình / cách tiếp cận gần như quên mất rằng các tổ chức hoạt động vì lợi nhuận thì mục tiêu hàng đầu là PROFIT chứ không phải là Accuracy, AUC hay Gini. AUC/Gini chỉ là điều kiện cần, là dấu hiệu của một mô hình tốt về mặt lợi nhuận cho tổ chức sử dụng. Để minh họa chúng ta xét bộ dữ liệu giả định chỉ có 9 khách hàng xin vay tín dụng với số tiền vay đề xuất thể hiện ở cột LOAN. Cột Model1 và Model2 lần lượt là nhãn được dự báo từ mô hình cận 1 và mô hình 2 còn Actual là nhãn thực tế:
# Load some packages:
library(tidyverse)
library(knitr)
# Artificial dataset:
loan_info <- tibble(ID = 1:9,
Loan = c(1000, 800, 1200, 800, 2000, 1500, 1700, 3000, 1200),
Actual = c("Good", "Bad", "Good", "Good", "Good",
"Good", "Good", "Bad", "Good"),
Model1 = c("Good", "Good", "Good", "Bad", "Good",
"Good", "Good", "Bad", "Good"),
Model2 = c("Bad", "Bad", "Bad", "Bad", "Good",
"Bad", "Good", "Bad", "Good"))
# Our data:
loan_info %>%
mutate_if(is.character, as.factor) -> loan_info
loan_info %>%
kable()
ID | Loan | Actual | Model1 | Model2 |
---|---|---|---|---|
1 | 1000 | Good | Good | Bad |
2 | 800 | Bad | Good | Bad |
3 | 1200 | Good | Good | Bad |
4 | 800 | Good | Bad | Bad |
5 | 2000 | Good | Good | Good |
6 | 1500 | Good | Good | Bad |
7 | 1700 | Good | Good | Good |
8 | 3000 | Bad | Bad | Bad |
9 | 1200 | Good | Good | Good |
Accuracy là 77.78% và 55.56% lần lượt cho Model 1 và Model 2 như chúng ta có thể thấy qua, ví dụ, ma trận nhầm lẫn:
## Confusion Matrix and Statistics
##
## Reference
## Prediction Bad Good
## Bad 1 1
## Good 1 6
##
## Accuracy : 0.7778
## 95% CI : (0.3999, 0.9719)
## No Information Rate : 0.7778
## P-Value [Acc > NIR] : 0.6781
##
## Kappa : 0.3571
##
## Mcnemar's Test P-Value : 1.0000
##
## Sensitivity : 0.5000
## Specificity : 0.8571
## Pos Pred Value : 0.5000
## Neg Pred Value : 0.8571
## Prevalence : 0.2222
## Detection Rate : 0.1111
## Detection Prevalence : 0.2222
## Balanced Accuracy : 0.6786
##
## 'Positive' Class : Bad
##
## Confusion Matrix and Statistics
##
## Reference
## Prediction Bad Good
## Bad 2 4
## Good 0 3
##
## Accuracy : 0.5556
## 95% CI : (0.212, 0.863)
## No Information Rate : 0.7778
## P-Value [Acc > NIR] : 0.9696
##
## Kappa : 0.25
##
## Mcnemar's Test P-Value : 0.1336
##
## Sensitivity : 1.0000
## Specificity : 0.4286
## Pos Pred Value : 0.3333
## Neg Pred Value : 1.0000
## Prevalence : 0.2222
## Detection Rate : 0.2222
## Detection Prevalence : 0.6667
## Balanced Accuracy : 0.7143
##
## 'Positive' Class : Bad
##
Nếu căn cứ vào Accuracy chúng ta dễ bị lôi kéo với nhận định rằng mô hình 1 sẽ nên được áp dụng và triển khai cho việc cấp tín dụng. Tuy nhiên đây lại là một nhận định sai. Thực vậy nếu chúng ta giả thiết rằng: (1) nếu hồ sơ xin vay thực sự là tốt và mô hình dự đoán đúng là tốt thì lợi nhuận thu về sẽ là 10% của khoản vay, (2) nếu hồ sơ thực sự là xấu nhưng mô hình dự đoán nhầm thành tốt thì mất hoàn toàn khoản đã cho vay, và (3) tất cả các trường hợp mà mô hình dự đoán là xấu thì sẽ không cho vay và do vậy lợi nhuận là 0. Dưới đây là tính toán lợi nhuận tương ứng cho mô hình 1 và mô hình 2 đối với từng khách hàng:
loan_info %>%
mutate(Profit1 = case_when(Model1 == "Good" & Actual == "Good" ~ 0.1*Loan,
Model1 == "Good" & Actual == "Bad" ~ -1*Loan,
TRUE ~ 0)) %>%
mutate(Profit2 = case_when(Model2 == "Good" & Actual == "Good" ~ 0.1*Loan,
Model2 == "Good" & Actual == "Bad" ~ -1*Loan,
TRUE ~ 0)) -> df_profit
df_profit %>%
kable()
ID | Loan | Actual | Model1 | Model2 | Profit1 | Profit2 |
---|---|---|---|---|---|---|
1 | 1000 | Good | Good | Bad | 100 | 0 |
2 | 800 | Bad | Good | Bad | -800 | 0 |
3 | 1200 | Good | Good | Bad | 120 | 0 |
4 | 800 | Good | Bad | Bad | 0 | 0 |
5 | 2000 | Good | Good | Good | 200 | 200 |
6 | 1500 | Good | Good | Bad | 150 | 0 |
7 | 1700 | Good | Good | Good | 170 | 170 |
8 | 3000 | Bad | Bad | Bad | 0 | 0 |
9 | 1200 | Good | Good | Good | 120 | 120 |
Mô hình 1 dự báo sai cho khách hàng thứ 3 và do vậy ngân hàng mất trắng 800$ khi cho khách hàng này vay. Thành thử dù rằng mô hình 1 có Accuracy cao nhưng lợi nhuận mang về nếu sử dụng mô hình 1 chi chưa bằng 1 / 8 nếu sử dụng mô hình 2 như ta có thể thấy:
## Profit1 Profit2
## 60 490
Điều tương tự cũng áp dụng cho nếu lựa chọn mô hình căn cứ vào AUC/Gini: không có gì đảm bảo rằng một mô hình có AUC/Gini cao lại là mô hình mang lại nhiều lợi nhuận cho tổ chức sử dụng và điều này càng đúng trong tình huống mà bộ dữ liệu là mất cân bằng ở mức cao (như bộ dữ liệu từ cuộc thi Kalapa’s Credit Scoring Challenge). Nhắc lại: đó chỉ mới là điều kiện cần. Một mô hình tuy có AUC/Gini thấp nhưng lại có thể dự báo tốt đối với nhóm khách hàng xấu (tức Recall cao) thì đó có thể là mô hình nên được sử dụng.
Nguyên nhân là sai lầm loại I và sai lầm loại II đi kèm với cái giá phải trả là khác nhau. Nhân chuyện Virut Corona đang là hot trend thì nói thế này cho đẽ nhớ: nếu một người bị nhiễm Corona nhưng mô hình chẩn đoán sai thành không nhiễm thì cái giá phải trả đắt hơn nhiều một người không bị Corona nhưng bị chẩn đoán sai thành nhiễm Corona. Tương tự, nếu dự báo nhầm một khách hàng tốt thành khách hàng xấu thì ngân hàng bỏ lỡ một cơ hội kiếm lãi trên khoản tiền cho vay nhưng dự báo sai một khách hàng xấu thành tốt thì sẽ mất trắng khoản đầu tư của mình.
Tiêu chí lựa chọn và đánh giá phẩm chất của mô hình phân cho bài toán Credit Scoring có thể tham khảo chi tiết ở trang 260, chương 11 (Measuring Performance in Classification Models) cuốn Applied Predictive Modeling.
Các bước thực hiện Bayesian Optimization ở post trước có thể được áp dụng để tìm kiếm ngưỡng phân loại (Classification Cutoff) sao cho tối đa hóa lợi nhuận. Trước hết sử dụng lại một số codes từ post này từ bước chuẩn bị dữ liệu đến huấn luyện Xgboost mặc định:
#=================================
# Data Pre-processing
#=================================
# Clear workspace:
rm(list = ls())
# Import data:
hmeq <- read_csv("http://www.creditriskanalytics.net/uploads/1/9/5/1/19511601/hmeq.csv")
# Dtage 1 - Impute missing data:
library(missRanger)
missRanger(hmeq, seed = 29) -> hmeq_imputed
##
## Missing value imputation by random forests
##
## Variables to impute: MORTDUE, VALUE, REASON, JOB, YOJ, DEROG, DELINQ, CLAGE, NINQ, CLNO, DEBTINC
## Variables used to impute: BAD, LOAN, MORTDUE, VALUE, REASON, JOB, YOJ, DEROG, DELINQ, CLAGE, NINQ, CLNO, DEBTINC
## iter 1: ...........
## iter 2: ...........
## iter 3: ...........
# Stage 2 - Conduct one-hot encoding process:
dummies <- dummyVars(~ REASON + JOB, data = hmeq_imputed)
predict(dummies, hmeq_imputed) %>% as.data.frame() -> features_oneHot
# Final data for modelling:
hmeq_imputed %>%
select_if(is.numeric) %>%
bind_cols(features_oneHot) -> df_final
# Split data:
set.seed(1)
id <- createDataPartition(df_final$BAD, p = 0.7, list = FALSE)
train <- df_final[id, ]
test <- df_final[-id, ]
#============================
# Modelling with XGBoost
#============================
# Convert to DMatrix form for train data:
X_train <- train %>%
select(-BAD) %>%
as.matrix()
Y_train <- train %>%
pull(BAD)
library(xgboost)
dtrain <- xgb.DMatrix(data = X_train, label = Y_train)
#------------------------------------------
# Train XGBoost with default parameters
#------------------------------------------
# Train a default XGBoost:
xgb1 <- xgboost(data = dtrain,
objective = "binary:logistic",
verbose = 0,
nround = 30)
Các mô Machine Learning - kể cả Xgboost sẽ tính toán xác suất vỡ nợ PD (Probability of Default) và sử dụng ngưỡng mặc định là 0.5 để dự báo nhãn rồi tính toán ma trận nhầm lẫn (hàm confusion_matrix() của Python và confusionMatrix() của R). Tất cả các tool khác đều mặc định sử dụng ngưỡng này. Do vậy, để khảo sát lợi nhuận tương ứng với mỗi ngưỡng chúng ta phải viết một hàm. Dưới đây chúng ta viết một hàm tính toán lợi nhuận cho 1000 khách hàng xin vay từ 1788 khách hàng ở bộ dữ liệu test theo phương pháp lấy mẫu ngẫu nhiên rồi lặp lại quá trình này 100 lần để tính lợi nhuận (Profit) trung bình tương ứng với một ngưỡng cutoff được chọn. Các giả thiết khách là:
Hàm đó như sau:
# Function calculates mean profit for 1000 case
# randomly selected with a specific cutoff:
get_profit <- function(cutoff) {
rate <- 0.1
p <- 1000 / nrow(test)
prof_space <- c()
for (j in 1:100) {
set.seed(j)
id <- createDataPartition(test$BAD, p = p, list = FALSE)
test_mini <- test[id, ]
pd <- predict(xgb1, test_mini %>% select(-BAD) %>% as.matrix())
label_pred <- case_when(pd >= cutoff ~ "Bad", TRUE ~ "Good")
test_mini %>%
mutate(LabelPred = label_pred) %>%
select(BAD, LabelPred, LOAN) %>%
mutate(Profit = case_when(LabelPred == "Good" & BAD == 0 ~ rate*LOAN,
LabelPred == "Good" & BAD == 1 ~ -1*LOAN,
TRUE ~ 0)) -> df_profit
df_profit %>% pull(Profit) %>% sum() -> total_profit
prof_space <- c(prof_space, total_profit)
}
return(list(Score = mean(prof_space) / 1000, Pred = NULL))
}
Kế tiếp thực hiện Bayesian Optimization theo các thủ tục như đã được trình bày ở post trước:
#=============================================
# Search best cutoff that maximizes profit
#=============================================
# Space of rate:
my_rate <- list(cutoff = c(0, 1))
# Search optimal hyperparameter by Bayesian Optimization:
library(rBayesianOptimization)
system.time(OPT_Res <- BayesianOptimization(get_profit,
bounds = my_rate,
init_points = 10,
n_iter = 20,
acq = "ucb",
kappa = 2.576,
eps = 0.0,
verbose = FALSE))
##
## Best Parameters Found:
## Round = 16 cutoff = 0.1229 Value = 989.0822
## user system elapsed
## 6308.511 5889.070 537.624
kết quả ngưỡng tối ưu và lợi nhuận trung bình tương ứng là:
## cutoff
## 0.1228951
chúng ta có theo dõi sự thay đổi của lợi nhuận trung bình này tương ứng với cutoff được chọn trong đó ngưỡng mà tối đa lợi nhuận trung bình này là điểm màu đỏ:
OPT_Res$History %>%
data.frame() %>%
mutate(MeanProfit = Value / 1000) -> mean_pro_df
mean_pro_df %>%
ggplot(aes(cutoff, MeanProfit)) +
geom_line() +
geom_point() +
geom_point(data = mean_pro_df %>% dplyr::slice(-which.max(MeanProfit)), color = "blue") +
geom_point(data = mean_pro_df %>% dplyr::slice(which.max(MeanProfit)), color = "red") +
theme_minimal() +
labs(x = "Cutoff",
title = "Figure 1: Optimal cutoff that maximizes profit for XGBoost") -> p
plotly::ggplotly(p)
NSử dụng ngưỡng bestRate
tối ưu tìm được để sử dụng cho quyết định cho một khách hàng cụ thể được vay hay không và so sánh với phương án sử dụng ngưỡng mặc định. Trước hết tính toán lợi nhuận của hai phương án (ProfitDefault và ProfitCutoff) tướng ứng với việc sử dụng ngưỡng mặc định (Des1) và ngưỡng tối ưu (Des2):
#=============================================
# Compare profit between the two Scenarios
#=============================================
# Calculate Probability of Default (PD):
pd <- predict(xgb1, test %>% select(-BAD) %>% as.matrix())
# Calculate profit:
test %>%
mutate(PD = pd, Rate = 0.1) %>%
select(BAD, LOAN, PD, Rate) %>%
mutate(Des1 = case_when(PD >= 0.5 ~ "Bad", TRUE ~ "Good"),
Des2 = case_when(PD >= bestRate ~ "Bad", TRUE ~ "Good")) %>%
mutate(ProfitDefault = case_when(Des1 == "Good" & BAD == 0 ~ Rate*LOAN,
Des1 == "Good" & BAD == 1 ~ -1*LOAN,
TRUE ~ 0)) %>%
mutate(ProfitCutoff = case_when(Des2 == "Good" & BAD == 0 ~ Rate*LOAN,
Des2 == "Good" & BAD == 1 ~ -1*LOAN,
TRUE ~ 0)) -> df_profit
# Show some cases:
df_profit %>%
head(n = 10) %>%
kable()
BAD | LOAN | PD | Rate | Des1 | Des2 | ProfitDefault | ProfitCutoff |
---|---|---|---|---|---|---|---|
1 | 1300 | 0.8565313 | 0.1 | Bad | Bad | 0 | 0 |
1 | 1800 | 0.9763749 | 0.1 | Bad | Bad | 0 | 0 |
1 | 2000 | 0.9932522 | 0.1 | Bad | Bad | 0 | 0 |
1 | 2000 | 0.8356385 | 0.1 | Bad | Bad | 0 | 0 |
1 | 2000 | 0.9899027 | 0.1 | Bad | Bad | 0 | 0 |
1 | 2000 | 0.9504011 | 0.1 | Bad | Bad | 0 | 0 |
1 | 2000 | 0.8772576 | 0.1 | Bad | Bad | 0 | 0 |
0 | 2300 | 0.0774459 | 0.1 | Good | Good | 230 | 230 |
1 | 2300 | 0.9933444 | 0.1 | Bad | Bad | 0 | 0 |
1 | 2400 | 0.9401898 | 0.1 | Bad | Bad | 0 | 0 |
Lợi nhuận của việc sử dụng ngưỡng tối ưu cho Creding Scoring là vượt trội so với sử dụng ngưỡng mặc định như chúng ta có thể thấy trong Figure 2:
# Compare profit:
df_profit %>%
select(contains("Pro")) %>%
gather(key = "Scenario", value = "Profit") %>%
group_by(Scenario) %>%
summarise(Profit = sum(Profit) / 1000) %>%
ggplot(aes(Scenario, Profit, fill = Scenario)) +
geom_col() +
theme_minimal() +
theme(axis.title = element_blank()) +
theme(axis.text.x = element_blank()) +
labs(x = NULL, title = "Figure 2: Profit between two scenarios") -> g
plotly::ggplotly(g)
Một lần nữa chúng ta có thể xác nhận rằng cách tiếp cận 1 (hay rộng hơn là mô hình 1) sử dụng ngưỡng mặc định có Accuracy cao hơn hẳn dù đây là cách tiếp cận tạo ra lợi nhuận không cao:
# Confusion matrix when cutoff = 0.5 and cutoff = bestRate:
df_profit %>%
mutate(TrueLabel = case_when(BAD == 1 ~ "Bad", TRUE ~ "Good")) %>%
mutate_if(is.character, as.factor) -> df_profit
confusionMatrix(df_profit$Des1, df_profit$TrueLabel, positive = "Bad") # Default cutoff.
## Confusion Matrix and Statistics
##
## Reference
## Prediction Bad Good
## Bad 248 20
## Good 104 1416
##
## Accuracy : 0.9306
## 95% CI : (0.9179, 0.942)
## No Information Rate : 0.8031
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.759
##
## Mcnemar's Test P-Value : 9.081e-14
##
## Sensitivity : 0.7045
## Specificity : 0.9861
## Pos Pred Value : 0.9254
## Neg Pred Value : 0.9316
## Prevalence : 0.1969
## Detection Rate : 0.1387
## Detection Prevalence : 0.1499
## Balanced Accuracy : 0.8453
##
## 'Positive' Class : Bad
##
## Confusion Matrix and Statistics
##
## Reference
## Prediction Bad Good
## Bad 316 202
## Good 36 1234
##
## Accuracy : 0.8669
## 95% CI : (0.8503, 0.8823)
## No Information Rate : 0.8031
## P-Value [Acc > NIR] : 7.8e-13
##
## Kappa : 0.6427
##
## Mcnemar's Test P-Value : < 2e-16
##
## Sensitivity : 0.8977
## Specificity : 0.8593
## Pos Pred Value : 0.6100
## Neg Pred Value : 0.9717
## Prevalence : 0.1969
## Detection Rate : 0.1767
## Detection Prevalence : 0.2897
## Balanced Accuracy : 0.8785
##
## 'Positive' Class : Bad
##
Nguyên nhân là mô hình sử dụng Optimal cutoff = bestRate
tuy có Accuracy thấp hơn nhưng đây chính lại là mô hình có Recall (hay Sensitivity) cao hơn. Nhắc lại rằng Recall/Sensitivity là thước đo đánh giá khả năng phân biệt - dự báo nhóm khách hàng xấu.
Như đã phân tích ở mục trước, nguyên nhân là, ví dụ, khách hàng ở dòng đầu tiên thì sử dụng ngưỡng mặc định 0.5 sẽ được cho là khách hàng tốt (vì PD < 0.5) nhưng thực tế đây là khách hàng xấu (BAD = 1) nên khi cấp một khoản vay cho khách hàng này ngân hàng sẽ mất trắng vốn (lợi nhuận tương ứng của khách hàng này mang dấu âm trên cột ProfitCutoff) trong khi đó “mô hình” phân loại sử dụng ngưỡng tối ưu sẽ xếp khách này là xấu (và thực tế đúng là vậy) như ta thấy:
df_profit %>%
filter(Des1 == "Good", Des2 == "Bad", TrueLabel == "Bad") %>%
head() %>%
knitr::kable()
BAD | LOAN | PD | Rate | Des1 | Des2 | ProfitDefault | ProfitCutoff | TrueLabel |
---|---|---|---|---|---|---|---|---|
1 | 5000 | 0.4625529 | 0.1 | Good | Bad | -5000 | 0 | Bad |
1 | 5000 | 0.3907019 | 0.1 | Good | Bad | -5000 | 0 | Bad |
1 | 5700 | 0.3001138 | 0.1 | Good | Bad | -5700 | 0 | Bad |
1 | 7000 | 0.3496028 | 0.1 | Good | Bad | -7000 | 0 | Bad |
1 | 8600 | 0.3134321 | 0.1 | Good | Bad | -8600 | 0 | Bad |
1 | 8700 | 0.1312989 | 0.1 | Good | Bad | -8700 | 0 | Bad |
Bayesian Optimization không chỉ được sử dụng để tinh chỉnh và tìm kiếm tham số tối ưu cho các mô hình Machine Learning mà còn có thể được sử dụng để tối ưu hóa Lợi Nhuận - một mục tiêu cụ thể và đặc thù của bài toán Credit Scoring.
Với các tổ chức hoạt động vì lợi nhuận thì Accuracy, AUC/Gini hay bất cứ tiêu chí gì có thể nghĩ ra mà không cải thiện được lợi nhuận của tổ chức thì cũng không có ý nghĩa nhiều ngoài giá trị tham khảo. Trong một số tình huống cụ thể như kinh tế bất ổn cần siết chặt tín dụng và hoạt động cho vay thì rủi ro về tổn thất vốn là mục tiêu hàng đầu lúc đó thì Recall/Sensitivity - chỉ tiêu đánh chất lượng phân loại cho nhóm khách hàng xấu có thể được ưu tiên hàng đầu.
Ngoài sử dụng Bayesian Optimization chúng ta có thể tìm ngưỡng tối ưu một cách thủ công (nhưng hiệu quả) nếu sử dụng R hoặc sử dụng Python
Martens, D., B. Baesens, T. Van Gestel, and J. Vanthienen. 2007. “Comprehensible Credit Scoring Models Using Rule Extraction from Support Vector Machines.” European Journal of Operational Research 183:1466–1476.
Baesens, B., Roesch, D., & Scheule, H. (2016). Credit risk analytics: Measurement techniques, applications, and examples in SAS. John Wiley & Sons.
Siddiqi, N. (2012). Credit risk scorecards: developing and implementing intelligent credit scoring. John Wiley & Sons.
Anderson R (2007): The Credit Scoring Toolkit: Theory and Practice for Retail Credit Risk Management and Decision Automation. Oxford, Oxford University Press.
Thomas LC (2009): Consumer Credit Models: Pricing, Profit, and Portfolio. Oxford, Oxford University Press.
Ben-David, A., & Frank, E. (2009). Accuracy of machine learning models versus “hand crafted” expert systems–a credit scoring case study. Expert Systems with Applications, 36(3), 5264-5271.