快速导航×

【金融风控系列】_[1]_贷款违约预测2025-07-21 17:32:20
本文围绕天池贷款违约预测赛题,介绍零基础入门金融风控的实现过程。使用超过120万条贷款记录数据,对比多种数据不平衡处理方法,以CatBoost为基分类器,经5折交叉验证,结合过采样的模型线上表现较好(AUC 0.7347),SMOTE和BalanceCascade效果欠佳,为相关任务提供参考。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

【金融风控系列】_[1]_贷款违约预测 -

零基础入门金融风控-贷款违约预测

该赛题来自 TIANCHI 零基础入门系列,仅用作学习交流

该数据来自某信贷平台的贷款记录,总数据量超过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,为一些贷款人行为计数特征的处理

参考:

[1] https://github.com/caozichuan/TianChi_loadDefault/blob/main/GitHub_loadDefault/model/xgb_github.ipynb

[2] https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.21852664.0.0.4f70379c1gFeCt&postId=129321

[3] https://imbalanced-ensemble.readthedocs.io/en/latest/index.html

[4] https://blog.csdn.net/qq_31367861/article/details/111145816?spm=5176.21852664.0.0.22686e12UZfkmZ

文献4方法线上提交分数为0.7391

       

[5] https://blog.csdn.net/qq_44694861/article/details/109753004?spm=5176.21852664.0.0.667f4288swn2OQ

本项目主要是对金融风控中常见的数据不平衡方法进行对比,以及对 self-paced-ensemble 包进行学习

使用的方法如下:

  • 无处理
  • SMOTE算法

SMOTE(Synthetic Minority Oversampling Technique),合成少数类过采样技术.它是基于随机过采样算法的一种改进方案,为了克服随机过采样算法泛化能力差的缺点,SMOTE算法的对少数类样本近邻进行采样,根据少数类样本人工合成新样本添加到数据集中。

美图云修 美图云修

商业级AI影像处理工具

美图云修 50 查看详情 美图云修
  • SPE算法

Liu Z , Cao W , Gao Z , et al. Self-paced Ensemble for Highly Imbalanced Massive Data Classification[J]. 2019.

  • 过采样算法

重复正样本,使得正样本与负样本比例接近 1 : 1

  • 欠采样算法

从数量多的样本里面随机选择样本进行抛弃,为了避免随机性影响,我们独立训练多个模型进行简单平均

  • BalanceCascade算法

Liu, X. Y., Wu, J., & Zhou, Z. H. “Exploratory undersampling for class-imbalance learning.” IEEE Transactions on Systems, Man, and Cybernetics, Part B (Cybernetics) 39.2 (2008): 539-550

注意事项

  • 运行主要有两部分,数据处理 和 模型选择 ,根据注释部分,自己进行数据处理和算法组合。默认CBT模型+过采样算法。
In [1]
#!pip install pandas --user#!pip install lightgbm --user#!pip install xgboost --user!pip install catboost --user
!pip install joblib --user
!pip install scikit-learn --user
!pip install imblearn --user
!pip install imbalanced-ensemble --user
!pip self-paced-ensemble --user
    In [2]
import pandas as pdimport datetimeimport warnings
warnings.filterwarnings('ignore')from sklearn.model_selection import StratifiedKFold#warnings.filterwarnings('ignore')#%matplotlib inlinefrom sklearn.metrics import roc_auc_score## 数据降维处理的from sklearn.model_selection import train_test_split  
from catboost import CatBoostClassifierimport imbalanced_ensemble as imbensfrom collections import Counterfrom imbalanced_ensemble.sampler.over_sampling import     SMOTE 
import numpy as np
train=pd.read_csv("./data/data53042/train.csv")
testA=pd.read_csv("./data/data53042/testA.csv")

numerical_fea = list(train.select_dtypes(exclude=['object']).columns)
category_fea = list(filter(lambda x: x not in numerical_fea,list(train.columns)))
label = 'isDefault'numerical_fea.remove(label)#按照中位数填充数值型特征train[numerical_fea] = train[numerical_fea].fillna(train[numerical_fea].median())
testA[numerical_fea] = testA[numerical_fea].fillna(testA[numerical_fea].median())#按照众数填充类别型特征train[category_fea] = train[category_fea].fillna(train[category_fea].mode())
testA[category_fea] = testA[category_fea].fillna(testA[category_fea].mode())def make_fea(data):
    data['issueDate'] = pd.to_datetime(data['issueDate'],format='%Y-%m-%d')
    startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')    #构造时间特征
    data['issueDateDT'] = data['issueDate'].apply(lambda x: x-startdate).dt.days

    data['grade'] = data['grade'].map({'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7})
    data['subGrade'] = data['subGrade'].map({'E2':1,'D2':2,'D3':3,'A4':4,'C2':5,'A5':6,'C3':7,'B4':8,'B5':9,'E5':10,        'D4':11,'B3':12,'B2':13,'D1':14,'E1':15,'C5':16,'C1':17,'A2':18,'A3':19,'B1':20,        'E3':21,'F1':22,'C4':23,'A1':24,'D5':25,'F2':26,'E4':27,'F3':28,'G2':29,'F5':30,        'G3':31,'G1':32,'F4':33,'G4':34,'G5':35})

    data = pd.get_dummies(data, columns=['subGrade', 'homeOwnership', 'verificationStatus', 'purpose', 'regionCode'], drop_first=True)
    data['employmentLength'] = data['employmentLength'].map({'NaN':-1,'1 year':1,'2 years':2,'3 years':3,'4 years':4,'5 years':5,'6 years':6,'7 years':7,'8 years':8,'9 years':9,'10+ years':10,'< 1 year':0})
    data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda s: int(s[-4:]))    for item in ['n0','n1','n2','n2.1','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:
        data['grade_to_mean_' + item] = data['grade'] / data.groupby([item])['grade'].transform('mean')
        data['grade_to_std_' + item] = data['grade'] / data.groupby([item])['grade'].transform('std')
    data['n15']=data['n8']*data['n10']    return data# 特征工程 简单处理train = make_fea(train)
testA = make_fea(testA)print(train.shape)print(testA.shape)print("数据预处理完成!")  
sub=testA[['id']].copy()
sub['isDefault']=0testA=testA.drop(['id','issueDate'],axis=1)
data_x=train.drop(['isDefault','id','issueDate'],axis=1)

data_y=train[['isDefault']].copy()

col=['grade','subGrade','employmentTitle','homeOwnership','verificationStatus','purpose','postCode','regionCode',     'initialListStatus','applicationType','policyCode']for i in data_x.columns:    if i in col:
        data_x[i] = data_x[i].astype('str')for i in testA.columns:    if i in col:
        testA[i] = testA[i].astype('str')

answers = []
mean_score = 0n_folds = 5sk = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=2019)print(data_x.shape)print(testA.shape)# 在测试集中有部分特征训练集中没有,进行处理COLNAME = data_x.columns
testA = testA[COLNAME]# 处理INF NAN ,替换成 -1 注意:np.inf和-np.inf是不同的两个值,这里经常遇到忘记处理后者引发错误data_x.replace([np.inf, -np.inf, np.nan], -1, inplace=True)
testA.replace([np.inf, -np.inf, np.nan], -1, inplace=True)#  -----重复正样本,因为原始数据比例约为3:1 因此对正样本重复三次拼接到原始数据中-----a = [i for i, x in enumerate(data_y.values) if x[0]==1]
all_1 = data_x.iloc[a]
all_y = pd.DataFrame(np.ones(all_1.shape[0],dtype=int),columns=['isDefault'])print(all_y.shape)print(data_x.shape,data_y.shape)for i in range(3):
    data_y= pd.concat([data_y,all_y],axis=0)
    data_x = pd.concat([data_x,all_1],axis=0)    print(data_x.shape,data_y.shape)
data_x.reset_index(drop=True,inplace =True)
data_y.reset_index(drop=True,inplace =True)'''
# -----使用SMOTE算法对负样本进行扩充-----
sm = SMOTE(random_state=42)
data_x, data_y = sm.fit_resample(data_x, data_y)
print('Resampled dataset' , data_y.value_counts())
'''
   

基础模型定义及训练

使用CatBoost分类模型作为基准模型

In [ ]
model=CatBoostClassifier(
            loss_function="Logloss",
            eval_metric="AUC",
            task_type="CPU",
            learning_rate=0.1,
            iterations=500,
            random_seed=2025,
            verbose=500,
            depth=7)for train, test in sk.split(data_x, data_y):
    x_train = data_x.iloc[train]
    y_train = data_y.iloc[train]
    x_test = data_x.iloc[test]
    y_test = data_y.iloc[test]    '''
    # -----使用SPE 算法对不平衡数据进行处理-----
    clf = imbens.ensemble.SelfPacedEnsembleClassifier(
        base_estimator = model,#基准分类模型 可以自定义,需要模型包含fit等方法,具体信息请查看参考文献3
        n_estimators = 5,
        random_state=49
    ).fit(x_train, y_train)
    '''
    # -----使用BalanceCascade 算法对不平衡数据进行处理-----
    '''
    clf = imbens.ensemble.BalanceCascadeClassifier(        
        base_estimator = model,#基准分类模型 可以自定义,需要模型包含fit等方法,具体信息请查看参考文献3
        n_estimators = 5,
        random_state=49
    ).fit(x_train, y_train)
    '''

    # 基准分类模型
    clf = model.fit(x_train,y_train)#, eval_set=(x_test,y_test),cat_features=col)

    yy_pred_valid=clf.predict_proba(x_test)[:,-1]    print('cat验证的auc:{}'.format(roc_auc_score(y_test, yy_pred_valid)))
    mean_score += roc_auc_score(y_test, yy_pred_valid) / n_folds
    y_pred_valid = clf.predict_proba(testA)[:,-1]
    answers.append(y_pred_valid)print('mean valAuc:{}'.format(mean_score))
cat_pre=sum(answers)/n_folds
sub['isDefault']=cat_pre
sub.to_csv('金融预测.csv',index=False)
   

欠采样模型训练

In [ ]
model=CatBoostClassifier(
            loss_function="Logloss",
            eval_metric="AUC",
            task_type="CPU",
            learning_rate=0.1,
            iterations=500,
            random_seed=2025,
            verbose=500,
            depth=7)#----- 欠采样模型 -----a = [i for i, x in enumerate(data_y.values) if x[0]==1]
all_1 = data_x.iloc[a]
all_y1 = pd.DataFrame(np.ones(all_1.shape[0]),columns=['isDefault'])
all_y0 = pd.DataFrame(np.ones(all_y.shape[0]),columns=['isDefault'])print(all_y1.shape)#for i in range(4): # 数据比1:4+i= 0all_0 = data_x.iloc[i*all_y1.shape[0]:(i+1)*all_y1.shape[0],:]
data_y_= pd.concat([all_y1,all_y0],axis=0)
data_x_ = pd.concat([all_1,all_0],axis=0)print(data_x_.shape,data_y_.shape,all_0.shape)for train, test in sk.split(data_x, data_y):
    x_train = data_x.iloc[train]
    y_train = data_y.iloc[train]
    x_test = data_x.iloc[test]
    y_test = data_y.iloc[test]    # 基准分类模型
    #clf = model.fit(x_train,y_train)#, eval_set=(x_test,y_test),cat_features=col)

    clf = imbens.ensemble.SelfPacedEnsembleClassifier(
        base_estimator = model,#基准分类模型 可以自定义,需要模型包含fit等方法,具体信息请查看参考文献3
        n_estimators = 5,
        random_state=49
    ).fit(x_train, y_train)
    yy_pred_valid=clf.predict_proba(x_test)[:,-1]    print('cat验证的auc:{}'.format(roc_auc_score(y_test, yy_pred_valid)))
    mean_score += roc_auc_score(y_test, yy_pred_valid) / n_folds
    y_pred_valid = clf.predict_proba(testA)[:,-1]
    answers.append(y_pred_valid)print('mean valAuc:{}'.format(mean_score))
cat_pre=sum(answers)/(n_folds) # 数据比1:4sub['isDefault']=cat_pre
sub.to_csv('金融预测.csv',index=False)
   

总结

  • 本项目主要以CBT算法为基分类器对比了几种常用的数据不平衡的处理效果,关于数据特征构建的知识可以查看开头参考文献。

数据的对比提交结果如下:

数据集 TIANCHI-贷款违约预测(ONLine)
比例 4.01(80W)
指标 AUC(线下五折验证/线上)
CaTBoost+SPE(5) 0.7348/0.7328
CaTBoost 0.7354/0.7338
CaTBoost+SMOTE 0.9334/0.6949
CaTBoost+过采样 0.7477/0.7346
CaTBoost+欠采样 0.7354/0.7338
CaTBoost+BalanceCascade(5) 0.6052/0.6032
CaTBoost+SPE(5)+过采样 0.7485/0.7347

imbalanced-ensemble库中包含了许多其他的非平衡算法,有兴趣的同学可以关注参考文献[3]

  • 此外,在训练中,随着基模型数量的增加此数据上的分类表现有所下降,这可能是由于此数据失衡仅4:1,不足以体现算法优势。

以上就是【金融风控系列】_[1]_贷款违约预测的详细内容,更多请关注其它相关文章!


# cad  # qq  # ai  # yy  # red  # descript  # type  # git  # 网站优化设计名词解释  # 高州seo优化关键词  # 开州区可靠网站建设  # 渭南富平seo优化  # 昆明长沙seo费用信誉保障  # 武汉推广招人网站排名榜  # 网站建设与管理课程学啥  # 康复学科营销推广方案  # 百度营销推广教程  # 浦江网站建设流程设计  # 安装包  # 对不  # 一键  # 数据处理  # 中文网  # 请查看  # 不平衡  # 自定义  # 线上  # 美图 


相关栏目: 【 企业资讯168 】 【 行业动态50218 】 【 媒体报道120512


相关推荐: 汽车的type-c接口是什么  万能表上的power是什么意思  固态硬盘内存如何查找  没网环境如何安装typescript  苹果16关闭哪些功能好  苹果16会升级哪些  苹果16更新了哪些版本  typescript入门要多久  苹果16充电方式有哪些  忐忑不安是什么意思  命令行如何运行c  固态硬盘4k如何看  如何使用命令行界面  哪些编程软件需要typescript  春运提前抢票攻略  市盈率292是什么意思  如何用好typescript  高市盈率是什么意思  如何管理员打开cmd命令行窗口  如何测试固态硬盘速度  如何通过命令行聊天  如何创建解压文件命令  ai如何重复使用上一命令  望远镜上power是什么意思  云笔记本电脑有什么用  如何激活固态硬盘  热水器没热水显示power是什么意思  酷狗音乐pc版的每日推荐在哪 酷狗音乐PC版每日推荐查找指南  为什么youtube音乐打不开  折叠屏手机哪款最好  单片机log怎么看  一分钟等于多少秒  破太岁是什么意思  eraser是什么意思  春运抢票技巧攻略  光刻机分类有哪些品牌的  夸克为什么老是投屏失败  笔记本如何选择固态硬盘  哪个品牌有折叠屏手机卖  苹果16新增哪些功能  小屏折叠屏手机有哪些  电动车充电器上的power是什么意思  苹果16系统有哪些缺陷  春运车站抢票和网上抢票  手机nfc功能功能是什么意思  typescript有哪些版本  课程伴侣登不上怎么办  rxjs和typescript什么意思  360f4怎么取消百变壁纸  市盈率为负值是什么意思