數(shù)據(jù)源:融360-用戶貸款風(fēng)險預(yù)測
參考資料:http://www.itdecent.cn/p/aba5685c580a
流程如下:
項目目標(biāo)
數(shù)據(jù)解讀
數(shù)據(jù)預(yù)處理
-
特征工程
1.基于業(yè)務(wù)理解篩選
2.基于機器學(xué)習(xí)篩選
模型建立
一、項目目標(biāo)
通過舉辦方提供的用戶基本信息,消費行為,還款情況等,建立準(zhǔn)確的逾期預(yù)測模型,以預(yù)測用戶是否會逾期還款。
二、數(shù)據(jù)解讀
相關(guān)專業(yè)名詞可以去舉辦方融360官網(wǎng)上搜索https://www.rong360.com/ask/
1.數(shù)據(jù)概述:
用戶的基本屬性user_info.txt
銀行流水記錄bank_detail.txt
用戶瀏覽行為browse_history.txt
信用卡賬單記錄bill_detail.txt
放款時間loan_time.txt
逾期行為的記錄overdue.txt
(注意:并非每一位用戶都有非常完整的記錄,如有些用戶并沒有信用卡賬單記錄,有些用戶卻沒有銀行流水記錄。同時需要注意的是數(shù)據(jù)做過脫敏處理:(a) 隱藏了用戶的id信息;(b) 將用戶屬性信息全部數(shù)字化;(c) 將時間戳和所有金額的值都做了函數(shù)變換。)
2.數(shù)據(jù)詳細(xì)描述:
(1)用戶的基本屬性user_info.txt。共6個字段,其中字段性別為0表示性別未知。 用戶id,性別,職業(yè),教育程度,婚姻狀態(tài),戶口類型 6346,1,2,4,4,2
(2)銀行流水記錄bank_detail.txt。共5個字段,其中,第2個字段,時間戳為0表示時間未知;第3個字段,交易類型有兩個值,1表示支出、0表示收入;第5個字段,工資收入標(biāo)記為1時,表示工資收入。 用戶id,時間戳,交易類型,交易金額,工資收入標(biāo)記 6951,5894316387,0,13.756664,0
(3)用戶瀏覽行為browse_history.txt。共4個字段。其中,第2個字段,時間戳為0表示時間未知。 用戶id,時間戳,瀏覽行為數(shù)據(jù),瀏覽子行為編號 34724,5926003545,172,1
(4)信用卡賬單記錄bill_detail.txt。共15個字段,其中,第2個字段,時間戳為0表示時間未知。為方便瀏覽,字段以表格的形式給出。 文件示例如下: 用戶id,賬單時間戳,銀行id,上期賬單金額,上期還款金額,信用卡額度,本期賬單余額,本期賬單最低還款額,消費筆數(shù),本期賬單金額,調(diào)整金額,循環(huán)利息,可用金額,預(yù)借現(xiàn)金額度,還款狀態(tài) 3147,5906744363,6,18.626118,18.661937,20.664418,18.905766,17.847133,1,0.000000,0.000000,0.000000,0.000000,19.971271,0 上期賬單金額: 上月需要向信用卡還款的金額 上期還款金額:上月用戶已還款的金額 信用卡額度:信用額度(即信用卡最高可以透支使用的限額)+ 存入信用卡的金額。 本期賬單余額:指截止到出賬單日,本期賬單還未還的金額。 本期賬單最低還款額:最低還款額=信用額度內(nèi)消費款的10%+預(yù)借現(xiàn)金交易款的100%+前期最低還款額未還部分的100%+超過信用額度消費款的100%+費用和利息的100% 消費筆數(shù):用戶在賬單期內(nèi)的消費記錄總數(shù) 本期賬單金額:本期需要向信用卡還款的金額 調(diào)整金額:有可能是原先多還的款項, 在下期還款時會去掉這部分的金額 循環(huán)利息:當(dāng)您償還的金額等于或高于當(dāng)期帳單的最低還款額,但低于本期應(yīng)還金額時,剩余的延后還款的金額銀行會計算相應(yīng)的利息。 可用余額:信用額度-未還清的已出賬金額-已使用未入賬的累積金額 預(yù)借現(xiàn)金額度:是指持卡人使用信用卡通過ATM等自助終端提取現(xiàn)金的最高額度 還款狀態(tài):0--未還款,1--已還款
(5)放款時間信息loan_time.txt。共2個字段,用戶id和放款時間。 用戶id,放款時間 1,5914855887 銀行發(fā)放貸款的時間
(6)顧客是否發(fā)生逾期行為的記錄overdue.txt。共2個字段。樣本標(biāo)簽為1,表示逾期30天以上;樣本標(biāo)簽為0,表示逾期10天以內(nèi)。注意:逾期10天~30天之內(nèi)的用戶,并不在此問題考慮的范圍內(nèi)。用于測試的用戶,只提供id列表,文件名為testUsers.csv。 用戶id,樣本標(biāo)簽 1,1 此處可理解為1是逾期、0是未逾期
號:923414804 群里有志同道合的小伙伴,
互幫互助。群里有視頻學(xué)習(xí)教程和PDF,一起學(xué)習(xí),共同進(jìn)步!
加群免費獲取數(shù)十套PDF資料,助力python學(xué)習(xí)
三、數(shù)據(jù)預(yù)處理
導(dǎo)入數(shù)據(jù),檢查缺失,統(tǒng)計用戶id量、
看下缺失值情況:
沒有缺失,再看用戶id情況:
有9294個用戶有銀行流水記錄
同理導(dǎo)入其他表格信息:
看下,個表格的用戶id數(shù):
各個表格的id數(shù)不一樣,說明并非每個用戶都有銀行卡流水,賬單等信息;所以下一步就是整合6張表格,然后根據(jù)共有用戶id匹配完整信息,沒有完整信息的id去掉。
說明:我們要預(yù)測的測試集是沒有放款的客戶,因此訓(xùn)練集分析使用客戶放款之前的信息,將有時間截的表與放款表交叉,只篩選放款前的客戶id
通過上面各個表的信息情況,可以看到除了放款時間表外有時間字段的是銀行流水記錄表、賬單記錄表、瀏覽信息表。
time_x表示流水記錄時間,time_y表示放款時間:
去重客戶數(shù):
9271個客戶
同理其他表格也做此處理:
得出5735個用戶的記錄是完整的。然后通過這5735個id篩選每張表的記錄,并進(jìn)行字段預(yù)處理。
數(shù)據(jù)預(yù)處理:
(1)銀行流水表:整理收入,支出,工資:
把收入支出工資分別與用戶表交叉(只篩選5735個完整用戶的信息):
把收入支出工資整合到一個表里(注意此處是左鏈接how='left'):
(2)瀏覽表:先剔除5735以外的數(shù)據(jù),再統(tǒng)計每個用戶的瀏覽記錄(count)
統(tǒng)計瀏覽條數(shù)(count):
(3)賬單表:
去掉了時間、銀行id、還款狀態(tài)這幾個變量,按用戶id分組后對每個字段均值化處理
(4)逾期表,用戶表:
直接匹配5735個最終用戶即可
把前面整理好的表整合:
查看最后表信息:
一共有25個字段
四、特征工程
(1)基于業(yè)務(wù)理解篩選
主要看相關(guān)性
對特征做一下分類:
首先看一下用戶的基本屬性:
基于我自己的理解,跟逾期行為相關(guān)的因素有:性別,年齡,教育程度,婚姻情況,收入,用戶是否有放貸,車貸等其他經(jīng)濟(jì)情況。案例數(shù)據(jù)里我們可以先篩選出:性別,教育程度,婚姻狀況。
(職業(yè),戶口類型這兩個特征雖然能反映用戶的收入情況,但不確定性很大,所以不考慮篩選進(jìn)來,我們可以從用戶在銀行的收入/支出記錄來側(cè)面反映用戶的經(jīng)濟(jì)情況)
所以第一步篩選的特征為:
sex ,education,marriage,income_num,income_amount,expen_num,expen_amount,wages_num,wages_amount。
再看下這些特征的相關(guān)性:
銀行流水:
可見收入、支出、工資三個指標(biāo)的金額跟筆數(shù)是線性關(guān)系,那么后續(xù)將構(gòu)建一個新的特征:筆均=金額/筆數(shù),取工資筆均;而且收入、支出是強相關(guān)(0.82),所以只取一個即可,支出筆均。
賬單:
從圖中可以看出倆倆之間相關(guān)性程度比較高的特征有:
上期賬單金額last_bill_amount,上期還款金額last_repay_amount
,本期賬單金額cur_bill_amount, 額度credit_amount這四者之間,另外還有本期余額cur_bill_bal 與 本期最低還款cur_bill_minrepay。
說明:
本期應(yīng)還金額 = 上期賬單金額-上期還款金額 + 本期賬單金額 - 本期調(diào)整金額 + 循環(huán)利息
這個公式的意思是:
上期還款金額 <上期賬單的最低還款額(一般是賬單金額*10%)或者不還,就視為逾期,而且本期的還款要加上循環(huán)利息和上期未還款的那部分。
上期最低還款額<上期還款金額<上期賬單金額,不視為逾期,但本期的還款要加上循環(huán)利息
上期還款金額 >上期賬單金額,也就是說用戶還多了,那么本期的還款會減去一個調(diào)整金額(多還的那部分),循環(huán)利息不計。
所以上期的賬單金額與還款金額是密切相關(guān)的,相關(guān)系數(shù)也高,可以引入一個新的特征:
上期還款差額 =上期賬單金額 - 上期還款金額, 上期還款差額還會直接影響用戶的信用額度以及本期的賬單金額。
本期的賬單余額與最低還款額具有高度共線性,決定只選用最低還款額。
另外調(diào)整金額和循環(huán)利息是跟“上期的還款差額”有關(guān)的:
還款差額>0,需要計算循環(huán)利息,調(diào)整金額不計
還款差額<0,需要計算調(diào)整金額,循環(huán)利息不計
所以可以將還款差額進(jìn)行“特征二值化”來代替這兩個特征。
可用余額=銀行核定的信用卡額度-尚未交還的賬單上欠款-未入賬但已發(fā)生的交易金額-其他相關(guān)利息、費用。所以可用余額與信用卡額度,上期還款差額,循環(huán)利息等都有一定的關(guān)系,但讓我感到奇怪的是,相關(guān)系數(shù)圖上并沒有表現(xiàn)出來,所以暫時不篩選進(jìn)這個特征。
預(yù)借現(xiàn)金額度,是指持卡人使用信用卡通過ATM等自助終端提取現(xiàn)金的最高額度,取現(xiàn)額度包含于信用額度之內(nèi),一般是信用額度的50%左右,所以可以不用這個特征,選擇信用額度即可。
講了這么多,現(xiàn)在可以篩選出我們要的信用卡記錄特征了:
last_repay_diff(上期還款差額),credit_amount,cur_bill_minrepay,cur_bill_amount,cons_num這5個特征。
最后我們把基于業(yè)務(wù)理解的特征選擇結(jié)果展示出來:
重新構(gòu)建數(shù)據(jù)表:
(2)基于機器學(xué)習(xí)篩選
根據(jù)業(yè)務(wù)理解篩選的特征數(shù)仍有11個,故還需要用機器學(xué)習(xí)的方法對特征作進(jìn)一步降維。
在篩選前我們將last_repay_diff這個特征作“二值化”(0,1)處理:
11個特征中sex,education,marriage,last_diff_label為分類變量,其它為連續(xù)型數(shù)值變量。
我們采取兩種特征選擇方法:filter法和Wrapper法
--Filter法篩選:
篩選的結(jié)果為:'sex' ,'education', 'expen_avg', 'wages_avg', 'cur_bill_minrepay'
--Wrapper法:
篩選結(jié)果為:'expen_avg', 'credit_amount', 'cur_bill_amount', 'cur_bill_minrepay', 'brows_beh'
發(fā)現(xiàn)倆種方法篩選的結(jié)果相差很大,wrapper法沒有選擇任何一個分類型變量,而filter法選擇了“sex”和“education”兩個分類型變量,相同點是都選擇了expen_avg和cur_bill_minrepay。
用交叉檢驗的方法看一下哪種方法好吧:
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" contenteditable="true" cid="n149" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">from sklearn.model_selection import cross_val_score
x_test1 = df_select[['sex','education','expen_avg','wages_avg','cur_bill_minrepay']]
x_test1 = np.array(x_test1)
y_test1 = df_select[['overdue']]
y_test1 = np.array(y_test1)
m1 = DecisionTreeClassifier()
m1.fit(x_test1,y_test1)
scores = -cross_val_score(m1, x_test1, y_test1, cv=5, scoring= 'neg_mean_absolute_error')
print(np.mean(scores))</pre>
filter法結(jié)果為:0.24080262097612498
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" contenteditable="true" cid="n151" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">from sklearn.model_selection import cross_val_score
x_test2 = df_select[['expen_avg','credit_amount','cur_bill_amount','cur_bill_minrepay','brows_beh']]
x_test2 = np.array(x_test2)
y_test2 = df_select[['overdue']]
y_test2 = np.array(y_test2)
m2 = DecisionTreeClassifier()
m2.fit(x_test2,y_test2)
scores = -cross_val_score(m2, x_test2, y_test2, cv=5, scoring= 'neg_mean_absolute_error')
print(np.mean(scores))</pre>
Wrapper法結(jié)果為:0.2537052432049981
交叉檢驗的結(jié)果表明還是用Filter法好一點(越小越好),接下來就是建模了,既然篩選的5個特征既有分類型,又有連續(xù)型,用決策樹。
五、數(shù)據(jù)建模
1.拆分訓(xùn)練集和測試集數(shù)據(jù):比例為4:1
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" contenteditable="true" cid="n158" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state = 0)</pre>
\2. 設(shè)定決策樹參數(shù),進(jìn)行建模
這里采用的是決策樹里的CART法,之所以不用ID3和C4.5,是因為這兩個方法不能處理連續(xù)型數(shù)據(jù),必須轉(zhuǎn)換成離散型。
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" contenteditable="true" cid="n161" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">from sklearn import tree
clf = tree.DecisionTreeClassifier(criterion='gini', #劃分屬性的選擇標(biāo)準(zhǔn),基尼系數(shù),entropy信息增益
splitter='best', #在節(jié)點中選擇的分類策略(best 最好的)
max_depth=3, #樹的最大深度
min_samples_split=10, #區(qū)分一個內(nèi)部節(jié)點所需要的最少樣本數(shù)
min_samples_leaf=5 #葉子節(jié)點所需要的最小樣本數(shù)
)
clf = clf.fit(x_train,y_train)
?
y_pred = clf.predict(x_test)
y_pred = y_pred[:, np.newaxis]
y_pred</pre>
3.模型結(jié)果評價
看一下模型結(jié)果分析報告:
精確率0.82,召回率0.86,f1_score為0.81,模型的綜合評價還是可以的。
接下來可以對模型進(jìn)行調(diào)參,在這里我主要對樹的深度進(jìn)行了不同設(shè)置,發(fā)現(xiàn)當(dāng)樹的深度為3時,模型的結(jié)果是比較好的。
另外決策樹最大的問題是會容易出現(xiàn)”過擬合”,我在網(wǎng)上看到的解決方法主要有倆種:
1.對樹進(jìn)行剪枝,包括預(yù)剪枝和后剪枝
2.用隨機森林的方法,進(jìn)行隨機選取樣本和隨機選取屬性