構建lending club的申請評分卡模型
建模不能脫離商業環境和業務訴求。有時候數學上的最佳答案並不是商業上最佳選擇。
——范若愚《大數據時代的商業建模》
建模之前的幾個假設:
- 研究對象未來的行為模式與過去的相似;
- 社會政治經濟的大環境基本不變。
Lending Club是全球最大的撮合借款人和投資人的線上金融平台,它利用互聯網模式建立了一種比傳統銀行系統更有效率的、能夠在借款人和投資人之間自由配置資本的機制。
經過1000多行的代碼,我完成了構建lending club的評分卡模型。
文章大致分為下列幾個階段
- 好壞定義說明
- 樣本概述和說明
- 輸入變數和單變數探索以及部分數據字典
- 缺失值處理,同值化處理
- 篩選變數(基於隨機森林和IV值)
- 最優分箱
- 初步模型結果
- 參數估計結果
- 初步評分卡結果
- 初步模型結果
- 模型的穩定性校驗
- 最終模型結果
數據集是2016年第4季度和2017年第1,2季度的數據,數據集的下載地址為:
Lending Club Statistics
另外:
機器學習領域一般稱之為特徵。數據建模一般稱之為變數。比如特徵選擇,變數篩選,其實本質是一樣的。本文後面不在贅述。
(一)好壞定義說明
要開發一個申請的評分卡,最重要的就是先定義好壞。也就是編碼y變數,0和1 。
我們先要去了解一下Lending Club的大概情況:
Lending Club的深度報告及其商業模式查看本次數據的y值如下:
得知lending club的貸款產品貸款期限在36期或者60期,也就是分期產品。
貸款金額普遍在1000$ - 40000$之間。
其實我們看到很多同學在做這個lending club的分析或者建模的時候,對於好壞的分析太多不清晰,大部分人都是粗暴的直接分為好壞,而沒有劃分Indeterminate(不確定)。
根據行業經驗,正常還款12期以上的借款人,在12期以後的逾期率會趨於穩定,我們依次作為表現期。
我們用來定義好壞,我們定義逾期30天以上的客戶為壞客戶。
其中,好壞定義如下:
Bad: Late (31-120 days) , Charged Off(壞賬)
Indeterminate: Late (16-30 days) ,In Grace Period (處於寬限期)
Good: Current, Fully Paid(結清)
##########################定義好壞#定義新函數 , 給出目標Y值def coding(col, codeDict): colCoded = pd.Series(col, copy=True) for key, value in codeDict.items(): colCoded.replace(key, value, inplace=True) return colCoded#把貸款狀態LoanStatus編碼為逾期=1, 正常=0:pd.value_counts(df["loan_status"])df["loan_status"] = coding(df["loan_status"], {Current:0,Fully Paid:0, Late (31-120 days):1,Charged Off:1, Late (16-30 days):2,In Grace Period:2,Default:2})print(
After Coding:)
(二)樣本概述和說明
表現窗口:在時間軸上從觀察點向後推得的表現窗口,用來提取目標變數和進行表現排除。
觀察窗口:從觀察點向前推一段時間得到觀察窗口,用來提取自變數信息和進行觀察窗口排除,觀察窗口一般長度通常為6-12個月。
我們下載數據的時間是2018年2月,那麼我們開發的樣本到目前為止還款全部超過12個月。驗證樣本在未來3個月也會全部全部還款到12個月。
本次開發樣本總共有204185條數據;驗證樣本數據有101018條數據,所以我們的數據量還是比較大的。
df.groupby(y).size()y0.0 1860911.0 116186.24%
我們這裡的數據,bad佔比為6%多,數據屬於不平衡樣本。後期要做不平衡樣本處理。
(三)輸入變數和單變數探索以及部分數據字典
我們的數據裡面有145個變數, 部分比較重要的變數及其解釋如下。
下面探索每個變數的意義,了解數據的情況,是本次模型構建最耗時間的。
由於本人英文水平和業務水平有限,部分變數可能翻譯不準。
部分變數如下:
風險等級,可以看到BC兩檔中風險的申請的人最多。高風險的人基本很少。
部分變數如下:
風險等級,可以看到BC兩檔中風險的申請的人最多。高風險的人基本很少。
工作年限,看到大部分都是10年以上的;
年收入來源,三者比較相當,比如來源確定,不確定。
房產性質,按揭和租賃的最多。
申請貸款金額;基本符合正態分布
總負債金額(總信貸金額),符合正態分布
貸款利率,基本符合正態分布
最近開設循環賬戶(信用卡)的月份數, 符合正態分布
過去24個的交易數目
(四)缺失值處理,同值化處理
1.刪除一些無意義的變數
在進行缺失值處理之前,我會將一部分無意義的,或貸中,貸後變數刪掉,以免向模型提前泄露信息。
id,member_id, 變數為空值,本身對建模無意義。
url,desc,zip_code,addr_state 數據對建模無意義。
刪除後,變數由145個變為139個。
2.同值化處理
變數的同值性如果一個變數大部分的觀測都是相同的特徵,那麼這個特徵或者輸入變數就是無法用來區分目標時間,一般來說,臨界點在90%。但是最終的結果還是應該基於業務來判斷。
本次處理,我刪掉了 臨界值大於94.1%的變數。
變數由139變為122個。
3.缺失值處理
對於缺失值處理,各種資料給出很多方法,
包括
- 直接捨棄(一般缺失值佔比較多的)
- 均值,中位數,眾數等填充法(一般缺失值佔比較少的),或者填充0
- 用上下數據進行填充
- 插值法(拉格朗日插值法)
- 機器學習演算法擬合或預測缺失值(如使用隨機森林進行缺失值預測)
我們這裡採取對缺失值大於95.7%的變數進行刪除。
對於缺失值10%-80%的變數單獨和Y變數編組,然後計算這幾個變數的IV值,R語言這個時候很好的計算出了缺失值的IV值。
library(smbinning) #最優分箱library(DMwR) #檢測離群值library(xlsx) ####################################################################################readFilePath<-"C:/Users/Administrator/Desktop/df7.csv"df<-read.csv(readFilePath)head(df)names(df)#smbinning(df, y, x, p = 0.05)#df: 數據#y: 二分類變數(0,1) 整型#x:連續變數:至少滿足10 個不同值,取值範圍有限#p:每個Bin記錄數佔比,默認5% (0.05) 範圍0%-50%#smbinning.plot, smbinning.sql,and smbinning.gen.result1<-smbinning(df=df,x="acc_open_past_24mths",y="y",p=0.05)smbinning.plot(result1,option="WoE",sub="acc_open_past_24mths")r1 <- merge(result1$x,result1$ivtable)result2<-smbinning(df=df,x="inq_last_12m",y="y",p=0.05)smbinning.plot(result2,option="WoE",sub="inq_last_12m")r2 <- merge(result2$x,result2$ivtable)result3<-smbinning(df=df,x="bc_open_to_buy",y="y",p=0.05)smbinning.plot(result3,option="WoE",sub="bc_open_to_buy")r3 <- merge(result3$x,result3$ivtable)result4<-smbinning(df=df,x="mths_since_rcnt_il",y="y",p=0.05)smbinning.plot(result4,option="WoE",sub="mths_since_rcnt_il")r4 <- merge(result4$x,result4$ivtable)r_total <- rbind(r1,r2,r3,r4)outFilePath <- "F:/TS/Lending_Club/04_output/03_r_smbining/r_best_binging.xlsx"write.xlsx(r_total, outFilePath) ##################################################################################### Information Value for all variables in one step ---------------------------smbinning.sumiv(df=df,y="y") # IV for eache variable# Plot IV for all variables -------------------------------------------------sumivt=smbinning.sumiv(df,y="y")sumivt # Display table with IV by characteristicpar(mfrow=c(1,1))smbinning.sumiv.plot(sumivt,cex=1) # Plot IV summary table####################################################################################
通過計算,我們看到這些變數除了mths_since_recent_inq和il_util,其他IV值都是小於0.01的,可以直接刪除。
##針對缺失值進行IV值計算和分箱null_data = df3[[mths_since_last_record,mths_since_recent_bc_dlq,mths_since_last_major_derog,mths_since_recent_revol_delinq,mths_since_last_delinq,il_util,mths_since_recent_inq,y]]
所以,查看缺失值情況,變數由122變為83個;
其中幾個40%-80%的缺失值其意義不大,無需進行missing編碼,也就是歸為一類。
il_util缺失12%,該變數的缺失部分可以分類進入0,所以賦值為0。
其他變數的缺失部分佔比都非常小,所以我們對其缺失部分賦值為0。
數據裡面有一些變數的觀測值不是數據值型,我們要做處理:
如:
##處理帶有百分號的數據df4[revol_util] = df4[revol_util].str.rstrip(%).astype(float)df4[int_rate] = df4[int_rate].str.rstrip(%).astype(float)df4[term] = df4[term].str.rstrip(months).astype(float)##刪掉一些無意義或者重複的變數,變數由83變為72個##刪除一些貸後的變數的,這些變數會向申請模型泄露信息=============================================================================# next_pymnt_d : 客戶下一個還款時間,沒有意義# emp_title :數據分類太多,實用性不大# last_pymnt_d :最後一個還款日期,無意義# last_credit_pull_d :最近一個貸款的時間,沒有意義# sub_grade : 與grade重複,分類太多# title: title與purpose的信息基本重複,數據分類太多# issue_d : 放款時間,申請模型用不上# earliest_cr_line : 貸款客戶第一筆借款日期# =============================================================================total_rec_prncp 已還本金total_rec_int 已還利息out_prncp 剩餘未償本金總額last_pymnt_d 最後一個還款日last_pymnt_amnt 最後還款金額next_pymnt_d 下一個還款日installment 每月分期金額bc_open_to_buy 數據字典中未找到percent_bc_gt_75 數據字典中未找到tot_hi_cred_lim 無法譯出真實意義mths_since_recent_inq 數據字典中未找到total_bc_limit 數據字典中未找到然後對字元型的數據進行編碼:mapping_dict = {"initial_list_status": {"w": 0,"f": 1,}, "emp_length": {"10+ years": 11,"9 years": 10,"8 years": 9, "7 years": 8,"6 years": 7,"5 years": 6,"4 years":5, "3 years": 4,"2 years": 3,"1 year": 2,"< 1 year": 1, "n/a": 0}, "grade": {"A": 0,"B": 1,"C": 2, "D": 3, "E": 4,"F": 5,"G": 6}, "verification_status": {"Not Verified":0,"Source Verified":1,"Verified":2}, "purpose": {"credit_card":0,"home_improvement":1,"debt_consolidation":2, "other":3,"major_purchase":4,"medical":5,"small_business":6, "car":7,"vacation":8,"moving":9, "house":10, "renewable_energy":11,"wedding":12}, "home_ownership": {"MORTGAGE":0,"ANY":1,"NONE":2,"OWN":3,"RENT":4}} df6 = df5.replace(mapping_dict)
(五)篩選變數(基於隨機森林和IV值)
為了創建評分卡,所以我們採用了計算WOE值,不對數據進行歸一化,啞變數處理。
1.IV值篩選變數
分箱的方法有幾種,包括等距分箱,等寬分箱,最優分箱等。
分箱的重要性及其優勢
- 離散特徵的增加和減少都很容易,易於模型的快速迭代;
- 稀疏向量內積乘法運算速度快,計算結果方便存儲,容易擴展;
- 離散化後的特徵對異常數據有很強的魯棒性;
- 離散化後可以進行特徵交叉,由M+N個變數變為M*N個變數,進一步引入非線性,提升表達能力;
- 特徵離散化以後,起到了簡化了邏輯回歸模型的作用,降低了模型過擬合的風險。
- 可以將缺失作為獨立的一類帶入模型。
本次我在做分箱時候,採用了 等距分箱,基於卡方的最優分箱,基於R語言smbinning包進行最優分箱。通過投票的方式,也根據業務的經驗,最後保留最優的分箱結果。
IV值的預測能力如下:
我先使用等距分箱對變數進行10等份處理,刪除掉IV<0.02的變數;
IV保留大於0.02的變數,63個變數保留26個
### 利用IV值來刪除不重要的特徵def filter_iv(data, group=10): iv_value,all_iv_detail = iv.cal_iv(data, group=group) ##利用IV值,先刪除掉IV值<0.02的特徵 IV值小於0.02,變數的預測能力太弱 list_value = iv_value[iv_value.ori_IV <= 0.02].var_name filter_data = iv_value[[var_name,ori_IV]].drop_duplicates() print(filter_data)
new_list = list(set(list_value)) print(小於0.02的變數有:,len(new_list)) print(new_list) #new_list.sort(key = list_value.index) drop_list = new_list new_data = data.drop(drop_list, axis = 1) return new_data, iv_valuevar_name ori_IVgroup_num0.0 int_rate 0.5039690.0 grade 0.4773030.0 verification_status 0.0814550.0 acc_open_past_24mths 0.0730150.0 inq_last_12m 0.0642790.0 inq_last_6mths 0.0637810.0 num_tl_op_past_12m 0.0613750.0 mths_since_rcnt_il 0.0467760.0 open_il_12m 0.0459330.0 open_rv_24m 0.0455440.0 open_il_24m 0.0449630.0 il_util 0.0438360.0 mo_sin_rcnt_tl 0.0435470.0 open_acc_6m 0.0415150.0 dti 0.0391860.0 all_util 0.0367190.0 inq_fi 0.0346880.0 mo_sin_rcnt_rev_tl_op 0.0318690.0 open_rv_12m 0.0304290.0 total_rec_int 0.0282720.0 mths_since_recent_bc 0.0272110.0 home_ownership 0.0267280.0 tot_cur_bal 0.0253990.0 mort_acc 0.0234170.0 avg_cur_bal 0.0230550.0 total_rev_hi_lim 0.0202630.0 mo_sin_old_rev_tl_op 0.0201190.0 funded_amnt 0.0180390.0 loan_amnt 0.0180390.0 initial_list_status 0.017818... ... ...0.0 annual_inc 0.0144770.0 purpose 0.0139330.0 revol_util 0.0135950.0 emp_length 0.0126340.0 mo_sin_old_il_acct 0.0108580.0 max_bal_bc 0.0083190.0 total_bal_il 0.0058450.0 total_cu_tl 0.0047880.0 revol_bal 0.0046080.0 open_act_il 0.0042650.0 pub_rec 0.0042450.0 num_actv_rev_tl 0.0041830.0 num_actv_bc_tl 0.0036100.0 total_bal_ex_mort 0.0034070.0 pub_rec_bankruptcies 0.0031730.0 delinq_2yrs 0.0030140.0 num_rev_tl_bal_gt_0 0.0027780.0 num_accts_ever_120_pd 0.0022390.0 total_il_high_credit_limit 0.0021440.0 num_bc_tl 0.0019690.0 num_sats 0.0019680.0 open_acc 0.0019590.0 num_bc_sats 0.0015900.0 num_il_tl 0.0015590.0 num_tl_90g_dpd_24m 0.0015330.0 pct_tl_nvr_dlq 0.0013600.0 num_rev_accts 0.0013370.0 total_acc 0.0011380.0 num_op_rev_tl 0.0010160.0 tot_coll_amt 0.000925[62 rows x 2 columns]小於0.02的變數有: 35[revol_bal, num_il_tl, num_bc_tl, num_actv_rev_tl, funded_amnt, max_bal_bc, revol_util, num_bc_sats, num_sats, tot_coll_amt, num_accts_ever_120_pd, open_act_il, term, total_cu_tl, purpose, num_rev_tl_bal_gt_0, pct_tl_nvr_dlq, emp_length, mo_sin_old_il_acct, num_rev_accts, pub_rec, delinq_2yrs, pub_rec_bankruptcies, open_acc, loan_amnt, total_bal_ex_mort, total_il_high_credit_limit, num_tl_90g_dpd_24m, num_actv_bc_tl, bc_util, num_op_rev_tl, annual_inc, total_acc, total_bal_il, initial_list_status]
然後對數據按照IV大小順序進行排序,以便於刪除相關性較高裡面IV值低的變數。
邏輯回歸對於相關性比較高的變數很敏感,所以要計算相關性。
皮爾森係數繪圖,觀察多重共線的變數多變數分析,保留相關性低於閾值0.6的變數。對產生的相關係數矩陣進行比較,並刪除IV比較小的變數,由26個變數,保留20個變數。
回歸分析中有一個假設,就是模型的變數中輸入的變數,即方程
y = a0 + a1x1 +a2x2 + ...anxn,
這裡的係數都是需要獨立不相關的。皮爾森係數>0.75(或<-0.75),在這裡,我們採用threshold = 0.60。
上圖中,黃色部分就是正相關性比較高的,深紫色是負相關性比較高。
刪除了相關性高於閾值的變數,再做可視化:
2.隨機森林篩選數據
機器學習裡面有很多特徵選擇(變數篩選)的方法
- 遞歸消除演算法來暴力選擇特徵,
- 頂層特徵選擇演算法;穩定性選擇,頂層特徵選擇演算法
這裡我也會做一下基於隨機森林的特徵選擇,然後作為IV值篩選之後的一個參考和篩選。
隨機森林會給出特徵的權重,我會比較與我剛才通過IV值以及相關係數保留下的變數作對照:
3.最優分箱結果
通過投票的方式,最優分箱結果如下:
(六)初步模型結果
我們要將iv中分組的WOE值根據最優分箱的結果,按照劃分的區間大小回填到原始的數據樣本中,將原始數據進行替換,然後做邏輯回歸擬合。
再做一次膨脹因子檢驗,一般大小5或者10的變數是要刪除掉的。
建模是一個多次迭代的過程,我們對模型做了多次迭代,結果如下:
每一次刪除掉一些變數,是他們的回歸係數較小而刪除,因為回歸係數較小,評分卡最後幾乎沒有什麼區分能力,這樣的變數對模型的貢獻度比較低。
我們這裡的數據,bad佔比為6%多,數據屬於不平衡樣本。
關於不平衡樣本的處理,很很多的方法,一般來說,有
- 使用原始數據,但是要調整模型參數的閾值。如果sklearn有個參數是class_weight,可以調整為class_weight =balanced。
- 欠採樣(從好樣本裡面隨機抽取與壞樣本同樣多的數據進行模型擬合,但是拋棄了大數據好樣本的數據,可能會造成較大的偏差)
- 過採樣(將壞樣本反覆抽取並生成與好樣本同樣多的數據進行模型擬合,只是單純的重複了正例,可能會過擬合)
- SMOTE演算法(本質是過採樣,通過在局部區域進行K-近鄰生成了新的壞樣本,可以理解為是一種集成學習,但是也可能會過擬合,但是方法由於單純的過採樣方法)
使用下面幾種方法來計算最終的各項指標的結果。採取模型投票的方法選取最優的一個模型:
- baseline模型,不劃分訓練集和測試集,一般全變數建模,參數使用class_weight =balanced。
- 將數據按照70%和30%隨機劃分訓練集和測試集,使用class_weight =balanced,對訓練集和擬合,做超參數網格搜索演算法求出最佳參數,然後對測試集進行驗證。
- baseline模型,不劃分訓練集和測試集,一般全變數建模,使用SMOTE演算法做過採樣。
- 劃分訓練集和測試集,使用使用SMOTE演算法過採樣。對訓練集和擬合,對測試集進行驗證。
- 調用python的統計包,向後淘汰法和向前選擇法。
對此,我們還有向後淘汰法和向前選擇法。結果如下:
使用了多種方法以後,我們進行模型投票,選取那個最終結果最優的。
我們給出了最終的結果。保留14個變數,KS = 29.7。
對於風險模型 申請評分卡來說,這樣的結果算比較滿意了。
Test set accuracy score: 0.64758 precision recall f1-score support 0.0 0.65 0.64 0.65 186091 1.0 0.65 0.65 0.65 186091avg / total 0.65 0.65 0.65 372182The confusion_matrix is:[[119368 66723][ 64443 121648]]
accuracy_score 0.647575648473precision_score 0.645789426186recall_score 0.653701683585ROC_AUC is 0.702947965916
K-S score 0.295828385037
(七)參數估計結果
每次迭代都保存下面的記錄。模型的回歸係數和截圖。
如果回歸係數的絕對值越大,那麼這個變數的最後的評分區分度就越好。
(八)初步評分卡結果
接下來,通過評分卡公式,我們代入WOE值,截距和係數,就可以算出評分卡了。
(九)初步模型結果
我們將每個樣本的分數都計算出來,然後計算出開發樣本的KS = 29.7% 。
我們會對驗證樣本做之前相同的數據清洗,根據開發樣本得到的評分卡,給出驗證樣本每條數據的總的分數。
值得注意的是我們的驗證樣本沒有參與模型的開發,所以可以放心的進行測試。
評分結果在開發樣本數上分數分布如下:
分數符合正態分布。以及下列的概率密度函數,看得出好壞是有一部分是分離的。
使用上述的評分卡結果去計算出驗證樣本KS = 31.0%,對於申請評分卡來說,說明我們的模型預測能力尚可,區分能力尚可。
在2017.4-2017-06的數據是驗證樣本,這部分人很多還款還沒有到12期。到後期全部到期以後可以再驗證查看結果。
(十)模型的穩定性校驗
我們要計算每個變數的PSI指標,校驗數據在驗證集上的表現差異。即使開發樣本上模型表現非常好,但是最終驗證集上結果很差,那也是不可行的。
其中有變數PSI值沒有大於25%的。我採取的是直接使用這些變數。
(十一)最終模型結果
最終的模型結果就是初步模型結果。
因此,開發樣本,KS=29.7%, GINI = 36.8% 。
驗證樣本 KS = 31.0% , GINI = 42.4%。
對於申請評分卡來說,說明我們的模型區分能力尚可。
(十二)關於評分模型的討論
信用評分的核心功能是對客群按風險水平進行排序,也即對客群按風險水平進行「差異化」。
在風險管理領域,我們不可能只用一張評分卡就來篩選客戶,我們還會配置很多的規則和策略,評分卡只是其中的一關。
一般來說評分卡功能如下:
- 降低存量規則授信人群的逾期率
- 在不提高逾期率的前提下,提高通過率
如果模型可以上線,綜合考慮風險(壞賬率)和收益(審批通過率)之間的平衡,即在能接受的壞賬率水平和審批通過率之間綜合考慮。 那麼我們可能會做風險收益分析,來權衡評分卡的分數的制定規則。
比如說,分數超過了該機構的風險偏好,如<267分的,來申請貸款的人會被拒絕。如果認為拒絕的太多,再使用其他規則去這部分拒絕的客戶裡面尋找較好的人群。
如高分段的申請在配置審批通過/拒絕策略時一般設置為「自動批准」或「建議批准」,建議授信的額度也較高;低分段的申請則配置為「自動拒絕」或「建議拒絕」,同時授信的額度也較低。
上線以後,還要長期監控穩定性,逾期率情況等。
參考資料
[1]:《SAS開發經典案例解析》(楊馳然)
[2]:《大數據時代的商業建模》(范若愚)
[3]:《Python數據分析與挖掘實戰》(張良均)
[4] :知乎用戶(京東白條)
由於本人水平有限,文章不可避免有錯誤,還請大佬們指摘。
喜歡的話點個贊哈哈哈。未經授權嚴禁轉載。
推薦閱讀:
※推薦系統中的矩陣分解技術
※為什麼有些公司在機器學習業務方面傾向使用R+Hadoop方案?
※Kaggle Titanic Data Analysis(Top 1.6%)經驗分享
※手把手教你使用ggplot2繪製折線圖