1. 程式人生 > 實用技巧 >風控模型6大核心指標(附程式碼)

風控模型6大核心指標(附程式碼)

目錄

Part 1. 生成樣本
Part 2. 計算AUC、KS、GINI
Part 3. PSI
Part 4. 分數分佈
Part 5. 完整工程程式碼

正文

在我們開發完信用分模型後,經常需要計算如下的一些指標:
●區分度的指標:○AUC○KS○GINI
●穩定性的指標:○PSI
●分數分佈:○總人數比例○壞使用者比例

一、生成樣本

注意資料是構造的,而非真實的資料

import numpy as np
import pandas as pd

n_sample = 1000

#構造虛擬的資料,主要欄位有4個
df_score = pd.DataFrame({
    'user_id': [u for
u in range(n_sample)], 'label':np.random.randint(2, size=n_sample), 'score': 900*np.random.random(size=n_sample), 'term': 20201+np.random.randint(5, size=n_sample) })

統計下分term的總人數,壞人數和壞人比例:

#根據期限去計算好壞使用者佔比
df_score.groupby('term').agg(total=('label', 'count'), 
                             bad
=('label', 'sum'), bad_rate=('label', 'mean'))

所以我們平時需要注意一下groupby之後的agg的用法

二、計算AUC、KS、GINI

這裡對原有sklearn的auc計算做了一點修改,如果AUC<0.5的話會返回1-AUC, 這樣能忽略區分度的方向性。

#KS,GINI,AUC

from sklearn.metrics import roc_auc_score, roc_curve

#auc
def get_auc(ytrue, yprob):
    auc = roc_auc_score(ytrue, yprob)
    
if auc < 0.5: auc = 1 - auc return auc #ks def get_ks(ytrue, yprob): fpr, tpr, thr = roc_curve(ytrue, yprob) ks = max(abs(tpr - fpr)) return ks #gini=2 * auc - 1 (既然acu在80%左右,那麼這個應該是在69%左右) def get_gini(ytrue, yprob): auc = get_auc(ytrue, yprob) gini = 2 * auc - 1 return gini #根據期限去計算KS,GINI,AUC,score可以當做是預測值,label就是真實值,這樣可以直接使用sklearn去計算 df_metrics = pd.DataFrame({ 'auc': df_score.groupby('term').apply(lambda x: get_auc(x['label'], x['score'])), 'ks': df_score.groupby('term').apply(lambda x: get_ks(x['label'], x['score'])), 'gini': df_score.groupby('term').apply(lambda x: get_gini(x['label'], x['score'])) })

最後得到一個包含這些指標的df

這裡需要注意一下groupby.apply的用法

三、PSI模型穩定性

這裡先分成2步:

  • 簡單對隨機生成的信用分按固定分數區間分段;

  • 按照分段計算PSI:使用pivot_table把資料按照term進行排列計算每個term上的人數比例

#PSI,也就是穩定性,可以認定為訓練集和測試集的分佈差異不大

df_score['score_bin'] = pd.cut(df_score['score'], [0, 500, 700, 800, 900])

df_total = pd.pivot_table(df_score, 
                          values='user_id', 
                          index='score_bin', 
                          columns=['term'], 
                          aggfunc="count", 
                          margins=True)
df_ratio = df_total.div(df_total.iloc[-1, :], axis=1)

透視表之後的結果如下:

div裡面的df是上面透視表最後一行,也就是說所有的資料對應的列分別除以最後一行對應的資料,最終結果如下:

根據人數比例計算PSI再放回表格內

eps = np.finfo(np.float32).eps
lst_psi = list()
for idx in range(1, len(df_ratio.columns)-1):
    last, cur = df_ratio.iloc[0, -1: idx-1]+eps, df_ratio.iloc[0, -1: idx]+eps
    psi = sum((cur-last) * np.log(cur / last))
    lst_psi.append(psi)
df_ratio.append(pd.Series([np.nan]+lst_psi+[np.nan], 
                          index=df_ratio.columns, 
                          name='psi'))

我們可以看出這個資料是這樣計算出來的: