阿里云安全大赛与贷款违约预测-代码思路分享

赛事介绍

赛题以预测用户贷款是否违约为任务,数据集报名后可见并可下载,该数据来自某信贷平台的贷款记录,总数据量超过120w,包含47列变量信息,其中15列为匿名变量。为了保证比赛的公平性,将会从中抽取80万条作为训练集,20万条作为测试集A,20万条作为测试集B,同时会对employmentTitle、purpose、postCode和title等信息进行脱敏。

思路分享

该比赛主要是围绕着借款人的多个维度信息,对其违约行为进行预测,是一个经典的二分类问题。赛题的关键点在于构造新特征,因为贷款违约预测与业务强相关,所以特征构造的出发点主要从借款人的个人信息着手,利用借款人的一阶特征构造个人的"用户画像"(新特征),从不同维度体现借款人的生活压力/信用度/偿贷压力等。

模型

  • LightGBM
  • Xgboost
  • Catboost
  • 三个模型融合+5折Kfold

代码展示

特征工程

import warnings
from datetime import datetime, timedelta
import numpy as np
import pandas as pd

import gc
import os
from tqdm import tqdm

from sklearn.model_selection import StratifiedKFold

warnings.simplefilter('ignore')
tqdm.pandas()
%matplotlib inline

pd.set_option('max_columns', None)
pd.set_option('max_rows', None)
pd.set_option('max_colwidth', 200)

#加载数据
df_train = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/贷款违约预测/train.csv')
df_test = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/贷款违约预测/testA.csv')

df_feature = df_train.append(df_test)
df_feature = df_feature.reset_index(drop=True)

#查看数据集前5行
df_feature.head()

在这里插入图片描述

#删掉(唯一值个数 = 样本数)的特征
del df_feature['policyCode']

#为了形成等级划分,对贷款等级做有顺序的映射
grade_map = {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7}
df_feature['grade'] = df_feature['grade'].map(grade_map)


#一共5个贷款等级,每个贷款等级有5个子等级,那么一共会有5*5=25个贷款子等级。所以将所有贷款子等级映射到25个数字上
def subGrade_map(x):
    grade, num = list(x)
    ans = grade_map[grade]
    ans = ans * 5 + int(num) - 1
    return ans


df_feature['subGrade'] = df_feature['subGrade'].map(subGrade_map)
#将就业年限转换成整数的形式
def employmentLength_to_int(s):
    if pd.isnull(s):
        return s
    else:
        return np.int8(s.split()[0])

#就业年限10年以上的,转换成10年
df_feature['employmentLength'].replace(
    to_replace='10+ years', value='10 years', inplace=True)

#就业年限小于一年的,转换成0年
df_feature['employmentLength'].replace('< 1 year', '0 years', inplace=True)
df_feature['employmentLength'] = df_feature['employmentLength'].apply(
    employmentLength_to_int)
#将贷款发放月份转换成时间格式
df_feature['issueDate_dt'] = pd.to_datetime(
    df_feature['issueDate'], format='%Y-%m-%d')

# start = df_feature['issueDate_dt'].min()
# df_feature['issueDate_diff'] = df_feature['issueDate_dt'].apply(lambda x:x-start).dt.days

#取出贷款发放的年份
df_feature['issueDate_year'] = df_feature['issueDate_dt'].dt.year

#取出信用额度开立的年份,并转换成整数类型
df_feature['earliesCreditLine_year'] = df_feature['earliesCreditLine'].str.split(
    '-', expand=True)[1]
df_feature['earliesCreditLine_year'] = df_feature['earliesCreditLine_year'].astype(
    'int')

#构造新特征:贷款发放年份 - 信用额度开立年份 = 放贷时限
df_feature['issueDate_year_earliesCreditLine_year_minus'] = df_feature['issueDate_year'] - \
    df_feature['earliesCreditLine_year']

del df_feature['issueDate_dt']
'''
首先用(贷款金额/贷款年限)得到每年还贷的金额,然后再将其/年收入得到债务比率.
侧面映射出借款人的还贷压力。如果>1,说明借款人的年收入不够还款;反之则足以还款。
'''
df_feature['debt_ratio_year'] = df_feature['loanAmnt'] / df_feature['term'] / df_feature['annualIncome']
#(借款人在贷款期限间一共所需还贷的金额)/(信贷周转余额合计)
df_feature['installment_term_revolBal'] = df_feature['installment']*12*df_feature['term']/(df_feature['revolBal']+0.1)

#(循环额度利用率)/(信贷周转余额)
df_feature['revolUtil_revolBal'] = df_feature['revolUtil']/(df_feature['revolBal']+0.1)

#(年收入)/(贷款金额)
df_feature['annualIncome_loanAmnt'] = df_feature['annualIncome']/(df_feature['loanAmnt']+0.1)
#贷款金额/(债务比*年收入 = 总债务),侧面反映出借款人的偿债压力
df_feature['loanAmnt_dti_annualIncome'] = df_feature['loanAmnt']/(np.abs(df_feature['dti'])*df_feature['annualIncome']+0.1)
#信贷周转余额/贷款金额,侧面反映出贷款的利用率
df_feature['revolBal_loanAmnt'] = df_feature['revolBal']/(df_feature['loanAmnt']+0.1)
#信贷周转余额/分期付款金额,同样侧面反映出贷款的利用率
df_feature['revolBal_installment'] = df_feature['revolBal']/(df_feature['installment']+0.1)
#年收入/分期付款金额,侧面反映出贷款的偿债能力
df_feature['annualIncome_installment'] = df_feature['annualIncome']/(df_feature['installment']+0.1)
#借款人信用档案中未结信用额度的数量/借款人信用档案中当前的信用额度总数,侧面反映借款人的信用程度
df_feature['openAcc_totalAcc'] = df_feature['openAcc']/df_feature['totalAcc']
#填充 债务比 空值
df_feature['dti'] = np.abs(df_feature['dti'].fillna(1000))
#定义好类别特征
cate_features = ['applicationType', 'employmentLength', 'employmentTitle', 'grade', 'homeOwnership', 'initialListStatus',
                 'postCode', 'purpose', 'regionCode', 'subGrade', 'title', 'verificationStatus']
#定义好数值特征
dense_features = ['annualIncome', 'delinquency_2years', 'dti', 'employmentLength', 'ficoRangeHigh',
                  'ficoRangeLow', 'installment', 'interestRate', 'loanAmnt', 'openAcc', 'pubRec', 'pubRecBankruptcies',
                  'revolBal', 'revolUtil', 'subGrade', 'term', 'totalAcc']
#比较借款人的年收入和就业年限水平
df_feature['avg_income'] = df_feature['annualIncome'] / df_feature['employmentLength']
#计算借款人的总收入
df_feature['total_income'] = df_feature['annualIncome'] * df_feature['employmentLength']
#计算借款人每年的还贷金额
df_feature['avg_loanAmnt'] = df_feature['loanAmnt'] / df_feature['term']
#贷款利率/贷款年限
df_feature['mean_interestRate'] = df_feature['interestRate'] / df_feature['term']
#计算借款人总共需要还款的金额
df_feature['all_installment'] = df_feature['installment'] * df_feature['term']
#每年还贷的金额/年收入
df_feature['rest_money_rate'] = df_feature['avg_loanAmnt'] / (df_feature['annualIncome'] + 0.1)
#借款人信用档案中当前的信用额度总数 - 借款人信用档案中未结信用额度的数量
df_feature['closeAcc'] = df_feature['totalAcc'] - df_feature['openAcc']
#借款人在贷款发放时的fico所属平均范围
df_feature['ficoRange_mean'] = (df_feature['ficoRangeHigh'] + df_feature['ficoRangeLow']) / 2
del df_feature['ficoRangeHigh'], df_feature['ficoRangeLow']
#贬损公共记录的数量 - 公开记录清除的数量
df_feature['rest_pubRec'] = df_feature['pubRec'] - df_feature['pubRecBankruptcies']
#贷款金额 - 信贷周转余额合计
df_feature['rest_Revol'] = df_feature['loanAmnt'] - df_feature['revolBal']
#计算贷款是否已经发放
df_feature['dis_time'] = df_feature['issueDate_year'] - (2020 - df_feature['earliesCreditLine_year'])
# 这里是做StratifiedKFold分层采用,确保训练集,验证集中各类别样本的比例与原始数据集中相同。然后再统计每个类别/二阶交叉特征的欺诈率。这样做的目的可以防止过拟合。
gps = []
for f in cate_features:
    gps.append([f])

for f1 in cate_features:
    for f2 in cate_features:
        if f1 != f2:
            gps.append([f1, f2])
                       
def statis_feat(df_know, df_unknow):
    for group_by in tqdm(gps):
        group = df_know.groupby(group_by).agg({'isDefault': ['mean']})
        columns = ['{}_default_ratio'.format('_'.join(group_by))]
        group.columns = columns
        group.reset_index(inplace=True)
        df_unknow = df_unknow.merge(group, on=group_by, how='left')

    return df_unknow


df_train = df_feature[~df_feature['isDefault'].isnull()]
df_train = df_train.reset_index(drop=True)
df_test = df_feature[df_feature['isDefault'].isnull()]

df_stas_feat = None
kf = StratifiedKFold(n_splits=5, random_state=seed, shuffle=True)
for train_index, val_index in kf.split(df_train, df_train['isDefault']):
    df_fold_train = df_train.iloc[train_index]
    df_fold_val = df_train.iloc[val_index]

    df_fold_val = statis_feat(df_fold_train, df_fold_val)
    df_stas_feat = pd.concat([df_stas_feat, df_fold_val], axis=0)

    del (df_fold_train)
    del (df_fold_val)
    gc.collect()

df_test = statis_feat(df_train, df_test)
df_feature = pd.concat([df_stas_feat, df_test], axis=0)
df_feature = df_feature.reset_index(drop=True)

del (df_stas_feat)
del (df_train)
del (df_test)
gc.collect()

模型(只展示模型超参)

model = lgb.LGBMClassifier(objective='binary',
                           boosting_type='gbdt',
                           num_leaves=32,
                           max_depth=6,
                           learning_rate=0.05,
                           n_estimators=10000,
                           subsample=0.8,
                           feature_fraction=0.6,
                           reg_alpha=10,
                           reg_lambda=12,
                           random_state=seed,
                           is_unbalance=True,
                           metric='auc')
model = xgb.XGBClassifier(max_depth=6,
                          learning_rate=0.05,
                          n_estimators=10000,
                          subsample=0.8,
                          reg_alpha=10,
                          reg_lambda=12,
                          tree_method='gpu_hist',
                          random_state=seed)
model = cbt.CatBoostClassifier(eval_metric='AUC',
                               max_depth=6,
                               learning_rate=0.05,
                               n_estimators=10000,
                               reg_lambda=12,
                               task_type='GPU',
                               random_state=seed)
  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.Wiggles

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值