日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

逻辑回归评分卡实现和评估

發(fā)布時間:2025/3/21 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 逻辑回归评分卡实现和评估 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

評分卡實現(xiàn)和評估

? 上一節(jié)講得是模型評估,主要有ROC曲線、KS曲線、學(xué)習曲線和混淆矩陣。今天學(xué)習如何實現(xiàn)評分卡和對評分卡進行評估。

? 首先,要了解評分卡是如何從概率映射到評分的,這個之前寫過評分卡映射的邏輯。見邏輯回歸卡評分映射邏輯,一定要看,明白概率如何映射到評分的以及每個變量的得分如何計算。附上評分卡映射的代碼。結(jié)合邏輯回歸評分卡映射的原理才能看懂代碼。

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:設(shè)定的壞好比score:在這個odds下的分數(shù)PDO: 好壞翻倍比model:邏輯回歸模型return :A,B,base_score"""B = PDO/np.log(2)A = score+B*np.log(odds)# base_score = A+B*model.intercept_[0]print('B: {:.2f}'.format(B))print('A: {:.2f}'.format(A))# print('基礎(chǔ)分為:{:.2f}'.format(base_score))return A,B#假設(shè)基礎(chǔ)分為50,odds為5%,PDO為10,可以自行調(diào)整。這一步是為了計算出A和B。 cal_scale(50,0.05,10,lr_model)def Prob2Score(prob, A,B):#將概率轉(zhuǎn)化成分數(shù)且為正整數(shù)y = np.log(prob/(1-prob))return float(A-B*y)trainData['score'] = trainData['prob'].map(lambda x:Prob2Score(x, 6.78,14.43))

? 可以看到,評分越高,違約概率越低。網(wǎng)上很多實現(xiàn)評分卡映射的代碼,都沒太看懂,這個是根據(jù)邏輯來寫的,有時間再把映射邏輯整理一下。

1. 得分的KS曲線

? 和模型的KS曲線一樣,只不過橫坐標的概率變成了得分。
直接放上代碼。

# 得分的KS def plot_score_ks(df,score_col,target):"""df:數(shù)據(jù)集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是查全率、召回率。這兩個指標時既矛盾又統(tǒng)一的。因為為了提高精確率P,就是要更準確地預(yù)測正樣本,但此時往往會過于保守而漏掉很多沒那么有把握的正樣本,導(dǎo)致召回率R降低。
? 同ROC曲線的形成一樣,PR曲線的形成也是不斷移動截斷點形成不同的(R,P)繪制成一條線。

? 當接近原點時,召回率R接近于0,精確率P較高,說明得分前幾位的都是正樣本。隨著召回率的增加,精確率整體下降,當召回率為1時,說明所有的正樣本都被挑了出來,此時的精確率很低,其實就是相當于你將大部分的樣本都預(yù)測為正樣本。注意,只用某個點對應(yīng)的(R,P)無法全面衡量模型的性能,必須要通過PR曲線的整體表現(xiàn)。此外,還有F1 score和ROC曲線也能反映一個排序模型的性能。

  • PR曲線和ROC曲線的區(qū)別
    ? 當正負樣本的分布發(fā)生變化時,ROC曲線的形狀基本不變,PR曲線形狀會發(fā)生劇烈變化。上圖中PR曲線整體較低就是因為正負樣本不均衡導(dǎo)致的。因為比如評分卡中壞客戶只有1%,好客戶有99%,將全部客戶預(yù)測為好客戶,那么準確率依然有99%。雖然模型整體的準確率很高,但并不代表對壞客戶的分類準確率也高,這里壞客戶的分類準確率為0,召回率也為0。
# PR曲線 def plot_PR(df,score_col,target,plt_size=None):"""df:得分的數(shù)據(jù)集score_col:分數(shù)的字段名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.得分分布圖

? 理想中最好的評分卡模型應(yīng)該是將好壞客戶完全區(qū)分出來,但是實際中好壞用戶的評分會有一定的重疊,我們要做的盡量減小重疊。
? 另外好壞用戶的得分分布最好都是正態(tài)分布,如果呈雙峰或多峰分布,那么很有可能是某個變量的得分過高導(dǎo)致,這樣對評分卡的穩(wěn)定性會有影響。

# 得分分布圖 def plot_score_hist(df,target,score_col,plt_size=None,cutoff=None):"""df:數(shù)據(jù)集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.得分明細表

? 按分數(shù)段區(qū)分,看不同分數(shù)段的好壞樣本情況、違約率等指標。

? 可以看到高分段的違約概率明顯比低分段低,說明評分卡的效果是顯著的。

# 得分明細表 def score_info(df,score_col,target,x=None,y=None,step=None):"""df:數(shù)據(jù)集target:目標變量的字段名score_col:最終得分的字段名x:最小區(qū)間的左值y:最大區(qū)間的右值step:區(qū)間的分數(shù)間隔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['用戶數(shù)'] = group[target].count()score_info_df['壞用戶'] = group[target].sum()score_info_df['好用戶'] = score_info_df['用戶數(shù)']-score_info_df['壞用戶']score_info_df['違約占比'] = score_info_df['壞用戶']/score_info_df['用戶數(shù)']score_info_df['累計用戶'] = score_info_df['用戶數(shù)'].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.提升圖和洛倫茲曲線

? 假設(shè)目前有10000個樣本,壞用戶占比為30%,我們做了一個評分卡(分數(shù)越低,用戶壞的概率越高),按照評分從低到高劃分成10等份(每個等份用戶數(shù)為1000),計算每等份的壞用戶占比,如果評分卡效果很好,那么越靠前的等份里,包含的壞用戶應(yīng)該越多,越靠后的等份里,包含的壞用戶應(yīng)該要更少。作為對比,如果不對用戶評分,按照總體壞用戶占比30%來算,每個等份中壞用戶占比也是30%。將這兩種方法的每等份壞用戶占比放在一張柱狀圖上進行對比,就是提升圖。

? 將這兩種方法的累計壞用戶占比放在一張曲線圖上,就是洛倫茲曲線圖。

? 此外,洛倫茲曲線可以比較兩個評分卡的優(yōu)劣,例如下圖中虛線對應(yīng)的分數(shù)假設(shè)是600分,那么在600分這cutoff點下,A和B的拒絕率都是40%,但A可以拒絕掉88%的壞用戶,B只能拒掉78%的壞用戶,說明A評分卡的效果更好。

# 繪制提升圖和洛倫茲曲線 def plot_lifting(df,score_col,target,bins=10,plt_size=None):"""df:數(shù)據(jù)集,包含最終的得分score_col:最終分數(shù)的字段名target:目標變量名bins:分數(shù)劃分成的等份數(shù)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.設(shè)定cutoff

? cutoff即根據(jù)評分劃分通過/拒絕的點,其實就是看不同的閾值下混淆矩陣的情況。設(shè)定cutoff時有兩個指標,一個是誤傷率,即FPR,就是好客戶中有多少被預(yù)測為壞客戶而拒絕。另一個是拒絕率,就是這樣劃分的情況下有多少客戶被拒絕。

# 設(shè)定cutoff點,衡量有效性 def rule_verify(df,col_score,target,cutoff):"""df:數(shù)據(jù)集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('規(guī)則拒絕率:{}'.format(pass_rate))return matrix_df

【作者】:Labryant
【原創(chuàng)公眾號】:風控獵人
【簡介】:某創(chuàng)業(yè)公司策略分析師,積極上進,努力提升。乾坤未定,你我都是黑馬。
【轉(zhuǎn)載說明】:轉(zhuǎn)載請說明出處,謝謝合作!~

總結(jié)

以上是生活随笔為你收集整理的逻辑回归评分卡实现和评估的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。