tianchi——贷款违约预测

解决的问题是预测预测用户贷款是否违约为任务。提交的形式应该是一个二分类形式(2个字段,一个是id,一个是违约的可能性),目前提交第一名结果是0.749。该结果是竞赛采用AUC作为评价指标。AUC(Area Under Curve)被定义为 ROC曲线 下与坐标轴围成的面积。
比赛地址:https://tianchi.aliyun.com/competition/entrance/531830/forum

1.数据处理

train.csv训练集所包含的内容

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,为一些贷款人行为计数特征的处理

如果想更方便快捷地了解数据的全貌,推荐一个python库:pandas_profiling,这个库只需要一行代码就可以生成数据EDA报告,文章最后有调试介绍。


pandas_profiling形成的数据预览

Constant表示只有一个变量值;High cardinality是指高数量类别特征;High correlation是指高相似特征


特征之间的关系

当然还包括缺失值、最值、均值、中值、标准差等,还可以查看Common values和Extreme values这两类普遍值和极端值。
对以上特征进行分类:

numerrical——表示数值特征
nominal——表示无顺序的类别特征
ordina——表示有顺序的类别特征
y——表示预测值。

    numerrical = ['loanAmnt','interestRate','installment','annualIncome','dti',
                  'delinquency_2years','ficoRangeHigh','ficoRangeLow','openAcc',
                  'pubRec','pubRecBankruptcies','revolBal','revolUtil','totalAcc']
    nominal = ['term','employmentTitle','homeOwnership','verificationStatus',
               'purpose','postCode','regionCode','initialListStatus','applicationType',
               'title','n0','n1','n2','n3','n4','n5','n6','n7','n8','n9','n10','n11','n12',
               'n13','n14','id']
    ordinal = ['grade','subGrade','employmentLength','earliesCreditLine','issueDate']
    y = ['isDefault']

2.特征工程

通过以上pandas_profiling对探索性数据分析(EDA)后,通过删除一些冗余值(若干疑似重复列n2),在没有引入业务知识;
对所有非数值字段直接Target encode;采用LGBMRegressor,随手设置了一些参数;就可以实现本地十折AUC均值0.7317,线上0.7291,具体可以见官网分享操作。

import pandas as pd
import numpy as np
from category_encoders.target_encoder import TargetEncoder
from sklearn.model_selection import KFold
from sklearn.metrics import auc, roc_curve
from lightgbm import LGBMRegressor

# 导入数据
train = pd.read_csv('train.csv', index_col='id')
test = pd.read_csv('testA.csv', index_col='id')
target = train.pop('isDefault')
test = test[train.columns]

# 非数值列
s = train.apply(lambda x:x.dtype)
tecols = s[s=='object'].index.tolist()

# 模型
def makelgb():
    lgbr = LGBMRegressor(num_leaves=30
                        ,max_depth=5
                        ,learning_rate=.02
                        ,n_estimators=1000
                        ,subsample_for_bin=5000
                        ,min_child_samples=200
                        ,colsample_bytree=.2
                        ,reg_alpha=.1
                        ,reg_lambda=.1
                        )
    return lgbr

# 本地验证
kf = KFold(n_splits=10, shuffle=True, random_state=100)
devscore = []
for tidx, didx in kf.split(train.index):
    tf = train.iloc[tidx]
    df = train.iloc[didx]
    tt = target.iloc[tidx]
    dt = target.iloc[didx]
    te = TargetEncoder(cols=tecols)
    tf = te.fit_transform(tf, tt)
    df = te.transform(df)
    lgbr = makelgb()
    lgbr.fit(tf, tt)
    pre = lgbr.predict(df)
    fpr, tpr, thresholds = roc_curve(dt, pre)
    score = auc(fpr, tpr)
    devscore.append(score)
print(np.mean(devscore))

# 在整个train集上重新训练,预测test,输出结果
lgbr = makelgb()
te = TargetEncoder(cols=tecols)
tf = te.fit_transform(train, target)
df = te.transform(test)
lgbr.fit(tf, target)
pre = lgbr.predict(df)
pd.Series(pre, name='isDefault', index=test.index).reset_index().to_csv('submit.csv', index=False)

那么结合标杆算法,我们首先确定的基础是用的LGB,分三个角度去优化,分别是:特征工程,编码以及业务知识优化(和第一点有重合)。

2.1 特征工程

①债权类——从annualIncome(年收入)、installment(分期付款金额)、loanAmnt(贷款金额)、dti(债务收入比)几个财务类信息互相组合提取出新特征,如:
年收入/分期付款,如年收入10w,分期付款1w,那么这个比就是10.
贷款金额/分期付款,如贷款30w,分期付款1w,那个这个比就是30.
收入*债务比,就得到了债务的值,如收入10w,债务比是0.2,那么债务就5w,这个值是为下面的比值服务的。
贷款金额/债务,就得到银行的贷款和外债的比,如贷款30w,债务是5w,那么这个比就是6,很明显这个值要越大越好(0除外),即没有债务。
计算信用开户到本次借贷的时间(CreditLine),即信用账户的年限。

    x['Income_installment']=round(x.loc[:,'annualIncome']/x.loc[:,'installment'],2)
    x['loanAmnt_installment']=round(x.loc[:,'loanAmnt']/x.loc[:,'installment'],2)
    x['debt']=round(x.loc[:,'annualIncome']*x.loc[:,'dti'],2)
    x['loanAmnt_debt']=round(x.loc[:,'annualIncome']/x.loc[:,'debt'],2)
    #----------------------------CreditLine--------------------------------
    x['issueDate'] = x.loc[:,"issueDate"].apply(lambda s: int(s[:4]))
    x['earliesCreditLine'] = x.loc[:,'earliesCreditLine'].apply(lambda s: int(s[-4:]))
    x['CreditLine'] = x.loc[:,'earliesCreditLine'] - x.loc[:,'issueDate']

③变量的处理
如employmentLength——把就业年限变为几个档次(转换为连续变量)

    def employmentLength_to_int(s):
        if pd.isnull(s):
            return s
        else:
            return np.int8(s.split()[0])
    x["employmentLength"].replace(to_replace="10+ years", value="10 years", inplace=True)
    x["employmentLength"].replace(to_replace="< 1 year", value="0 years", inplace=True)
    x['employmentLength'] = x.loc[:,"employmentLength"].apply(employmentLength_to_int)

数据特征中有提到fico有high有low,求平均

  x['fico']=(x.loc[:,'ficoRangeHigh']+x.loc[:,'ficoRangeLow'])*0.5
代码块

综上所述,增添新特征;根据之前Warnings,删除相似特征(High correlation)、唯一值(Unique)、单变量值(Constant),和以上用来生成新特征的旧特征。

 numerrical=list(set(numerrical) - {'ficoRangeHigh', 'ficoRangeLow'}) + 
    ['Income_installment','loanAmnt_installment','loanAmnt_debt','fico']
    nominal=list(set(nominal)-{'id','n10', 'n2'})
    ordinal=list(set(ordinal) - {'grade', 'earliesCreditLine', 'issueDate'}) + ['CreditLine']

2.2 特征编码

选择了XGB模型,所以按照模型去查询编码方式(CatBoost)。
根据XGBoost之类别特征的处理和kaggle编码categorical feature两篇编码总结。
https://blog.csdn.net/m0_37870649/article/details/104550054
https://zhuanlan.zhihu.com/p/40231966
anyway,编码总结:
label encoding
特征存在内在顺序 (ordinal feature)
one hot encoding
特征无内在顺序,category数量 < 4
target encoding (mean encoding, likelihood encoding, impact encoding)
特征无内在顺序,category数量 > 4
beta target encoding
特征无内在顺序,category数量 > 4, K-fold cross validation
不做处理(模型自动编码)
CatBoost,lightgbm

from category_encoders import WOEEncoder ,OneHotEncoder,CatBoostEncoder,TargetEncoder
    def Category_Encoders(train_x, train_y, test_x, vel_x):
        for col in nominal:
            distinct = train_x[col].nunique()
            if distinct < 4 and distinct >2:
                enc = OneHotEncoder(handle_missing='indicator').fit(train_x[col], train_y)
            elif distinct >= 4:
                # enc = WOEEncoder().fit(train_x[col], train_y)
                # enc = TargetEncoder().fit(train_x[col],train_y)
                enc = CatBoostEncoder().fit(train_x[col],train_y)

            train_x[col] = enc.transform(train_x[col])
            test_x[col] = enc.transform(test_x[col])
            vel_x[col] = enc.transform(vel_x[col])

        return train_x, test_x, vel_x

3.模型调参

调参一般使用GridSearchCV,但是几M的数据,范围调整大一些,就可能要跑十几个小时,本次是80w条数据,基本放弃。
当需要调很多参数或是数据集很大的时候,欢迎使用贝叶斯优化调参:

3.1贝叶斯优化调参

https://blog.csdn.net/ssswill/article/details/85274097

def BO_xgb(x,y):
    t1=time.clock()

    def xgb_cv(max_depth,gamma,min_child_weight,max_delta_step,subsample,colsample_bytree):
        paramt={'booster': 'gbtree',
                'max_depth': int(max_depth),
                'gamma': gamma,
                'eta': 0.1,
                'objective': 'binary:logistic',
                'nthread': 4,
                'eval_metric': 'auc',
                'subsample': max(min(subsample, 1), 0),
                'colsample_bytree': max(min(colsample_bytree, 1), 0),
                'min_child_weight': min_child_weight,
                'max_delta_step': int(max_delta_step),
                'seed': 1001}
        model=XGBClassifier(**paramt)
        res = cross_val_score(model,x, y, scoring='roc_auc', cv=5).mean()
        return res
    cv_params ={'max_depth': (5, 12),
                'gamma': (0.001, 10.0),
                'min_child_weight': (0, 20),
                'max_delta_step': (0, 10),
                'subsample': (0.4, 1.0),
                'colsample_bytree': (0.4, 1.0)}
    xgb_op = BayesianOptimization(xgb_cv,cv_params)
    xgb_op.maximize(n_iter=20)
    print(xgb_op.max)

    t2=time.clock()
    print('耗时:',(t2-t1))

    return xgb_op.max

我们对'max_depth','gamma','min_child_weight','max_delta_step','subsample','colsample_bytree'六个参数进行调参,并最后赋予'n_estimators':1000,'learning_rate':0.02。
最终最佳参数为

'booster': 'gbtree','eta': 0.1,'nthread': 4,'eval_metric': 'auc','objective': 'binary:logistic',
                    'colsample_bytree': 0.4354, 'gamma': 9.888, 'max_delta_step': 4,'n_estimators':1000,'learning_rate':0.02,
                    'max_depth': 10, 'min_child_weight': 3.268, 'subsample': 0.7157

3.2分别观察下预测集和训练集的ROC

def roc(m,x,y,name):
    y_pred = m.predict_proba(x)[:,1]
    """"预测并计算roc的相关指标"""
    fpr, tpr, threshold = metrics.roc_curve(y, y_pred)
    roc_auc = metrics.auc(fpr, tpr)
    print(name+'AUC:{}'.format(roc_auc))
    """画出roc曲线图"""
    plt.figure(figsize=(8, 8))
    plt.title(name)
    plt.plot(fpr, tpr, 'b', label = name + 'AUC = %0.4f' % roc_auc)
    plt.ylim(0,1)
    plt.xlim(0,1)
    plt.legend(loc='best')
    plt.title('ROC')
    plt.ylabel('True Positive Rate')
    plt.xlabel('False Positive Rate')
    # 画出对角线
    plt.plot([0,1],[0,1],'r--')
    plt.show()

3.3 提交成绩

def prediction(m,x):
    submit=pd.read_csv('sample_submit.csv')
    y_pred = m.predict_proba(x)[:,1]
    submit['isDefault'] = y_pred
    submit.to_csv('prediction.csv', index=False)

4.操作参考

官网分享操作:
【零基础入门金融风控-贷款违约预测】比赛描述
https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.18.3b3022fa38mjJF&postId=129318
【零基础入门金融风控-贷款违约预测】Baseline-LGBM(结果0.730左右)
https://tianchi.aliyun.com/forum/postDetail?spm=5176.12586969.1002.21.3b3022fa38mjJF&postId=128654
【sklearn中多种编码方式】
https://mattzheng.blog.csdn.net/article/details/107851162

pandas_profiling :教你一行代码生成数据分析报告:
https://zhuanlan.zhihu.com/p/85967505

讲解视频
第1讲:赛题理解baseline讲解
主讲人:鱼佬(王贺)武汉大学计算机硕士,天池数据科学家,2019和2020腾讯广告算法冠军
链接:https://tianchi.aliyun.com/course/video?liveId=41203
第2讲:数据探索性分析和特征工程
主讲人:言溪(陶旭东):北京师范大学硕士 ,算法工程师
链接:https://tianchi.aliyun.com/course/live?liveId=41204
第3讲:建模调参,模型融合
主讲人:小一(吴争光)Datawhale成员,金融风控爱好者,数据分析工程师
链接:https://tianchi.aliyun.com/course/live?liveId=41206

https://www.jianshu.com/p/bc96824e1ca8

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容