机器学习kaggle案例:风控评分卡模型(Give_Me_Some_Credit)
⼀、简介
kaggle上经典的风控模型:通过预测未来两年内某⼈将⾯临财务困境的可能性,提⾼信⽤评分的现有⽔平
1.1 ⽐赛描述
银⾏在市场经济中起着⾄关重要的作⽤。他们决定谁可以获得融资以及在什么条件下获得投资决策。要使市场和社会发挥作⽤,个⼈和公司需要获得信贷。
信⽤评分算法,⽤于猜测违约概率,是银⾏⽤来确定是否应该授予贷款的⽅法。
该竞赛要求参与者通过预测某⼈在未来两年内遇到财务困境的可能性来改进信⽤评分的现有技术⽔平。
本次竞赛的⽬标是建⽴⼀个借款⼈可以⽤来帮助做出最佳财务决策的模型。 250,000名借款⼈提供历史数据,奖⾦池为5,000美元(第⼀名为3,000美元,第⼆名为1,500美元,第三名为500美元)。
1.2 ⽐赛评估
使⽤AUC(Area Under
ROC Curve)作为性能性能评估标准。意思是ROC曲线下的⾯积
ROC全称是受试者⼯作特征。横坐标是假正例率(Fal Positive
Rate),纵坐标是真正例率(True Positive
Rate)
1.3 数据描述
数据字典(取⾃Data Dictionary.xls⽂件):
在这⾥讲⼀下,kaggle每个竞赛都会提供
数据字典(可能是在介绍中也可能是单独提供⼀个数据字典⽂件,就像这个案例⼀样)
⼀个训练集
⼀个测试集(不含⽬标值)
⼀个提交⽂件的⽰例(本案例就是sampleEntry.csv⽂件)
Variable Name Description Type 1SeriousDlqin2yrs(⽬标值)是否有超过90天或更长时间逾期未还的不良⾏为Y/N(0为好1为坏)2RevolvingUtilizationOfUncuredLines可⽤额度⽐值percentage 3age年龄integer 4NumberOfTime30-59DaysPastDueNotWor逾期30-59天笔数integer 5DebtRatio还款率(每⽉偿还债务,赡养费,⽣活费⽤).percentage 6MonthlyIncome⽉收⼊real 7NumberOfOpenCreditLinesAndLoans信贷数量integer 8NumberOfTimes90DaysLate逾期90天笔数integer 9NumberRealEstateLoansOrLines固定资产贷款量integer 10NumberOfTime60-89DaysPastDueNotWor逾期60-89天笔数integer 11NumberOfDependents家属数量integer
1.4 ⾏业知识补充
信⽤评分模型的书:
风控模型:先说分类,本⽂评分卡模型属于A卡申请者评级模型
信⽤风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有⼀系列评级模型组成,其中主体评级模型可⽤“四张卡”来表⽰,分别是A卡、B卡、C卡和F 卡;债项评级模型通常按照主体的融资⽤途,分为企业融资模型、现⾦流融资模型和项⽬融资模型等。
A卡,⼜称为申请者评级模型,个⼈⽤户申请相应的⾦融产品,对⽤户进⾏筛选分类,区分好坏⽤户,据此决定是否通过申请。在获客过程中⽤到的信⽤风险模型。从模型的⾓度来看,它会对⽤户未来⼀定周期内的逾期风险作预测,即模型会在⽤户授权的情况下收集⽤户多维度的信息,以此来预测逾期概率。预测的逾期概率被⽤于风控策略或者转换成信⽤评分,⽐如国外经典的FICO评分,国内的蚂蚁信⽤评分、京东⼩⽩评分、携程⾦融的程信分等。A卡评分除了⽤于决定是否通过⽤户的信⽤申请,还⽤于风险定价,⽐如额度、利率等
B卡,⼜称为⾏为评级模型,在⽤户使⽤⾦融产品的过程中,根据⽤户的历史使⽤的⾏为数据对⽤户进⾏评级,对可能出现的逾期、延期等⾏为进⾏预测。⽤户拿到信⽤额度后,模型根据⽤户的贷中⾏为数据,进⾏风险⽔平的预测。本质上讲,这个模型是⼀个事件驱动的模型,在互联⽹⾦融领域,⼀般会⽐A卡的预测时间窗⼝要短,对⽤户的⾏为更为敏感。B卡除了可以⽤于⾼风险⽤户的拦截,也可以作为额度、利率调整的重要参考因素。
C卡,⼜称为催收评级模型,对业务中的存量客户是否需要催收进⾏预测。例如当⽤户出现逾期时,机构应该先催谁,或者哪些⽤户不⽤催,就⾃动会把钱还回来。催收模型⼀定程度节约催收成本,提⾼回催率。
F卡,⼜称为欺诈评级模型,对业务中的新⽤户可能存在的欺诈⾏为进⾏预测。根据⽤户提交的材料进⾏⾝份核实,确保⽤户不存在欺诈⾏为;
风控建模所使⽤的客户数据主要分为:(1)信贷数据,(2)资质数据,(3)消费数据,(4)⾏为数据。
香港回归的意义
风控中常⽤的模型
就先补充这么多吧。跟细致的以后再写。
⼆、代码
2.1 获取数据
2.1.1 下载数据
所有数据都下载到了你当前⽂件夹下的datats⽂件下,每个案例涉及到的数据全部下载到了以该案例命名的⽂件夹下。
我所有的kaggle案例的博客,下载数据均会使⽤这个函数,只需要修改前两个常量即可。
> 注:此函数只⽤于下载数据,函数在该代码框内就运⾏了。不再⽤到其它代码中,包括常量,也不会⽤在其他地⽅。
import os
import zipfile
ves import urllib
FILE_NAME = "GiveMeSomeCredit.zip" #⽂件名
DATA_PATH ="datats/GiveMeSomeCredit" #存储⽂件的⽂件夹,取跟⽂件相同(相近)的名字便于区分
DATA_URL = "/824024445/KaggleCas/blob/master/datats/" + FILE_NAME + "?raw=true"
def fetch_data(data_url=DATA_URL, data_path=DATA_PATH, file_name=FILE_NAME):
if not os.path.isdir(data_path): #查看当前⽂件夹下是否存在"datats/GiveMeSomeCredit",没有的话创建
os.makedirs(data_path)
zip_path = os.path.join(data_path, file_name) #下载到本地的⽂件的路径及名称
# urlretrieve()⽅法直接将远程数据下载到本地
data_zip = zipfile.ZipFile(zip_path)
actall(path=data_path) #什么参数都不输⼊就是默认解压到当前⽂件,为了保持统⼀,是泰坦尼克的数据就全部存到GiveMeSomeCredit⽂件夹下
data_zip.clo()
fetch_data()
2.1.2 读取数据
import pandas as pd
import numpy as np
train_df = pd.read_csv("datats/GiveMeSomeCredit/cs-training.csv")
test_df = pd.read_csv("datats/GiveMeSomeCredit/cs-test.csv")
刻骨铭心的意思combine=[train_df, test_df]
train_df.head()
Unnamed:
0SeriousDlqin2yrs RevolvingUtilizationOfUncuredLines age
NumberOfTime30-
59DaysPastDueNotWor
DebtRatio MonthlyIncome NumberOfOpenCreditLinesAndLoans
0110.7661274520.8029829120.013 1200.9571514000.1218762600.04 2300.6581803810.0851133042.02 3400.2338103000.0360503300.05 4500.9072394910.024********.07 2.2 观察数据
2.2.1 info()
train_df.info()
<class 'frame.DataFrame'>
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 12 columns):
Unnamed: 0 150000 non-null int64
SeriousDlqin2yrs 150000 non-null int64
RevolvingUtilizationOfUncuredLines 150000 non-null float64
age 150000 non-null int64
NumberOfTime30-59DaysPastDueNotWor 150000 non-null int64
DebtRatio 150000 non-null float64
MonthlyIncome 120269 non-null float64
NumberOfOpenCreditLinesAndLoans 150000 non-null int64
怎样预防乳腺癌
NumberOfTimes90DaysLate 150000 non-null int64
NumberRealEstateLoansOrLines 150000 non-null int64
合作养殖NumberOfTime60-89DaysPastDueNotWor 150000 non-null int64
NumberOfDependents 146076 non-null float64
dtypes: float64(4), int64(8)
memory usage: 13.7 MB
观察到:
"MonthlyIncome"和"NumberOfDependents"有空值。等会数据清洗把空值处理了。
test_df.info()
<class 'frame.DataFrame'>
RangeIndex: 101503 entries, 0 to 101502
Data columns (total 12 columns):
Unnamed: 0 101503 non-null int64
SeriousDlqin2yrs 0 non-null float64
RevolvingUtilizationOfUncuredLines 101503 non-null float64
age 101503 non-null int64
NumberOfTime30-59DaysPastDueNotWor 101503 non-null int64
DebtRatio 101503 non-null float64
MonthlyIncome 81400 non-null float64
NumberOfOpenCreditLinesAndLoans 101503 non-null int64
NumberOfTimes90DaysLate 101503 non-null int64
NumberRealEstateLoansOrLines 101503 non-null int64
NumberOfTime60-89DaysPastDueNotWor 101503 non-null int64
NumberOfDependents 98877 non-null float64
dtypes: float64(5), int64(7)
memory usage: 9.3 MB
2.2.2 decribe()
#decribe查看数值型数据的信息.没有⾮数值型的数据,所以不使⽤describe(include=['O'])查看⾮数值型数据了。
train_df.describe()
Unnamed: 0SeriousDlqin2yrs RevolvingUtilizationOfUncuredLines age
NumberOfTime30-
59DaysPastDueNotWor
DebtRatio MonthlyIncome NumberOfOpenC
司马头陀count150000.000000150000.000000150000.000000150000.000000150000.000000150000.000000 1.202690e+05 mean75000.5000000.066840 6.04843852.2952070.421033353.005076 6.670221e+03 std43301.4145270.249746249.75537114.771866 4.1927812037.818523 1.438467e+04 min 1.0000000.0000000.0000000.0000000.0000000.0000000.000000e+00 25%37500.7500000.0000000.02986741.0000000.0000000.175074 3.400000e+03 50%75000.5000000.0000000.15418152.0000000.0000000.366508 5.400000e+03 75%112500.2500000.0000000.55904663.0000000.0000000.8682548.249000e+03 max150000.000000 1.00000050708.000000109.00000098.000000329664.000000 3.008750e+06观察到:
“NumberOfDependents” 有50%以上的⼈没有家属,离散值较⼤,选取众数填充null
2.2.3 corr()查找关联
#查找关联(后⾯清洗数据的时候也要经常⽤的,⽤来⽐较效果)
import matplotlib.pyplot as plt
import aborn as sns
corr_matrix = ()
print(corr_matrix["SeriousDlqin2yrs"].sort_values(ascending=Fal))
# 下⾯的代码是图形化地展⽰各特征之间的相关性
# fig, ax = plt.subplots(figsize=(12,12))
# sns.heatmap(corr_matrix,xticklabels=lumns,yticklabels=lumns,annot=True)
SeriousDlqin2yrs 1.000000
NumberOfTime30-59DaysPastDueNotWor 0.125587
NumberOfTimes90DaysLate 0.117175
NumberOfTime60-89DaysPastDueNotWor 0.102261
Unnamed: 0 0.002801
RevolvingUtilizationOfUncuredLines -0.001802
NumberRealEstateLoansOrLines -0.007038
DebtRatio -0.007602
NumberOfDependents -0.013881
MonthlyIncome -0.018002
NumberOfOpenCreditLinesAndLoans -0.029669
age -0.115386
Name: SeriousDlqin2yrs, dtype: float64
<matplotlib.axes._subplots.AxesSubplot at 0x7f6dc5f0ad30>
查找SeriousDlqin2yrs(⽬标值,越⼩越好)与其它特征的相关性
其余特征,各特征相关性超过60%的就可以只⽤⼀个特征建模,此案例中,NumberOfTime30-
59DaysPastDueNotWor,NumberOfTimes90DaysLate,NumberOfTime60-89DaysPastDueNotWor可以只⽤⼀个2.3 数据清洗
清晰地还不是很好,过段时间我会重新整理的。其实,如果是纯⾃⼰探索的话,观察数据和数据清洗应该是⼀直交替进⾏的
2.3.1 缺失值处理
for data in combine:
data["MonthlyIncome"].fillna(data["MonthlyIncome"].mean(), inplace=True)
data["NumberOfDependents"].fillna(data["MonthlyIncome"].mode()[0], inplace=True)
#查看⼀下替换后的数据,嗯,没有空值了
train_df.info()
<class 'frame.DataFrame'>
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 12 columns):
Unnamed: 0 150000 non-null int64
SeriousDlqin2yrs 150000 non-null int64
RevolvingUtilizationOfUncuredLines 150000 non-null float64
age 150000 non-null int64
NumberOfTime30-59DaysPastDueNotWor 150000 non-null int64
DebtRatio 150000 non-null float64
MonthlyIncome 150000 non-null float64
NumberOfOpenCreditLinesAndLoans 150000 non-null int64
NumberOfTimes90DaysLate 150000 non-null int64
NumberRealEstateLoansOrLines 150000 non-null int64
NumberOfTime60-89DaysPastDueNotWor 150000 non-null int64
NumberOfDependents 150000 non-null float64
dtypes: float64(4), int64(8)
memory usage: 13.7 MB
2.3.2 异常值处理
NumberOfDependents
#可以看到,家属数量居然有6670的,⽽且数量还不少,占到了2.6%,先⽤平均值填补吧
train_df.NumberOfDependents.value_counts()
0.000000 86902
1.000000 26316
2.000000 19522
3.000000 9483
6670.221237 3924
4.000000 2862
5.000000 746
6.000000 158
7.000000 51
8.000000 24
扇形的弧长9.000000 5
10.000000 5
13.000000 1
20.000000 1
Name: NumberOfDependents, dtype: int64
##填补前先看⼀下家属数和⽬标值的相关性,以便看⼀下效果,没处理前相关度-0.013881(我把所有的相关性列出来,是为了好随时查看其它⼏个的相关性)corr_matrix = ()
corr_matrix["SeriousDlqin2yrs"].sort_values(ascending=Fal)
SeriousDlqin2yrs 1.000000
NumberOfTime30-59DaysPastDueNotWor 0.125587 NumberOfTimes90DaysLate 0.117175
NumberOfTime60-89DaysPastDueNotWor 0.102261 Unnamed: 0 0.002801 RevolvingUtilizationOfUncuredLines -0.001802 NumberRealEstateLoansOrLines -0.007038
DebtRatio -0.007602
NumberOfDependents -0.013881
MonthlyIncome -0.018002 NumberOfOpenCreditLinesAndLoans -0.029669
age -0.115386
Name: SeriousDlqin2yrs, dtype: float64
《春》古诗for data in combine:
data["NumberOfDependents"][data["NumberOfDependents"]>30] = 0
()["SeriousDlqin2yrs"]["NumberOfDependents"]
#修改异常值后"NumberOfDependents"的相关性达到了0.046869 NumberOfDependents 0.046869
age
train_df = train_df[train_df["age"]>18]
test_df = test_df[test_df["age"]>18]
combine = [train_df, test_df]
train_df[train_df["age"]<18]
Unnamed:
0SeriousDlqin2yrs RevolvingUtilizationOfUncuredLines age
NumberOfTime30-
59DaysPastDueNotWor
DebtRatio MonthlyIncome NumberOfOpenCreditLinesAndLoans
2.3.3 创建新特征
for data in combine:
data["CombinedDefaulted"] = data["NumberOfTimes90DaysLate"] + data["NumberOfTime60-89DaysPastDueNotWor"] + data["NumberOfTime30-59DaysPastDueNotWor"]
data.loc[(data["CombinedDefaulted"] >= 1), "CombinedDefaulted"] = 1
data["CombinedCreditLoans"] = data["NumberOfOpenCreditLinesAndLoans"] + data["NumberRealEstateLoansOrLines"]
data["CombinedCreditLoans"] = data["NumberOfOpenCreditLinesAndLoans"] + data["NumberRealEstateLoansOrLines"]
data.loc[(data["CombinedCreditLoans"] <= 5), "CombinedCreditLoans"] = 0
data.loc[(data["CombinedCreditLoans"] > 5), "CombinedCreditLoans"] = 1
()["SeriousDlqin2yrs"][["CombinedDefaulted", "CombinedCreditLoans"]]
CombinedDefaulted 0.314415
CombinedCreditLoans -0.054778
Name: SeriousDlqin2yrs, dtype: float64
2.4 模型和预测
raced数据集要再切分⼀下,前⾯的test是⽤来最终测试的,没有⽬标值,提交到kaggle之后它会返回给你⼀个AUC成绩,相当于评价泛化能⼒。⽽现在⾸先要⾃⼰评价当前模型
attributes=["SeriousDlqin2yrs", 'age','NumberOfTime30-59DaysPastDueNotWor','NumberOfDepen
dents','MonthlyIncome',"CombinedDefaulted","CombinedCreditLoans"]
sol=['SeriousDlqin2yrs']
attributes2 = ["Unnamed: 0", 'age','NumberOfTime30-59DaysPastDueNotWor','NumberOfDependents','MonthlyIncome',"CombinedDefaulted","CombinedCreditLoans"]
sol=['SeriousDlqin2yrs']
train_df = train_df[attributes]
test_df = test_df[attributes2]
2.4.1 逻辑回归
为了快速测试,写了⼀个类,对输⼊的所有模型进⾏k折交叉验证