機器學習模型偏差與拒絕推斷的Python實現


幸存者偏差

風險分析的本質是使用部分樣本分布估計總體分布。在風險建模的過程中,普遍存在着幸存者偏差(Survivorship Bias)。其含義為,使用局部樣本代替總體樣本時,局部樣本無法充分表征總體樣本的分布信息,從而得到錯誤的總體估計

在風控架構體系中,多次涉及樣本被拒絕或客戶流失等問題。由於風險分析得到的結果認為部分樣本的預估表現較差,因此該部分樣本無法獲取有效的貸后信息,即無法參與未來的模型訓練。缺少該部分低分人群的信息,對全局樣本表示模型的影響非常大。因為當模型經過多次迭代后,其重要特征可能被逐漸弱化,甚至呈現出與原模型完全相反的負樣本分布趨勢。因此需要使用相應手段進行處理。

拒絕推斷(Reject Inference)

是一種對拒絕用戶進行推理歸納,從而得到該部分樣本標簽分布的方法。常見的拒絕推斷方法分為三種:數據驗證、標簽分裂、數據推斷。本章分別對這三種方法進行介紹。

一、數據驗證

數據驗證,又稱為下探,即從拒絕樣本中選取部分樣本進行放款。以獲得該部分樣本的真實標簽,從而帶入評分卡模型進行監督學習。數據驗證是最有效且實施起來非常簡單的一種拒絕推斷方法。通常為獲取較為豐富的拒絕樣本標簽,將當前模型打分低於通過閾值的客群,按照預測分值排序后,等頻划分為10箱。然后從中分別抽取部分拒絕樣本進行放款實驗。

數據驗證的缺點也非常明顯。首先,數據驗證本身是一個需要一定周期才能得到結果的方法。為將驗證樣本用於后續模型建模中,數據驗證方案需要提前整個貸款周期,再加上逾期觀察周期,預先進行實施。其次,拒絕樣本中的負樣本占比明顯高於通過樣本,因此該驗證方法會對平台造成一定程度上的收益損失。獲取該部分樣本的信息,對未來模型的表現有極大幫助。因此需要在短期收益與長期風險控制中選擇一個平衡點。

二、標簽 (y) 分裂

通常評分卡模型的標簽定義方式較為統一,如歷史最大逾期天數等。標簽分裂(Label Split)方法期望將標簽定義方法,拆分為多個和原始標簽定義方法強相關的子方法。常見的標簽分類方法包括如下兩類。

同生表現(Cohort Performance):利用當前產品的拒絕原因、平台其他產品線的貸后表現,或其余機構的標注信息定義拒絕樣本的偽標簽。如將征信數據標記為黑的樣本定義為負樣本,或將信審人員審批結果作為真實負標簽使用,又或者利用其歷史表現判斷該用戶是否為負樣本。其缺點有二:第一,不同平台對於標簽的定義有差異,因此外部數據在很多情況下無法直接用於拒絕樣本的偽標簽定義;第二,平台其余產品的貸后表現較容易獲得,而外部數據獲取的成本較高,並且需要考慮數據的泄露問題及數據交互的合規性。

多規則交叉(Multiple Rule Cross):由於規則制定通常使用IV較高的變量,其對負樣本的挑選精准度較高。然而利用單規則閾值對樣本進行的標記與模型低分直接標記無本質區別,因此通常使用多條關鍵規則進行交叉組合。將同時命中多條規則的用戶標記為負樣本。通常規則拒絕樣本對模型訓練的幫助較小,原因是該類樣本在前置規則中被拒絕,均無法參與后續模型評分。對於申請評分卡模型來說,規則拒絕樣本可以從整體樣本空間中剔除。然而規則也非一成不變,如果不使用拒絕推斷方法進行標注,在后續的規則迭代中同樣存在偏差問題。

標簽分裂的用途較廣,本質上是一種基於業務思想的方法。在實際使用中,限制條件也較多。相較之下,基於數據的推斷方法普適性更強,下面就來介紹幾種基於數據的拒絕推斷方法。

三、數據推斷

事實上,從業者常說的拒絕推斷(Inference methods),通常是指通過數據分析方法修正模型的參數估計偏差。拒絕推斷的主要意義在於,希望修正建模樣本和實際全量樣本之間的差異,本質上是為了降低模型參數估計的偏差

拒絕推斷場景下有如下3個概念。

已知好壞標簽(Know Good Bad,KGB)樣本:准入模型允許通過的樣本集,已知標簽。由KGB樣本訓練的模型又叫KGB模型

未知標簽(Inferred Good Bad,IGB)拒絕樣本:准入模型拒絕的樣本集,未知標簽。由於IGB樣本沒有標簽,通常不會用於訓練模型。在部分方法中可能會生成偽標簽,從而參與建模過程。

全量(All Good Bad,AGB)樣本:包含KGB和IGB兩部分的全量樣本集。由該部分數據訓練得到的模型又稱AGB模型。請牢記這三個概念,接下來的內容中會反復提到它們。下面來看一下常用的數據推斷方法。

a.硬截斷法

一種常見的思路是,直接使用KGB模型在拒絕樣本上做預測,並將低分樣本(如分數最低的20%樣本)認為是負樣本,帶入模型進行估計,其余拒絕樣本全部視為灰色樣本,不予考慮。這種推斷方法就叫作硬截斷法(Hard Cutoff)。硬截斷法假設“逾期”與“放款”之間相互獨立。其示意圖如圖所示。

利用KGB模型進行打分,按照逾期概率降序排列,選擇截斷點( cut-off)進行截斷后,僅將截斷點以下的藍色部分作為負樣本帶入模型進行學習,從而修正模型的偏差。

接下來通過一個申請評分卡的例子,看看如何在Python中實現基於數據技巧的拒絕推斷。首先加載相關庫和數據。

# 加載相關庫  
from sklearn.linear_model import LogisticRegression  
from sklearn.metrics import roc_curve  
import pandas as pd  
import numpy as np  
import random  
import math  
import warnings  
warnings.filterwarnings("ignore")  
# 讀取數據  
data = pd.read_csv('Acard_reject.txt')  
data.sample(5)  

 

 根據KGB數據訓練KGB模型。

# 有真實標簽  ,0和1是真實有標簽的,-1是沒有標簽的,可以認為是使用有標簽數據做模型,然后在使用無標簽做驗證,
kgb = data[data['bad_ind']!=-1].copy()  
# 無標簽拒絕樣本  
reject = data[data['bad_ind']==-1].copy()  
# 所有樣本  
agb = data.copy()   
# 指定變量名,使用LR模型進行擬合  
feature_lst = ['person_info', 'finance_info', 
                  'credit_info', 'act_info']  
x = kgb[feature_lst]  
y = kgb['bad_ind']  
lr_model = LogisticRegression(C=0.1)  
lr_model.fit(x, y)

b.模糊展開法

與硬截斷法類似的還有模糊展開法(Fuzzy Augmentation),后者同樣假設“逾期”與“放款”之間相互獨立。模糊展開法將每條拒絕樣本復制為不同類別、不同權重的兩條。假設當前有一個拒絕樣本,KGB模型預測其為負樣本的概率為0.8,為正樣本的概率為0.2,則分別生成兩條新樣本。第一個樣本標簽為負('bad_ind’=1),權重為0.8;第二個樣本標簽為正(’bad_ind’=0),權重為0.2。將兩條樣本分別帶入AGB模型進行訓練。其概念如圖所示。

 

 

 在Python中簡單實現模糊展開法

# 復制樣本  
reject1 = reject.copy()  
reject2 = reject.copy()  
# 按照正負樣本概率加權  
reject1['weight'] = lr_model.predict_proba(reject1[feature_lst])[:,1]  
reject2['weight'] = lr_model.predict_proba(reject2[feature_lst])[:,0]  
# 合並  一個用戶有兩條數據
labeled = pd.concat([reject1, reject2], ignore_index=True)  
# 為KGB樣本設置權重  
kgb['weight'] = 1  
# 合后並重新建模  
final = pd.concat([labeled, kgb], ignore_index=True)  
x = final[feature_lst]  
y = final['bad_ind']  
lr_fuzz = LogisticRegression(C=0.1)  
lr_fuzz.fit(x, y, sample_weight=final['weight']) 

c.重新加權法

模糊展開法通過權重調整,修正模型的偏差,其效果與AGB模型的識別能力相關性較高。使用權重進行調整的拒絕推斷方法還有重新加權法(Reweighting)。與前面的兩種方法不同,重新加權法不使用拒絕樣本進行學習而僅利用其樣本分布特點,調整原KGB數據集分布權重。在重新加權法中,首先使用KGB模型獲得AGB樣本的逾期概率,並將逾期概率升序排列;接着等頻分箱為10等份,分別計算每一個分箱中的負樣本占比然后將負樣本占比乘以當前箱中的權重修正項,獲取AGB樣本中負樣本占比的邊際期望;最后將權重帶入建模過程,得到新的KGB模型作為最終模型。其權重修正公式為:

其中,Reject_j表示當前分組中拒絕樣本的個數,Accept_i表示當前樣本中接受樣本的個數。Accept_i可以表示為當前分箱中,已知正樣本個數(Good_i)和已知負樣本個數(Bad_i)的和。在Python中簡單實現重新加權法。

# 負樣本概率,越大越可能是負樣本  
agb['y_pred'] = lr_model.predict_proba(agb[feature_lst])[:,1]  # 等頻分箱  
agb['range'] = pd.qcut(agb['y_pred'], 10)  # 分組計算權重  
final_1 = pd.DataFrame()  
for i in list(set(agb['range'])):  
    tt = agb[agb['range']==i].copy()  
    good = sum(tt['bad_ind']==1)  
    bad = sum(tt['bad_ind']==0)  
    re = sum(tt['bad_ind']==-1)  
    # 權重計算  
    tt['weight'] = (good+bad+re)/(good+bad)  
    final_1 = final.append(tt)  
# 帶入權重,重新擬合模型  
x = final[feature_lst]  
y = final['bad_ind']  
lr_weighted = LogisticRegression(C=0.1)  
lr_weighted.fit(x, y, sample_weight=final_1['weight'])

d.外推法

除了基於數據分析修正模型偏差外,還可以根據經驗風險因子調控,引入人工修正。外推法(Extrapolation)根據KGB模型在拒絕樣本上的預測結果,通過人工指定經驗風險因子,獲取不同分組上的負樣本占比然后按照正負樣本的比例,為無標簽拒絕樣本隨機賦值為0或1。紅寶書中將經驗風險因子公式定義為:

經驗風險因子表示拒絕推斷壞好比與放貸已知壞好比的倍數。數值越大,代表拒絕樣本越“壞”,通常取值在2~4之間。這里使用動態的IK值進行權重調整。假設AGB樣本使用AGB模型預測獲得逾期概率,按照逾期概率升序排列后等頻划分為10箱,則每一箱樣本中的負樣本占比應逐箱遞增,因此定義其經驗風險逐箱遞增。從第1箱初始IK為2,逐步遞增至第10箱IK為4。每一箱之間增加的數值相等,恆等於0.2。

在Python中簡單實現外推法。

# 負樣本概率,越大越可能是負樣本  
kgb['y_pred'] = lr_model.predict_proba(kgb[feature_lst])[:,1]  
# 等頻分箱  
kgb['range'] = pd.qcut(kgb['y_pred'], 10)  
# 在AGB有標記樣本上計算等頻分箱閾值和負樣本占比  
pmax = kgb['y_pred'].max()  
cutpoints = list(set(kgb['y_pred'].quantile(
                    [0.1 * n for n in range(1, 10)]))) + [pmax + 1]
cutpoints.sort(reverse=False)  
dct = {}  
for i in range(len(cutpoints) - 1):  
    # 分箱  
    data = kgb.loc[np.logical_and(kgb['y_pred'] >= cutpoints[i],  
                                  kgb['y_pred'] < cutpoints[i + 1]), 
                                         ['bad_ind']] 
    good = sum(data['bad_ind']==0)  
    bad = sum(data['bad_ind']==1)  
    # 通過遞增的步長,使得經驗風險因子從2增長至4  
    step = (i + 1) * 0.2  
    dct[i] = bad / (bad + good) * 2 * step  
# 拒絕樣本按照閾值進行划分  
reject['y_pred'] = lr_model.predict_proba(reject[feature_lst])[:,1]  
rejectNew = pd.DataFrame()  
for i in range(len(cutpoints) - 1):  
    # 分箱  
    data = reject.loc[np.logical_and(reject['y_pred']>=cutpoints[i],  
                      reject['y_pred']<cutpoints[i+1])]  
    data['badrate'] = dct[i]  
    rejectNew.append(data)  
    if rejectNew is None:  
        rejectNew= data  
    else:  
        rejectNew = rejectNew.append(data)  
# 定義隨機打分函數  
def assign(x):  
    tt = random.uniform(0, 1)  
    if tt < x:  
        return 1  
    else:  
        return 0  
# 按照加權負樣本占比隨機賦值  
rejectNew['bad_ind'] = rejectNew['badrate'].map(lambda x:assign(x))  
# 合后並重新建模  
final = pd.concat([rejectNew,kgb], ignore_index=True)  
x = final[feature_lst]  
y = final['bad_ind']  
lr_Extra = LogisticRegression(C=0.1)  
lr_Extra.fit(x,y) 

外推法的思路簡單,實現邏輯也不復雜。然而其缺點是隨機賦值有較大的偶然性,因此可以在每一個分箱內引入硬截斷法,即不按照正負樣本比例進行隨機賦值,而是按照AGB模型的預測概率排序后選擇百分比閾值進行截斷。感興趣的讀者可以自行嘗試。

e.迭代再分類法

前幾種方法普遍存在一個問題:無法有效保證修正偏差后的模型仍是有效的。迭代再分類法(Iterative Reclassification)是一種通過多次迭代,保證模型結果有效且收斂的拒絕推斷方法。其基本思想是,先使用硬截斷法為拒絕樣本的標簽賦值;隨后將具有“偽標簽”的樣本加入原KGB模型進行學習,得到部分標簽失真的AGB模型;接着使用AGB模型獲取拒絕樣本的逾期概率;之后再次使用硬截斷法,不斷重復上述過程,直至某個指標收斂。迭代再分類法的思路是啟發式的,可以使用任何指標作為判斷模型是否收斂的依據。

在Python中簡單實現迭代再分類法。

maxKS = 0  
n = 0  
x = kgb[feature_lst]  
y = kgb['bad_ind']  
lr_hard = LogisticRegression(C=0.1)  
lr_hard.fit(x,y)  
reject['y_pred'] = lr_hard.predict_proba(reject[feature_lst])[:,1]  
while True:  
    # 負樣本概率,越大越可能是負樣本  
    reject['y_pred'] = lr_hard.predict_proba(reject[feature_lst])[:,1]  
    # 0.8分位點  
    thd = reject['y_pred'].quantile(0.4)  
    # 閾值以上硬截斷為負樣本  
    reject['bad_ind'] = reject['y_pred'].map(
                             lambda x :1 if x >= thd else -1)
    # 只保留負樣本  
    labeled = reject[reject['bad_ind']==1]  
    labeled = labeled.drop('y_pred', axis=1)  
    # 合后並重新建模  
    final = pd.concat([labeled,kgb], ignore_index=True)  
    x = final[feature_lst]  
    y = final['bad_ind']  
    lr_hard = LogisticRegression(C=0.1)  
    lr_hard.fit(x,y)  
    y_pred = lr_hard.predict_proba(kgb[feature_lst])[:,1]  
    fpr_lr_train,tpr_lr_train,_ = roc_curve(kgb['bad_ind'],y_pred)  
    ks = abs(fpr_lr_train - tpr_lr_train).max()  
    if maxKS <= ks:  
        maxKS = ks  
        n+=1  
        print('迭代第%s輪,ks值為%s' % (n,ks))  
    else:  
        break

其運行結果為:

迭代第1輪,ks值為0.3553602599594899

迭代第2輪,ks值為0.3638000806187246迭代

第3輪,ks值為0.36671654105103924

迭代第4輪,ks值為0.3670731426379163

文章轉自:https://zhuanlan.zhihu.com/p/162724703

https://zhuanlan.zhihu.com/p/88624987

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM