生活随笔
收集整理的這篇文章主要介紹了
逻辑回归评分卡实现和评估
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
評分卡實現和評估
? 上一節講得是模型評估,主要有ROC曲線、KS曲線、學習曲線和混淆矩陣。今天學習如何實現評分卡和對評分卡進行評估。
? 首先,要了解評分卡是如何從概率映射到評分的,這個之前寫過評分卡映射的邏輯。見邏輯回歸卡評分映射邏輯,一定要看,明白概率如何映射到評分的以及每個變量的得分如何計算。附上評分卡映射的代碼。結合邏輯回歸評分卡映射的原理才能看懂代碼。
from sklearn
.linear_model
import LogisticRegression
'''
第六步:邏輯回歸模型。
要求:
1,變量顯著
2,符號為負
'''
y
= trainData
['y']
x
= trainData
[multi_analysis
]lr_model
= LogisticRegression
(C
=0.1)
lr_model
.fit
(x
,y
)
trainData
['prob'] = lr_model
.predict_proba
(x
)[:,1]
def cal_scale(score
,odds
,PDO
,model
):"""odds:設定的壞好比score:在這個odds下的分數PDO: 好壞翻倍比model:邏輯回歸模型return :A,B,base_score"""B
= PDO
/np
.log
(2)A
= score
+B
*np
.log
(odds
)print('B: {:.2f}'.format(B
))print('A: {:.2f}'.format(A
))return A
,B
cal_scale
(50,0.05,10,lr_model
)def Prob2Score(prob
, A
,B
):y
= np
.log
(prob
/(1-prob
))return float(A
-B
*y
)trainData
['score'] = trainData
['prob'].map(lambda x
:Prob2Score
(x
, 6.78,14.43))
? 可以看到,評分越高,違約概率越低。網上很多實現評分卡映射的代碼,都沒太看懂,這個是根據邏輯來寫的,有時間再把映射邏輯整理一下。
1. 得分的KS曲線
? 和模型的KS曲線一樣,只不過橫坐標的概率變成了得分。
直接放上代碼。
def plot_score_ks(df
,score_col
,target
):"""df:數據集target:目標變量的字段名score_col:最終得分的字段名"""total_bad
= df
[target
].sum()total_good
= df
[target
].count
()-total_badscore_list
= list(df
[score_col
])target_list
= list(df
[target
])items
= sorted(zip(score_list
,target_list
),key
=lambda x
:x
[0]) step
= (max(score_list
)-min(score_list
))/200 score_bin
=[] good_rate
=[] bad_rate
=[] ks_list
= [] for i
in range(1,201):idx
= min(score_list
)+i
*step score_bin
.append
(idx
) target_bin
= [x
[1] for x
in items
if x
[0]<idx
] bad_num
= sum(target_bin
)good_num
= len(target_bin
)-bad_num goodrate
= good_num
/total_good badrate
= bad_num
/total_badks
= abs(goodrate
-badrate
) good_rate
.append
(goodrate
)bad_rate
.append
(badrate
)ks_list
.append
(ks
)fig
= plt
.figure
(figsize
=(8,6))ax
= fig
.add_subplot
(1,1,1)ax
.plot
(score_bin
,good_rate
,color
='green',label
='good_rate')ax
.plot
(score_bin
,bad_rate
,color
='red',label
='bad_rate')ax
.plot
(score_bin
,ks_list
,color
='blue',label
='good-bad')ax
.set_title
('KS:{:.3f}'.format(max(ks_list
)))ax
.legend
(loc
='best')return plt
.show
(ax
)
2. PR曲線
? 還是這個混淆矩陣的圖,P是查準率、精確率,R是查全率、召回率。這兩個指標時既矛盾又統一的。因為為了提高精確率P,就是要更準確地預測正樣本,但此時往往會過于保守而漏掉很多沒那么有把握的正樣本,導致召回率R降低。
? 同ROC曲線的形成一樣,PR曲線的形成也是不斷移動截斷點形成不同的(R,P)繪制成一條線。
? 當接近原點時,召回率R接近于0,精確率P較高,說明得分前幾位的都是正樣本。隨著召回率的增加,精確率整體下降,當召回率為1時,說明所有的正樣本都被挑了出來,此時的精確率很低,其實就是相當于你將大部分的樣本都預測為正樣本。注意,只用某個點對應的(R,P)無法全面衡量模型的性能,必須要通過PR曲線的整體表現。此外,還有F1 score和ROC曲線也能反映一個排序模型的性能。
- PR曲線和ROC曲線的區別
? 當正負樣本的分布發生變化時,ROC曲線的形狀基本不變,PR曲線形狀會發生劇烈變化。上圖中PR曲線整體較低就是因為正負樣本不均衡導致的。因為比如評分卡中壞客戶只有1%,好客戶有99%,將全部客戶預測為好客戶,那么準確率依然有99%。雖然模型整體的準確率很高,但并不代表對壞客戶的分類準確率也高,這里壞客戶的分類準確率為0,召回率也為0。
def plot_PR(df
,score_col
,target
,plt_size
=None):"""df:得分的數據集score_col:分數的字段名target:目標變量的字段名plt_size:繪圖尺寸return: PR曲線"""total_bad
= df
[target
].sum()score_list
= list(df
[score_col
])target_list
= list(df
[target
])score_unique_list
= sorted(set(list(df
[score_col
])))items
= sorted(zip(score_list
,target_list
),key
=lambda x
:x
[0]) precison_list
= []tpr_list
= []for score
in score_unique_list
:target_bin
= [x
[1] for x
in items
if x
[0]<=score
] bad_num
= sum(target_bin
)total_num
= len(target_bin
)precison
= bad_num
/total_numtpr
= bad_num
/total_badprecison_list
.append
(precison
)tpr_list
.append
(tpr
)plt
.figure
(figsize
=plt_size
)plt
.title
('PR曲線')plt
.xlabel
('查全率')plt
.ylabel
('精確率')plt
.plot
(tpr_list
,precison_list
,color
='tomato',label
='PR曲線')plt
.legend
(loc
='best')return plt
.show
()
3.得分分布圖
? 理想中最好的評分卡模型應該是將好壞客戶完全區分出來,但是實際中好壞用戶的評分會有一定的重疊,我們要做的盡量減小重疊。
? 另外好壞用戶的得分分布最好都是正態分布,如果呈雙峰或多峰分布,那么很有可能是某個變量的得分過高導致,這樣對評分卡的穩定性會有影響。
def plot_score_hist(df
,target
,score_col
,plt_size
=None,cutoff
=None):"""df:數據集target:目標變量的字段名score_col:最終得分的字段名plt_size:圖紙尺寸cutoff :劃分拒絕/通過的點return :好壞用戶的得分分布圖""" plt
.figure
(figsize
=plt_size
)x1
= df
[df
[target
]==1][score_col
]x2
= df
[df
[target
]==0][score_col
]sns
.kdeplot
(x1
,shade
=True,label
='壞用戶',color
='hotpink')sns
.kdeplot
(x2
,shade
=True,label
='好用戶',color
='seagreen')plt
.axvline
(x
=cutoff
)plt
.legend
()return plt
.show
()
4.得分明細表
? 按分數段區分,看不同分數段的好壞樣本情況、違約率等指標。
? 可以看到高分段的違約概率明顯比低分段低,說明評分卡的效果是顯著的。
def score_info(df
,score_col
,target
,x
=None,y
=None,step
=None):"""df:數據集target:目標變量的字段名score_col:最終得分的字段名x:最小區間的左值y:最大區間的右值step:區間的分數間隔return :得分明細表"""df
['score_bin'] = pd
.cut
(df
[score_col
],bins
=np
.arange
(x
,y
,step
),right
=True)total
= df
[target
].count
()bad
= df
[target
].sum()good
= total
- badgroup
= df
.groupby
('score_bin')score_info_df
= pd
.DataFrame
()score_info_df
['用戶數'] = group
[target
].count
()score_info_df
['壞用戶'] = group
[target
].sum()score_info_df
['好用戶'] = score_info_df
['用戶數']-score_info_df
['壞用戶']score_info_df
['違約占比'] = score_info_df
['壞用戶']/score_info_df
['用戶數']score_info_df
['累計用戶'] = score_info_df
['用戶數'].cumsum
()score_info_df
['壞用戶累計'] = score_info_df
['壞用戶'].cumsum
()score_info_df
['好用戶累計'] = score_info_df
['好用戶'].cumsum
()score_info_df
['壞用戶累計占比'] = score_info_df
['壞用戶累計']/bad score_info_df
['好用戶累計占比'] = score_info_df
['好用戶累計']/goodscore_info_df
['累計用戶占比'] = score_info_df
['累計用戶']/total score_info_df
['累計違約占比'] = score_info_df
['壞用戶累計']/score_info_df
['累計用戶']score_info_df
= score_info_df
.reset_index
()return score_info_df
5.提升圖和洛倫茲曲線
? 假設目前有10000個樣本,壞用戶占比為30%,我們做了一個評分卡(分數越低,用戶壞的概率越高),按照評分從低到高劃分成10等份(每個等份用戶數為1000),計算每等份的壞用戶占比,如果評分卡效果很好,那么越靠前的等份里,包含的壞用戶應該越多,越靠后的等份里,包含的壞用戶應該要更少。作為對比,如果不對用戶評分,按照總體壞用戶占比30%來算,每個等份中壞用戶占比也是30%。將這兩種方法的每等份壞用戶占比放在一張柱狀圖上進行對比,就是提升圖。
? 將這兩種方法的累計壞用戶占比放在一張曲線圖上,就是洛倫茲曲線圖。
? 此外,洛倫茲曲線可以比較兩個評分卡的優劣,例如下圖中虛線對應的分數假設是600分,那么在600分這cutoff點下,A和B的拒絕率都是40%,但A可以拒絕掉88%的壞用戶,B只能拒掉78%的壞用戶,說明A評分卡的效果更好。
def plot_lifting(df
,score_col
,target
,bins
=10,plt_size
=None):"""df:數據集,包含最終的得分score_col:最終分數的字段名target:目標變量名bins:分數劃分成的等份數plt_size:繪圖尺寸return:提升圖和洛倫茲曲線"""score_list
= list(df
[score_col
])label_list
= list(df
[target
])items
= sorted(zip(score_list
,label_list
),key
= lambda x
:x
[0])step
= round(df
.shape
[0]/bins
,0)bad
= df
[target
].sum()all_badrate
= float(1/bins
)all_badrate_list
= [all_badrate
]*binsall_badrate_cum
= list(np
.cumsum
(all_badrate_list
))all_badrate_cum
.insert
(0,0)score_bin_list
=[]bad_rate_list
= []for i
in range(0,bins
,1):index_a
= int(i
*step
)index_b
= int((i
+1)*step
)score
= [x
[0] for x
in items
[index_a
:index_b
]]tup1
= (min(score
),)tup2
= (max(score
),)score_bin
= tup1
+tup2score_bin_list
.append
(score_bin
)label_bin
= [x
[1] for x
in items
[index_a
:index_b
]]bin_bad
= sum(label_bin
)bin_bad_rate
= bin_bad
/badbad_rate_list
.append
(bin_bad_rate
)bad_rate_cumsum
= list(np
.cumsum
(bad_rate_list
))bad_rate_cumsum
.insert
(0,0)plt
.figure
(figsize
=plt_size
)x
= score_bin_listy1
= bad_rate_listy2
= all_badrate_listy3
= bad_rate_cumsumy4
= all_badrate_cumplt
.subplot
(1,2,1)plt
.title
('提升圖')plt
.xticks
(np
.arange
(bins
)+0.15,x
,rotation
=90)bar_width
= 0.3plt
.bar
(np
.arange
(bins
),y1
,width
=bar_width
,color
='hotpink',label
='score_card')plt
.bar
(np
.arange
(bins
)+bar_width
,y2
,width
=bar_width
,color
='seagreen',label
='random')plt
.legend
(loc
='best')plt
.subplot
(1,2,2)plt
.title
('洛倫茲曲線圖')plt
.plot
(y3
,color
='hotpink',label
='score_card')plt
.plot
(y4
,color
='seagreen',label
='random')plt
.xticks
(np
.arange
(bins
+1),rotation
=0)plt
.legend
(loc
='best')return plt
.show
()
plot_lifting
(trainData
,'score','y',bins
=10,plt_size
=(10,5))
6.設定cutoff
? cutoff即根據評分劃分通過/拒絕的點,其實就是看不同的閾值下混淆矩陣的情況。設定cutoff時有兩個指標,一個是誤傷率,即FPR,就是好客戶中有多少被預測為壞客戶而拒絕。另一個是拒絕率,就是這樣劃分的情況下有多少客戶被拒絕。
def rule_verify(df
,col_score
,target
,cutoff
):"""df:數據集target:目標變量的字段名col_score:最終得分的字段名 cutoff :劃分拒絕/通過的點return :混淆矩陣"""df
['result'] = df
.apply(lambda x
:30 if x
[col_score
]<=cutoff
else 10,axis
=1)TP
= df
[(df
['result']==30)&(df
[target
]==1)].shape
[0] FN
= df
[(df
['result']==30)&(df
[target
]==0)].shape
[0] bad
= df
[df
[target
]==1].shape
[0] good
= df
[df
[target
]==0].shape
[0] refuse
= df
[df
['result']==30].shape
[0] passed
= df
[df
['result']==10].shape
[0] acc
= round(TP
/refuse
,3) tpr
= round(TP
/bad
,3) fpr
= round(FN
/good
,3) pass_rate
= round(refuse
/df
.shape
[0],3) matrix_df
= pd
.pivot_table
(df
,index
='result',columns
=target
,aggfunc
={col_score
:pd
.Series
.count
},values
=col_score
) print('精確率:{}'.format(acc
))print('查全率:{}'.format(tpr
))print('誤傷率:{}'.format(fpr
))print('規則拒絕率:{}'.format(pass_rate
))return matrix_df
【作者】:Labryant
【原創公眾號】:風控獵人
【簡介】:某創業公司策略分析師,積極上進,努力提升。乾坤未定,你我都是黑馬。
【轉載說明】:轉載請說明出處,謝謝合作!~
總結
以上是生活随笔為你收集整理的逻辑回归评分卡实现和评估的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。