標籤:

基於Python的信用評分卡模型分析

信用風險計量體系包括主體評級模型和債項評級兩部分。主體評級和債項評級均有一系列評級模型組成,其中主體評級模型可用「四張卡」來表示,分別是A卡、B卡、C卡和F卡;債項評級模型通常按照主體的融資用途,分為企業融資模型、現金流融資模型和項目融資模型等。

我們主要討論主體評級模型的開發過程。

一、項目流程

典型的信用評分模型如圖1-1所示。信用風險評級模型的主要開發流程如下:

1.數據獲取,包括獲取存量客戶及潛在客戶的數據。存量客戶是指已經在證券公司開展相關融資類業務的客戶,包括個人客戶和機構客戶;潛在客戶是指未來擬在證券公司開展相關融資類業務的客戶,主要包括機構客戶,這也是解決證券業樣本較少的常用方法,這些潛在機構客戶包括上市公司、公開發行債券的發債主體、新三板上市公司、區域股權交易中心掛牌公司、非標融資機構等。

2.數據預處理,主要工作包括數據清洗、缺失值處理、異常值處理,主要是為了將獲取的原始數據轉化為可用作模型開發的格式化數據。

3.探索性數據分析,該步驟主要是獲取樣本總體的大概情況,描述樣本總體情況的指標主要有直方圖、箱形圖等。

4.變數選擇,該步驟主要是通過統計學的方法,篩選出對違約狀態影響最顯著的指標。主要有單變數特徵選擇方法和基於機器學習模型的方法。

5.模型開發,該步驟主要包括變數分段、變數的WOE(證據權重)變換和邏輯回歸估算三部分。

6.模型評估,該步驟主要是評估模型的區分能力、預測能力、穩定性,並形成模型評估報告,得出模型是否可以使用的結論。

7.信用評分,根據邏輯回歸的係數和WOE等確定信用評分的方法。將Logistic模型轉換為標準評分的形式。

8.建立評分系統,根據信用評分方法,建立自動信用評分系統。

圖1-1 信用評分模型開發流程

二、數據獲取

數據來自Kaggle的Give Me Some Credit,有15萬條的樣本數據,下圖可以看到這份數據的大致情況。

數據屬於個人消費類貸款,只考慮信用評分最終實施時能夠使用到的數據應從如下一些方面獲取數據:

– 基本屬性:包括了借款人當時的年齡。

– 償債能力:包括了借款人的月收入、負債比率。

– 信用往來:兩年內35-59天逾期次數、兩年內60-89天逾期次數、兩年內90

天或高於90天逾期的次數。

– 財產狀況:包括了開放式信貸和貸款數量、不動產貸款或額度數量。

– 貸款屬性:暫無。

– 其他因素:包括了借款人的家屬數量(不包括本人在內)。

– 時間窗口:自變數的觀察窗口為過去兩年,因變數表現窗口為未來兩年。

圖2-1 原始數據的變數

三、數據預處理

在對數據處理之前,需要對數據的缺失值和異常值情況進行了解。Python內有describe()函數,可以了解數據集的缺失值、均值和中位數等。

#載入數據data = pd.read_csv(cs-training.csv)#數據集確實和分布情況data.describe().to_csv(DataDescribe.csv)

數據集的詳細情況:

圖3-1 變數詳細情況

從上圖可知,變數MonthlyIncome和NumberOfDependents存在缺失,變數MonthlyIncome共有缺失值29731個,NumberOfDependents有3924個缺失值。

3.1 缺失值處理

這種情況在現實問題中非常普遍,這會導致一些不能處理缺失值的分析方法無法應用,因此,在信用風險評級模型開發的第一步我們就要進行缺失值處理。缺失值處理的方法,包括如下幾種。

1.直接刪除含有缺失值的樣本。

2.根據樣本之間的相似性填補缺失值。

3.根據變數之間的相關關係填補缺失值。

變數MonthlyIncome缺失率比較大,所以我們根據變數之間的相關關係填補缺失值,我們採用隨機森林法:

# 用隨機森林對缺失值預測填充函數def set_missing(df): # 把已有的數值型特徵取出來 process_df = df.ix[:,[5,0,1,2,3,4,6,7,8,9]] # 分成已知該特徵和未知該特徵兩部分 known = process_df[process_df.MonthlyIncome.notnull()].as_matrix() unknown = process_df[process_df.MonthlyIncome.isnull()].as_matrix() # X為特徵屬性值 X = known[:, 1:] # y為結果標籤值 y = known[:, 0] # fit到RandomForestRegressor之中 rfr = RandomForestRegressor(random_state=0, n_estimators=200,max_depth=3,n_jobs=-1) rfr.fit(X,y) # 用得到的模型進行未知特徵值預測 predicted = rfr.predict(unknown[:, 1:]).round(0) print(predicted) # 用得到的預測結果填補原缺失數據 df.loc[(df.MonthlyIncome.isnull()), MonthlyIncome] = predicted return df

NumberOfDependents變數缺失值比較少,直接刪除,對總體模型不會造成太大影響。對缺失值處理完之後,刪除重複項。

data=set_missing(data)#用隨機森林填補比較多的缺失值data=data.dropna()#刪除比較少的缺失值data = data.drop_duplicates()#刪除重複項data.to_csv(MissingData.csv,index=False)

3.2 異常值處理

缺失值處理完畢後,我們還需要進行異常值處理。異常值是指明顯偏離大多數抽樣數據的數值,比如個人客戶的年齡為0時,通常認為該值為異常值。找出樣本總體中的異常值,通常採用離群值檢測的方法。

首先,我們發現變數age中存在0,顯然是異常值,直接剔除:

# 年齡等於0的異常值進行剔除data = data[data[age] > 0]

對於變數NumberOfTime30-59DaysPastDueNotWorse、NumberOfTimes90DaysLate、NumberOfTime60-89DaysPastDueNotWorse這三個變數,由下面的箱線圖圖3-2可以看出,均存在異常值,且由unique函數可以得知均存在96、98兩個異常值,因此予以剔除。同時會發現剔除其中一個變數的96、98值,其他變數的96、98兩個值也會相應被剔除。

圖3-2 箱形圖

剔除變數NumberOfTime30-59DaysPastDueNotWorse、NumberOfTimes90DaysLate、NumberOfTime60-89DaysPastDueNotWorse的異常值。另外,數據集中好客戶為0,違約客戶為1,考慮到正常的理解,能正常履約並支付利息的客戶為1,所以我們將其取反。

#剔除異常值data = data[data[NumberOfTime30-59DaysPastDueNotWorse] < 90]#變數SeriousDlqin2yrs取反data[SeriousDlqin2yrs]=1-data[SeriousDlqin2yrs]

3.3 數據切分

為了驗證模型的擬合效果,我們需要對數據集進行切分,分成訓練集和測試集。

from sklearn.cross_validation import train_test_splitY = data[SeriousDlqin2yrs] X = data.ix[:, 1:] #測試集佔比30% X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3, random_state=0) # print(Y_train) train = pd.concat([Y_train, X_train], axis=1) test = pd.concat([Y_test, X_test], axis=1) clasTest = test.groupby(SeriousDlqin2yrs)[SeriousDlqin2yrs].count() train.to_csv(TrainData.csv,index=False) test.to_csv(TestData.csv,index=False)

四、探索性分析

在建立模型之前,我們一般會對現有的數據進行 探索性數據分析(Exploratory Data Analysis) 。 EDA是指對已有的數據(特別是調查或觀察得來的原始數據)在盡量少的先驗假定下進行探索。常用的探索性數據分析方法有:直方圖、散點圖和箱線圖等。

客戶年齡分布如圖4-1所示,可以看到年齡變數大致呈正態分布,符合統計分析的假設。

圖4-1 客戶年齡分布

客戶年收入分布如圖4-2所示,月收入也大致呈正態分布,符合統計分析的需要。

客戶年收入分布如圖4-2所示,月收入也大致呈正態分布,符合統計分析的需要。

五、變數選擇

特徵變數選擇(排序)對於數據分析、機器學習從業者來說非常重要。好的特徵選擇能夠提升模型的性能,更能幫助我們理解數據的特點、底層結構,這對進一步改善模型、演算法都有著重要作用。至於Python的變數選擇代碼實現可以參考結合Scikit-learn介紹幾種常用的特徵選擇方法。

在本文中,我們採用信用評分模型的變數選擇方法,通過WOE分析方法,即是通過比較指標分箱和對應分箱的違約概率來確定指標是否符合經濟意義。首先我們對變數進行離散化(分箱)處理。

5.1 分箱處理

變數分箱(binning)是對連續變數離散化(discretization)的一種稱呼。信用評分卡開發中一般有常用的等距分段、等深分段、最優分段。其中等距分段(Equval length intervals)是指分段的區間是一致的,比如年齡以十年作為一個分段;等深分段(Equal frequency intervals)是先確定分段數量,然後令每個分段中數據數量大致相等;最優分段(Optimal Binning)又叫監督離散化(supervised discretizaion),使用遞歸劃分(Recursive Partitioning)將連續變數分為分段,背後是一種基於條件推斷查找較佳分組的演算法。

我們首先選擇對連續變數進行最優分段,在連續變數的分布不滿足最優分段的要求時,再考慮對連續變數進行等距分段。最優分箱的代碼如下:

# 定義自動分箱函數def mono_bin(Y, X, n = 20): r = 0 good=Y.sum() bad=Y.count() - good while np.abs(r) < 1: d1 = pd.DataFrame({"X": X, "Y": Y, "Bucket": pd.qcut(X, n)}) d2 = d1.groupby(Bucket, as_index = True) r, p = stats.spearmanr(d2.mean().X, d2.mean().Y) n = n - 1 d3 = pd.DataFrame(d2.X.min(), columns = [min]) d3[min]=d2.min().X d3[max] = d2.max().X d3[sum] = d2.sum().Y d3[total] = d2.count().Y d3[rate] = d2.mean().Y d3[woe]=np.log((d3[rate]/(1-d3[rate]))/(good/bad)) d4 = (d3.sort_index(by = min)).reset_index(drop=True) print("=" * 60) print(d4) return d4

針對我們將使用最優分段對於數據集中的RevolvingUtilizationOfUnsecuredLines、age、DebtRatio和MonthlyIncome進行分類。

圖5-1 RevolvingUtilizationOfUnsecuredLines分箱情況

圖5-2 age分箱情況

圖5-3 DebtRatio分箱情況

圖5-4 MonthlyIncome分箱情況

針對不能最優分箱的變數,分箱如下:

# 連續變數離散化cutx3 = [ninf, 0, 1, 3, 5, pinf]cutx6 = [ninf, 1, 2, 3, 5, pinf]cutx7 = [ninf, 0, 1, 3, 5, pinf]cutx8 = [ninf, 0,1,2, 3, pinf]cutx9 = [ninf, 0, 1, 3, pinf]cutx10 = [ninf, 0, 1, 2, 3, 5, pinf]

5.2 WOE

WoE分析, 是對指標分箱、計算各個檔位的WoE值並觀察WoE值隨指標變化的趨勢。其中WoE的數學定義是:

woe=ln(goodattribute/badattribute)

在進行分析時,我們需要對各指標從小到大排列,並計算出相應分檔的WoE值。其中正向指標越大,WoE值越小;反向指標越大,WoE值越大。正向指標的WoE值負斜率越大,反響指標的正斜率越大,則說明指標區分能力好。WoE值趨近於直線,則意味指標判斷能力較弱。若正向指標和WoE正相關趨勢、反向指標同WoE出現負相關趨勢,則說明此指標不符合經濟意義,則應當予以去除。

woe函數實現在上一節的mono_bin()函數裡面已經包含,這裡不再重複。

5.3 相關性分析和IV篩選

接下來,我們會用經過清洗後的數據看一下變數間的相關性。注意,這裡的相關性分析只是初步的檢查,進一步檢查模型的VI(證據權重)作為變數篩選的依據。

相關性圖我們通過Python裡面的seaborn包,調用heatmap()繪圖函數進行繪製,實現代碼如下:

corr = data.corr()#計算各變數的相關性係數xticks = [x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10]#x軸標籤yticks = list(corr.index)#y軸標籤fig = plt.figure()ax1 = fig.add_subplot(1, 1, 1)sns.heatmap(corr, annot=True, cmap=rainbow, ax=ax1, annot_kws={size: 9, weight: bold, color: blue})#繪製相關性係數熱力圖ax1.set_xticklabels(xticks, rotation=0, fontsize=10)ax1.set_yticklabels(yticks, rotation=0, fontsize=10)plt.show()

圖5-5 數據集各變數的相關性

由上圖可以看出,各變數之間的相關性是非常小的。NumberOfOpenCreditLinesAndLoans和NumberRealEstateLoansOrLines的相關性係數為0.43。

接下來,我進一步計算每個變數的Infomation Value(IV)。IV指標是一般用來確定自變數的預測能力。 其公式為:

IV=sum((goodattribute-badattribute)*ln(goodattribute/badattribute))

通過IV值判斷變數預測能力的標準是:

< 0.02: unpredictive

0.02 to 0.1: weak

0.1 to 0.3: medium

0.3 to 0.5: strong

> 0.5: suspicious

IV的實現放在mono_bin()函數裡面,代碼實現如下:

# 定義自動分箱函數def mono_bin(Y, X, n = 20): r = 0 good=Y.sum() bad=Y.count()-good while np.abs(r) < 1: d1 = pd.DataFrame({"X": X, "Y": Y, "Bucket": pd.qcut(X, n)}) d2 = d1.groupby(Bucket, as_index = True) r, p = stats.spearmanr(d2.mean().X, d2.mean().Y) n = n - 1 d3 = pd.DataFrame(d2.X.min(), columns = [min]) d3[min]=d2.min().X d3[max] = d2.max().X d3[sum] = d2.sum().Y d3[total] = d2.count().Y d3[rate] = d2.mean().Y d3[woe]=np.log((d3[rate]/(1-d3[rate]))/(good/bad)) d3[goodattribute]=d3[sum]/good d3[badattribute]=(d3[total]-d3[sum])/bad iv=((d3[goodattribute]-d3[badattribute])*d3[woe]).sum() d4 = (d3.sort_index(by = min)).reset_index(drop=True) print("=" * 60) print(d4) cut=[] cut.append(float(-inf)) for i in range(1,n+1): qua=X.quantile(i/(n+1)) cut.append(round(qua,4)) cut.append(float(inf)) woe=list(d4[woe].round(3)) return d4,iv,cut,woe

生成的IV圖代碼:

ivlist=[ivx1,ivx2,ivx3,ivx4,ivx5,ivx6,ivx7,ivx8,ivx9,ivx10]#各變數IVindex=[x1,x2,x3,x4,x5,x6,x7,x8,x9,x10]#x軸的標籤fig1 = plt.figure(1)ax1 = fig1.add_subplot(1, 1, 1)x = np.arange(len(index))+1ax1.bar(x, ivlist, width=0.4)#生成柱狀圖ax1.set_xticks(x)ax1.set_xticklabels(index, rotation=0, fontsize=12)ax1.set_ylabel(IV(Information Value), fontsize=14)#在柱狀圖上添加數字標籤for a, b in zip(x, ivlist): plt.text(a, b + 0.01, %.4f % b, ha=center, va=bottom, fontsize=10)plt.show()

輸出圖像:

圖5-6 輸出的各變數IV圖

可以看出,DebtRatio、MonthlyIncome、NumberOfOpenCreditLinesAndLoans、NumberRealEstateLoansOrLines和NumberOfDependents變數的IV值明顯較低,所以予以刪除。

六、模型分析

證據權重(Weight of Evidence,WOE)轉換可以將Logistic回歸模型轉變為標準評分卡格式。引入WOE轉換的目的並不是為了提高模型質量,只是一些變數不應該被納入模型,這或者是因為它們不能增加模型值,或者是因為與其模型相關係數有關的誤差較大,其實建立標準信用評分卡也可以不採用WOE轉換。這種情況下,Logistic回歸模型需要處理更大數量的自變數。儘管這樣會增加建模程序的複雜性,但最終得到的評分卡都是一樣的。

在建立模型之前,我們需要將篩選後的變數轉換為WoE值,便於信用評分。

6.1 WOE轉換

我們已經能獲取了每個變數的分箱數據和woe數據,只需要根據各變數數據進行替換,實現代碼如下:

#替換成woe函數def replace_woe(series, cut, woe): list = [] I = 0 while i<len(series): value=series[i] j=len(cut) - 2 m=len(cut) - 2 while j >= 0: if value>=cut[j]: j = -1 else: j -= 1 m -= 1 list.append(woe[m]) i += 1 return list

我們將每個變數都進行替換,並將其保存到WoeData.csv文件中:

# 替換成woedata[RevolvingUtilizationOfUnsecuredLines] = Series(replace_woe(data[RevolvingUtilizationOfUnsecuredLines], cutx1, woex1))data[age] = Series(replace_woe(data[age], cutx2, woex2))data[NumberOfTime30-59DaysPastDueNotWorse] = Series(replace_woe(data[NumberOfTime30-59DaysPastDueNotWorse], cutx3, woex3))data[DebtRatio] = Series(replace_woe(data[DebtRatio], cutx4, woex4))data[MonthlyIncome] = Series(replace_woe(data[MonthlyIncome], cutx5, woex5))data[NumberOfOpenCreditLinesAndLoans] = Series(replace_woe(data[NumberOfOpenCreditLinesAndLoans], cutx6, woex6))data[NumberOfTimes90DaysLate] = Series(replace_woe(data[NumberOfTimes90DaysLate], cutx7, woex7))data[NumberRealEstateLoansOrLines] = Series(replace_woe(data[NumberRealEstateLoansOrLines], cutx8, woex8))data[NumberOfTime60-89DaysPastDueNotWorse] = Series(replace_woe(data[NumberOfTime60-89DaysPastDueNotWorse], cutx9, woex9))data[NumberOfDependents] = Series(replace_woe(data[NumberOfDependents], cutx10, woex10))data.to_csv(WoeData.csv, index=False)

6.2 Logisic模型建立

我們直接調用statsmodels包來實現邏輯回歸:

導入數據data = pd.read_csv(WoeData.csv)#應變數Y=data[SeriousDlqin2yrs]#自變數,剔除對因變數影響不明顯的變數X=data.drop([SeriousDlqin2yrs,DebtRatio,MonthlyIncome, NumberOfOpenCreditLinesAndLoans,NumberRealEstateLoansOrLines,NumberOfDependents],axis=1)X1=sm.add_constant(X)logit=sm.Logit(Y,X1)result=logit.fit()print(result.summary())

輸出結果:

圖6-1 邏輯回歸模型結果

通過圖6-1可知,邏輯回歸各變數都已通過顯著性檢驗,滿足要求。

6.3 模型檢驗

到這裡,我們的建模部分基本結束了。我們需要驗證一下模型的預測能力如何。我們使用在建模開始階段預留的test數據進行檢驗。通過ROC曲線和AUC來評估模型的擬合能力。

在Python中,可以利用sklearn.metrics,它能方便比較兩個分類器,自動計算ROC和AUC。

實現代碼:

#應變數Y_test = test[SeriousDlqin2yrs]#自變數,剔除對因變數影響不明顯的變數,與模型變數對應X_test = test.drop([SeriousDlqin2yrs, DebtRatio, MonthlyIncome, NumberOfOpenCreditLinesAndLoans,NumberRealEstateLoansOrLines, NumberOfDependents], axis=1)X3 = sm.add_constant(X_test)resu = result.predict(X3)#進行預測fpr, tpr, threshold = roc_curve(Y_test, resu)rocauc = auc(fpr, tpr)#計算AUCplt.plot(fpr, tpr, b, label=AUC = %0.2f % rocauc)#生成ROC曲線plt.legend(loc=lower right)plt.plot([0, 1], [0, 1], r--)plt.xlim([0, 1])plt.ylim([0, 1])plt.ylabel(真正率)plt.xlabel(假正率)plt.show()

輸出結果:

圖6-2 ROC曲線

從上圖可知,AUC值為0.85,說明該模型的預測效果還是不錯的,正確率較高。

七、信用評分

我們已經基本完成了建模相關的工作,並用ROC曲線驗證了模型的預測能力。接下來的步驟,就是將Logistic模型轉換為標準評分卡的形式。

7.1 評分標準

依據以上論文資料得到:

a=log(p_good/P_bad)

Score = offset + factor * log(odds)

在建立標準評分卡之前,我們需要選取幾個評分卡參數:基礎分值、 PDO(比率翻倍的分值)和好壞比。 這裡, 我們取600分為基礎分值,PDO為20 (每高20分好壞比翻一倍),好壞比取20。

# 我們取600分為基礎分值,PDO為20(每高20分好壞比翻一倍),好壞比取20。z = 20 / math.log(2)q = 600 - 20 * math.log(20) / math.log(2)baseScore = round(q + p * coe[0], 0)

個人總評分=基礎分+各部分得分

7.2 部分評分

下面計算各變數部分的分數。各部分得分函數:

#計算分數函數 def get_score(coe,woe,factor): scores=[] for w in woe: score=round(coe*w*factor,0) scores.append(score) return scores

計算各變數得分情況:

# 各項部分分數x1 = get_score(coe[1], woex1, p)x2 = get_score(coe[2], woex2, p)x3 = get_score(coe[3], woex3, p)x7 = get_score(coe[4], woex7, p)x9 = get_score(coe[5], woex9, p)

我們可以得到各部分的評分卡如圖7-1所示:

圖7-1 各變數的評分標準

八、自動評分系統

根據變數來計算分數,實現如下:

#根據變數計算分數def compute_score(series,cut,score): list = [] i = 0 while i < len(series): value = series[i] j = len(cut) - 2 m = len(cut) - 2 while j >= 0: if value >= cut[j]: j = -1 else: j -= 1 m -= 1 list.append(score[m]) i += 1 return list

我們來計算test裡面的分數:

test1 = pd.read_csv(TestData.csv)test1[BaseScore]=Series(np.zeros(len(test1)))+baseScoretest1[x1] = Series(compute_score(test1[RevolvingUtilizationOfUnsecuredLines], cutx1, x1))test1[x2] = Series(compute_score(test1[age], cutx2, x2))test1[x3] = Series(compute_score(test1[NumberOfTime30-59DaysPastDueNotWorse], cutx3, x3))test1[x7] = Series(compute_score(test1[NumberOfTimes90DaysLate], cutx7, x7)test1[x9] = Series(compute_score(test1[NumberOfTime60-89DaysPastDueNotWorse], cutx9, x9))test1[Score] = test1[x1] + test1[x2] + test1[x3] + test1[x7] +test1[x9] + baseScoretest1.to_csv(ScoreData.csv, index=False)

批量計算的部分分結果:

圖8-1 批量計算的部分結果

九、總結以及展望

本文通過對kaggle上的Give Me Some Credit數據的挖掘分析,結合信用評分卡的建立原理,從數據的預處理、變數選擇、建模分析到創建信用評分,創建了一個簡單的信用評分系統。

基於AI 的機器學習評分卡系統可通過把舊數據(某個時間點後,例如2年)剔除掉後再進行自動建模、模型評估、並不斷優化特徵變數,使得系統更加強大。

參考文獻

基於R語言的信用評分卡建模分析

信用卡評分模型

信用標準評分卡模型開發及實現

手把手教你用R語言建立信用評分模型

Scorecard 評分卡模型

使用python進行數據清洗

Python異常值處理與檢測

結合Scikit-learn介紹幾種常用的特徵選擇方法

推薦閱讀:

猴子第三講實踐筆記—對航班計劃數據的處理
Kindle是否值得包月
解讀《大數據下的數據分析平台架構》
另外一款編輯器 Geany

TAG:數據分析 |