爲了提高評分卡模型的開發效率,我爲 R 語言社區貢獻了一個開源項目 scorecard 包 (HomePage, Github, CRAN)。該 R 包提供了評分卡開發過程中的常用功能,包括變量粗篩、分箱與 woe 轉換、模型評估、評分刻度轉換等。
評分卡模型的開發流程通常包括以下五個主要步驟:數據準備、WOE 分箱、模型擬合、模型評估、評分卡刻度。下面結合 scorecard 包完成一個簡單的評分卡模型開發案例。更加詳細的評分卡模型開發介紹請參考幻燈片。
數據準備
首先加載 scorecard 包,並載入包內自帶的德國信貸數據集。該數據集包含了1000個借款人的信貸數據,20個 X 特徵與1個 Y 值。其詳細信息參見 UCI 的德國信貸數據集網站。
library(scorecard)
# load germancredit data
data(germancredit)
載入數據集後,可先通過變量的 IV 值、缺失率以及單類別率對 X 特徵進行初步篩選。var_filter 函數默認刪除信息值小於0.02、缺失率大於95%或單類別比例大於95%的變量。var_filter 函數還能夠人爲設定需要刪除或保留的變量,以及夠返回變量刪除的原因列表。
# filter variable via missing rate, iv, identical rate
dt = var_filter(germancredit, y = 'creditability')
## [INFO] filtering variables ...
將經過初篩的數據集拆分爲訓練集與測試集。在 split_df 函數中如果指定了 y 變量,那麼將基於 y 變量分層拆分,如果沒有指定,則隨機拆分數據集。ratio 爲拆分後兩個數據集的樣本量佔比。 seed 爲隨機種子,用於重現拆分的樣本。
# breaking dt into train and test
dt_list = split_df(dt, y="creditability", ratio = 0.6, seed = 30)
label_list = lapply(dt_list, function(x) x$creditability)
WOE分箱
接下來對數據集進行分箱與 woe 值轉換。由於這個數據集樣本量比較小,我們可以直接對全樣本進行分箱處理。如果數據量允許,應該使用訓練集進行分箱,並使用訓練集得到的 woe 值對其他數據集進行woe替換。
woebin 函數提供了樹形 tree、卡方合併 chimerge、等寬 width 與等高 freq 四種分箱方法。以樹形分箱爲例,默認情形是當信息值增益率 stop_limit 小於0.1, 或分箱數 bin_num_limit 大於8(缺失值除外)時停止分箱,同時確保每一個分箱的樣本佔比 count_distr_limit 不小於5%。當然還能夠通過 breaks_list 手動設定分箱節點。
woebin 函數輸出的結果爲多個 data.frame 組成的 list,可通過 data.table::rbindlist 或 dplyr::bind_rows 函數合併爲一個數據框然後保存。當然 woebin 函數也能夠直接輸出一個由分箱切割點組成的 list 並保存,下次使用時直接通過 woebin 函數對 breaks_list 參數對新的數據集進行分箱。
woebin_adj 函數可逐個觀察每個變量的分箱情況。如果不滿意默認的分箱結果,可以手動修改。最終返回一個經過手動調整的分箱節點。
分箱之後,需要使用 woebin_ply 函數將訓練集與測試集轉換爲對應的 woe 值。
# woe binning
bins = woebin(dt, "creditability", print_step=0)
## [INFO] creating woe binning ...
bins[[12]]
## variable bin count count_distr good bad badprob woe bin_iv total_iv breaks
## 1: other.installment.plans bank%,%stores 186 0.186 110 76 0.4086 0.4776 0.04594 0.05759 bank%,%stores
## 2: other.installment.plans none 814 0.814 590 224 0.2752 -0.1212 0.01166 0.05759 none
## is_special_values
## 1: FALSE
## 2: FALSE
woebin_plot(bins[[12]])
## $other.installment.plans
# converting train and test into woe values
dt_woe_list = lapply(dt_list, function(x) woebin_ply(x, bins))
## [INFO] converting into woe values ...
模型擬合
當獲得了 woe 值替換之後的數據集,可以使用邏輯迴歸進行擬合,並通過AIC、LASSO等方法對變量進一步篩選。下面使用基於 AIC 的逐步迴歸進一步篩選變量,最終得到了一個擁有13個變量的模型。
# lr
m1 = glm( creditability ~ ., family = binomial(), data = dt_woe_list$train)
# vif(m1, merge_coef = TRUE) # summary(m1)
# Select a formula-based model by AIC (or by LASSO for large dataset)
m_step = step(m1, direction="both", trace = FALSE)
m2 = eval(m_step$call)
vif(m2, merge_coef = TRUE) # summary(m2)
## variable Estimate Std. Error z value Pr(>|z|) gvif
## 1: (Intercept) -0.9448 0.1094 -8.639 0.0000 NA
## 2: status.of.existing.checking.account_woe 0.7756 0.1380 5.619 0.0000 1.042
## 3: duration.in.month_woe 0.7963 0.2291 3.476 0.0005 1.181
## 4: credit.history_woe 0.8308 0.2035 4.082 0.0000 1.064
## 5: purpose_woe 0.8632 0.2755 3.133 0.0017 1.043
## 6: credit.amount_woe 0.7669 0.2838 2.702 0.0069 1.251
## 7: savings.account.and.bonds_woe 0.8545 0.2606 3.279 0.0010 1.039
## 8: installment.rate.in.percentage.of.disposable.income_woe 1.8621 0.6822 2.730 0.0063 1.094
## 9: other.debtors.or.guarantors_woe 2.1018 0.8922 2.356 0.0185 1.037
## 10: age.in.years_woe 1.0154 0.3001 3.383 0.0007 1.033
## 11: other.installment.plans_woe 0.7623 0.4347 1.754 0.0795 1.060
## 12: housing_woe 0.7610 0.3665 2.077 0.0378 1.035
模型評估
通過邏輯迴歸獲得各變量的擬合係數之後,可以計算出各個樣本爲壞客戶的概率,然後評估模型的預測效果。 perf_eva 函數能夠計算的評估指標包括 mse, rmse, logloss, r2, ks, auc, gini,以及繪製多種可視化圖形 ks, lift, gain, roc, lz, pr, f1, density。
## predicted proability
pred_list = lapply(dt_woe_list, function(x) predict(m2, x, type='response'))
## performance
perf = perf_eva(pred = pred_list, label = label_list)
## [INFO] The threshold of confusion matrix is 0.3133.
評分卡刻度
當我們獲得了各個變量的分箱結果,並且確定了最終進入模型的變量以及係數,則可以創建標準評分卡。
有了評分卡之後,可用於對新樣本進行打分,從而評估該客戶的信用水平,並最終作出審批決策。
最後,評分卡模型的開發過程,還需要對模型的穩定性進行評估,即計算psi。
## scorecard
card = scorecard(bins, m2)
## credit score
score_list = lapply(dt_list, function(x) scorecard_ply(x, card))
## psi
perf_psi(score = score_list, label = label_list)
## $psi
## Null data.table (0 rows and 0 cols)
以上代碼均可以在該項目的主頁獲取。