金融风控入门赛01
我的机器学习实践之旅还在继续,这次我继续参加了DataWhale的组队学习活动,题目是金融风控。
一、背景介绍
赛题以金融风控中的个人信贷为背景,要求选手根据贷款申请人的数据信息预测其是否有违约的可能,以此判断是否通过此项贷款,这是一个典型的分类问题。通过这道赛题来引导大家了解金融风控中的一些业务背景,解决实际问题,帮助竞赛新人进行自我练习、自我提高。
二、数据介绍
赛题以预测用户贷款是否违约为任务,数据集报名后可见并可下载,该数据来自某信贷平台的贷款记录,总数据量超过120w,包含47列变量信息,其中15列为匿名变量。其中,80万条作为训练集,20万条作为测试集A,20万条作为测试集B,同时会对employmentTitle、purpose、postCode和title等信息进行脱敏。
Field | Description |
id | 为贷款清单分配的唯一信用证标识 |
loanAmnt | 贷款金额 |
term | 贷款期限(year) |
interestRate | 贷款利率 |
installment | 分期付款金额 |
grade | 贷款等级 |
subGrade | 贷款等级之子级 |
employmentTitle | 就业职称 |
employmentLength | 就业年限(年) |
homeOwnership | 借款人在登记时提供的房屋所有权状况 |
annualIncome | 年收入 |
verificationStatus | 验证状态 |
issueDate | 贷款发放的月份 |
purpose | 借款人在贷款申请时的贷款用途类别 |
postCode | 借款人在贷款申请中提供的邮政编码的前3位数字 |
regionCode | 地区编码 |
dti | 债务收入比 |
delinquency_2years | 借款人过去2年信用档案中逾期30天以上的违约事件数 |
ficoRangeLow | 借款人在贷款发放时的fico所属的下限范围 |
ficoRangeHigh | 借款人在贷款发放时的fico所属的上限范围 |
openAcc | 借款人信用档案中未结信用额度的数量 |
pubRec | 贬损公共记录的数量 |
pubRecBankruptcies | 公开记录清除的数量 |
revolBal | 信贷周转余额合计 |
revolUtil | 循环额度利用率,或借款人使用的相对于所有可用循环信贷的信贷金额 |
totalAcc | 借款人信用档案中当前的信用额度总数 |
initialListStatus | 贷款的初始列表状态 |
applicationType | 表明贷款是个人申请还是与两个共同借款人的联合申请 |
earliesCreditLine | 借款人最早报告的信用额度开立的月份 |
title | 借款人提供的贷款名称 |
policyCode | 公开可用的策略_代码=1新产品不公开可用的策略_代码=2 |
n系列匿名特征 | 匿名特征n0-n14,为一些贷款人行为计数特征的处理 |
三、赛题理解
本次的场景属于典型的金融风控场景,其中预测的目标为用户是否违约,训练集数据总量80万条,测试集数据量20万条,总特征数为47,其中,无缺失特征为25,首先先要对数据进行处理,这里主要做的处理为区分缺失列及转换特殊格式。
数据读取
import pandas as pd
import numpy as np
train = pd.read_csv('../data/train.csv')
testA = pd.read_csv('../data/testA.csv')
print('Train data shape:',train.shape) #Train data shape: (800000, 47)
print('TestA data shape:',testA.shape) #TestA data shape: (200000, 48)
print(train.columns)
#Index(['id', 'loanAmnt', 'term', 'interestRate', 'installment', #'grade',
# 'subGrade', 'employmentTitle', 'employmentLength', #'homeOwnership',
# 'annualIncome', 'verificationStatus', 'issueDate', #'isDefault',
# 'purpose', 'postCode', 'regionCode', 'dti', #'delinquency_2years',
# 'ficoRangeLow', 'ficoRangeHigh', 'openAcc', 'pubRec',
# 'pubRecBankruptcies', 'revolBal', 'revolUtil', 'totalAcc',
# 'initialListStatus', 'applicationType', 'earliesCreditLine', #'title',
# 'policyCode', 'n0', 'n1', 'n2', 'n2.1', 'n4', 'n5', 'n6', 'n7', 'n8',
# 'n9', 'n10', 'n11', 'n12', 'n13', 'n14'], dtype='object')
区分缺失列及非缺失列
not_null_column = ['id','loanAmnt','term','interestRate','installment','grade','subGrade','homeOwnership',
'annualIncome','verificationStatus','issueDate','purpose','regionCode',
'delinquency_2years','ficoRangeLow','ficoRangeHigh','openAcc','pubRec','revolBal',
'totalAcc','initialListStatus','applicationType','earliesCreditLine','policyCode','isDefault']
null_column = [col for col in train.columns if col not in not_null_column]
转换特殊格式
list1=sorted(train['subGrade'].astype(str).unique())
subGrade_dic = {list1[i]:i for i in range(len(list1))}
grade_dic = {'E':5, 'D':4, 'A':1, 'C':3, 'B':2, 'F':6, 'G':7}
earlies_dic = {'Aug':'08', 'May':'05', 'Jul':'07', 'Oct':'10', 'Dec':'12',
'Apr':'04', 'Jan':'01', 'Nov':'11', 'Feb':'02','Mar':'03', 'Jun':'06', 'Sep':'09'}
train['issueDate']=train['issueDate'].apply(lambda x:str(x)[:4]+str(x)[5:7]+str(x)[-2:])
train['grade']=train['grade'].map(grade_dic)
train['earliesCreditLine'] = train['earliesCreditLine'].apply(lambda x:x[4:]+earlies_dic[x[:3]])
train['subGrade'] = train['subGrade'].map(subGrade_dic)
然后,我还画出了数据数量与时间的相关关系。
从图中可以看到,大概从14年5月到18年年初用户交易较为活跃。
特征相关性分析
for i in not_null_column:
print(i,'isDefault',train[i].corr(train['isDefault']))
# id isDefault -0.00035108389556033935
# loanAmnt isDefault 0.06520961446281096
# term isDefault 0.17512564943237638
# interestRate isDefault 0.2592022258683037
# installment isDefault 0.05152448921221738
# grade isDefault 0.2618575780880932
# subGrade isDefault 0.26772357566751687
# homeOwnership isDefault 0.05459850810406215
# annualIncome isDefault -0.04278186579523253
# verificationStatus isDefault 0.08855737298108195
从相关性可以看出,贷款利率、贷款等级、贷款等级之子级与是否违约相关性较强。
catboost-baseline
from catboost import CatBoostClassifier
from sklearn.model_selection import train_test_split
import numpy as np
X_train, X_validation, y_train, y_validation = train_test_split(train[not_null_column],train['isDefault'],test_size=0.3 , random_state=1234)
model = CatBoostClassifier(iterations=100, depth=5,learning_rate=0.5, loss_function='Logloss',
logging_level='Verbose')
model.fit(X_train,y_train,eval_set=(X_validation, y_validation),plot=True)