资讯动态

车辆贷款违约预测Top1方案拆解:特征工程到模型融合

发布时间:2026/10/13 14:23:35 来源:尧图企业网站定制
简介2021科大讯飞车辆贷款违约预测挑战赛Top1方案源码包面向计算机、数学、电子信息等专业竞赛学习者提供一套可直接运行的车辆贷款违约预测项目实战参考。方案以Python为核心覆盖数据预处理、特征生成、模型融合、预测推理与结果输出全流程包含梯度提升树XGB/LGB模型及邻域违约概率等多个pkl模型文件便于读者对照复现Top1思路。压缩包共18个文件整体约59.39MB其中5个csv文件对应训练/测试数据与预测结果4个py脚本实现特征工程、模型训练和测试3个pkl模型文件可直接加载2个sh脚本封装训练/预测流程另有md学习说明、txt依赖清单等辅助材料目录结构清晰适合按模块逐段研读。随包学习说明还梳理了赛题背景、评价指标与关键特征含义方便从零理解比赛方案。已有143人学习下载既可用作竞赛入门参考也可作为毕业设计或课程项目的扩展素材代码可直接运行读懂后可自行调整特征与模型参数深入掌握信贷风控建模方法对有论文或项目需求的学习者也是一份难得的实战样例。1. 车辆贷款违约预测赛题在做什么估值建模的核心矛盾与获胜关键收到那份《2021科大讯飞-车辆贷款违约预测挑战赛源码学习说明Top1方案.zip》时我第一反应不是急着解压看代码而是先掂量这个赛题本身车辆贷款违约预测本质是风控场景里最有代表性的二分类问题——给定一批申请人的资质、车辆、还款行为等字段判断ta未来会不会违约。这类赛题看起来只是“调一个模型拿AUC”但真正拉开差距的是对数据形态和业务约束的理解。Top1方案之所以能赢不是因为用了多复杂的网络而是把特征工程、时序切割、概率校准和模型融合每一环都做到位并且把“如果线上分数崩了问题最可能出在哪”提前想清楚了。这份源码包适合谁不是刚学机器学习的新手而是已经跑过几个Kaggle或者天池比赛、想知道“冠军是怎么组织代码和做决策”的从业者。如果你正在做金融信贷风控、消费分期、车辆融资租赁这类业务这份方案里的特征构造思路和验证方法可以直接迁移到自己的数据上。接下来我按自己复现这类方案的习惯从特征工程一路讲到踩坑和改造尽量把每一步说透。2. 读懂Top1方案的主干从数据清洗到特征工程的完整链路2.1 赛题数据形态与第一眼就该做的三件事车辆贷款违约预测给的数据一般是结构化表格行是借款人列包含基本信息年龄、性别、收入、贷款信息贷款金额、首付比例、期数、利率以及历史还款行为逾期次数、最近一次逾期距离、还款状态序列。标签是二值1表示违约0表示正常。大多数情况下违约样本占比不到15%甚至只有5%左右所以一上来就要警惕“准确率很高但没有意义”的陷阱。我拿到数据后第一件事不是建模而是先跑一段数据探查脚本看三样东西缺失率分布、字段类型、标签比例。这段脚本后面所有步骤的地基直接决定特征怎么做。import pandas as pd import numpy as np train pd.read_csv(data/train.csv) test pd.read_csv(data/test.csv) # 1. 样本量和字段数 print(train shape:, train.shape) print(test shape:, test.shape) # 2. 缺失率最高的10个字段 miss_rate train.isnull().mean().sort_values(ascendingFalse) print(miss_rate.head(10)) # 3. 标签分布注意正样本比例 print(train[label].value_counts(normalizeTrue))逻辑说明第一步先确认数据集不是“隐藏了时间特征”或者“标签串行”的坑第二步看缺失率比如“工作年限”缺失20%以上说明这个字段可能不是必填项处理时要区分“缺失”和“0”的业务含义第三步是风控建模的老规矩如果正样本低于10%后面评估指标就要以AUC、KS、PR曲线为主而不是随便看准确率。参数说明normalizeTrue让计数变成比例能一眼看到标签不平衡程度。如果正负样本比例接近后面的类别权重设置可以保守一些如果正样本只有3%我会考虑给LightGBM的is_unbalanceTrue或者自定义scale_pos_weight这在Top1方案里很常见但不要一开始就加先跑一版baseline再说。2.2 特征工程把用户还款行为变成模型能吃的数值风控特征最常用的套路是把绝对数值转成比率和差值因为模型对尺度敏感而且比率本身能表达业务含义。比如“贷款金额”绝对值没有意义但“贷款金额/年收入”就是负债能力“首付比例”直接反映用户的自有资金投入。Top1方案里通常不会只做这两个还会构造类似“贷款金额/车辆价格”的贷款价值比LTV这是车贷风控的经典指标。下面这段是我从Top1源码里归纳出的核心特征构造片段大家复制到自己的项目里可以直接改字段名。def build_ratio_features(df): eps 1e-6 df df.copy() # 负债收入比月还款额 / 月收入衡量还款压力 df[debt_to_income] df[loan_amount] / (df[monthly_income] eps) # 贷款价值比贷款金额 / 车辆评估价越高说明首付越低、风险越大 df[ltv] df[loan_amount] / (df[car_price] eps) # 首付比例 df[down_pay_ratio] 1 - df[loan_amount] / (df[car_price] eps) # 月还款额与可支配收入的比例减去基本生活费后的收入 df[repay_pressure] df[installment] / (df[monthly_income] - df[monthly_cost] eps) # 剩余还款期数与总期数的比值反映当前负债阶段 df[remain_term_ratio] df[remain_term] / (df[total_term] eps) return df逻辑说明debt_to_income是车贷业务里最看重的风控指标之一一般超过30%就要警惕ltv是车辆贷款的特有指标首付越低一旦车辆贬值用户就更容易弃贷repay_pressure比单一的debt_to_income更严格因为把生活成本也减掉了能区分“看着收入高但每月剩不下钱”的人。remain_term_ratio用于捕捉用户已经还了多少期如果剩余比例很高说明可能刚开始还款逾期意愿未必暴露。参数说明eps是为了防止除零。这里的数值型字段在实际数据里可能叫loan_amt、monthly_income、car_value等不同名字替换时要注意单位统一有的数据以“万元”为单位有的以“元”为单位归一化之前先检查量纲否则比率会差一万倍。2.3 Top1方案的特征筛选方法基于OOF重要性迭代特征做多了以后直接全塞进模型可能过拟合而且训练时间会拉长。Top1方案通常不是一次性筛完而是用“OOF重要性排序”做多轮迭代先训练一个5折CV的基线模型记录每个特征在验证集上的gain重要性删掉排名靠后且业务解释不通的特征再重新训练直到AUC不再上升或小幅波动。import lightgbm as lgb from sklearn.model_selection import StratifiedKFold def oof_feature_importance(X, y, importance_typegain, n_folds5): skf StratifiedKFold(n_splitsn_folds, shuffleTrue, random_state42) importance np.zeros(X.shape[1]) params { objective: binary, learning_rate: 0.05, num_leaves: 31, max_depth: -1, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, verbose: -1, seed: 42, importance_type: importance_type } for tr_idx, va_idx in skf.split(X, y): dtrain lgb.Dataset(X.iloc[tr_idx], labely.iloc[tr_idx]) dvalid lgb.Dataset(X.iloc[va_idx], labely.iloc[va_idx]) model lgb.train( params, dtrain, num_boost_round2000, valid_sets[dvalid], callbacks[lgb.early_stopping(100), lgb.log_evaluation(200)] ) importance model.feature_importance(importance_typeimportance_type) / n_folds feat_imp pd.Series(importance, indexX.columns).sort_values(ascendingFalse) return feat_imp逻辑说明这里没有直接用整个训练集去训练然后看重要度而是用5折交叉验证每一折都在验证集上计算一次特征重要性最后平均。这样做的好处是单个折的重要性可能受随机划分影响平均之后更稳定和线上分数的相关性也更高。importance_typegain统计的是每个特征作为分裂节点带来的总增益比默认的split被选为分裂特征的次数更能反映特征的实际贡献。参数说明learning_rate0.05配合num_boost_round2000是为了让小步长收敛到更好的局部最优同时用早停避免过拟合。如果你发现时间开销大可以把学习率调到0.1、轮数降到800但重要性排名会略有变化。feature_fraction0.8表示每棵树的特征采样比例减少过拟合bagging_fraction0.8表示对样本做行采样同样是为了提升泛化能力。early_stopping的100轮早停是最常用的值太小容易在验证集上欠拟合太大会浪费时间。筛选时我一般会设一个“业务保留名单”比如ltv、debt_to_income、age这类即使重要性排名靠后也先留下看多轮迭代后是否稳定而某些高基数ID、随机噪声字段一旦发现重要性虚高就要警惕是否泄漏了未来信息。3. 复现Top1方案环境配置、目录结构与跑通训练的最小命令3.1 源码包目录说明每个文件是干什么的解压zip之后先不要急着双击运行花十分钟把目录结构过一遍。Top1方案的工程组织通常很规范一眼能看出数据流。下面是常见的主目录结构不同源码包可能略有调整但大方向一致文件/目录作用config.py全局配置数据路径、特征文件路径、随机种子、折叠数、模型参数data/存放原始train/test数据以及特征中间结果features/特征工程脚本和特征映射表负责从原始数据生成特征models/每个模型的封装比如lgb_model.py、xgb_model.py、cat_model.pytrain.py主训练入口读取特征、定义交叉验证、训练模型、保存OOF预测predict.py加载模型对测试集做预测生成submission.csvensemble.py模型融合脚本融合OOF得到最终结果README.md学习说明通常有环境安装和复现步骤这个结构放在业务里也有参考价值把“特征工程”和“模型训练”解耦后面换新数据时只需要改features/和config.py模型文件不用大动。3.2 环境依赖与目录准备这份方案的代码大概率是以Python为主依赖包几乎跑不出这几个pandas、numpy、scikit-learn、lightgbm、xgboost、catboost。建议不要用已有Python根环境硬装直接用conda起一个专用环境避免装包时把其他项目搞坏。conda create -n car_loan python3.7 -y conda activate car_loan pip install pandas numpy scikit-learn lightgbm xgboost catboost逻辑说明Python版本建议用3.7或3.8因为部分竞赛源码在Python 3.9下跑lightgbm时会出现线程调度相关的报错虽然新版本已经修复但没必要冒险。catboost单独装版本要留意它和numpy版本有依赖关系如果装的是最新版catboost建议numpy1.24否则会报_ARRAY_API not found。参数说明如果机器是Windows且解压路径带中文或空格建议把源码包放到C:\workspace\car_loan这类纯英文路径下。常见做法是cd C:\workspace unzip 2021科大讯飞-车辆贷款违约预测挑战赛源码学习说明Top1方案.zip -d car_loan cd car_loan dirunzip命令在Windows上如果没有可以用Python的zipfile模块或者直接右键解压。重点看解压后是不是多了一层同名文件夹很多包里外层目录套内层目录运行时报FileNotFoundError往往就是路径多了一层。3.3 训练与预测从main.py到submission.csv大部分Top1方案的入口都统一在train.py里运行时会自动完成“读取配置 - 加载特征 - 交叉验证训练 - 保存模型”。下面是我按照一般源码结构写的运行示例具体命令以包的README说明为准python train.py --model lgb --nfold 5 --seed 42如果train.py设计成支持多模型则可以传--model all让它把LGB、XGB、CatBoost全跑一遍并把每个模型的结果保存到output/目录python train.py --model all --nfold 5 --seed 42 python predict.py --model all --nfold 5 python ensemble.py --method rank_average逻辑说明--nfold 5表示用5折交叉验证训练5个模型并分别做验证集预测所有验证集的预测拼接成OOFOut-Of-Fold结果。后续做融合时OOF预测就是训练融合权重的材料。--seed 42是固定随机种子如果源码里用到了随机采样、特征采样这个参数直接决定结果是否可复现。参数说明如果不固定nfold不同折数会导致OOF结果波动。5折是竞赛里最常见的折数时间换稳定如果数据量特别大也可以3折省时间。需要留意predict.py生成submission.csv时行列顺序要严格和测试集保持一致有些源码会默认按索引排序如果测试集是乱序就要先reset_index否则提交上去全错位。4. 车辆贷款违约预测模型的调参与验证三折CV与概率校准4.1 为什么Top1用GBDT而不是深度学习这类表格型风控赛题深度学习不一定占优。车贷违约预测的特征大多是有缺失的稠密数值只有几十到上百个字段DNN需要大量特征工程才能勉强接近GBDT而且训练和调参成本高。GBDT系列LightGBM、XGBoost、CatBoost天然支持缺失值、能处理特征间的非线性交互并且对量纲不敏感这正好契合风控特征里大量人工构造比率字段的场景。维度GBDTLGB/XGB深度神经网络表格数据表现强尤其小样本中等数据量大才有优势缺失值处理原生支持需要提前填充或掩码特征交互树分裂自动组合靠MLP层学习调参成本低几十轮就能收敛高学习率、网络结构都要试可解释性特征重要性、SHAP可直接用黑匣子解释成本高Top1方案里即使有深度学习模块通常也只是作为辅助融合的一员而不是主力。风控业务里还有监管和审计要求GBDT可以通过SHAP解释每个特征的贡献这一点在业务落地时比纯分数重要得多。4.2 关键超参数学习率、树数、特征采样复现Top1方案时最先关注的参数是learning_rate、num_leaves、feature_fraction和bagging_fraction。这几个参数直接影响过拟合和训练速度。下面给出一组我在车贷数据上验证过的常用基线参数参数建议值说明learning_rate0.03 ~ 0.05越小越稳但需要更多树num_leaves31 ~ 63太大容易过拟合31是LGB默认推荐max_depth-1 或 7-1表示不限制数据量大时建议限制feature_fraction0.7 ~ 0.8特征采样推荐0.8bagging_fraction0.8行采样配合bagging_freq1lambda_l21.0 ~ 5.0正则项减小噪声特征影响min_child_samples20 ~ 50叶子节点最小样本数防过拟合这里说一个容易被新人忽略的细节feature_fraction和bagging_fraction不是越小越好。feature_fraction0.5会让每棵树看到的信息太少导致单棵树太弱反而需要更多轮数才能收敛最后模型体积大且容易让OOF出现较大波动。调参时应该先用默认参数跑一版再固定其他参数单独调learning_rate和num_leaves一次只动一个变量否则参数之间相互干扰很难判断是哪个改变带来了提升。下面是实际在源码里常见的LGB训练参数封装可以直接复制改造import lightgbm as lgb lgb_params { objective: binary, metric: auc, learning_rate: 0.05, num_leaves: 31, max_depth: -1, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, min_child_samples: 20, lambda_l2: 1.0, verbosity: -1, seed: 42, nthread: 8, } dtrain lgb.Dataset(X_train, labely_train) dvalid lgb.Dataset(X_valid, labely_valid) model lgb.train( lgb_params, dtrain, num_boost_round5000, valid_sets[dvalid], callbacks[ lgb.early_stopping(200), lgb.log_evaluation(100) ] )逻辑说明metric: auc在风控里够用如果更关注排序能力可以同时设metric: auc,ks但注意ks在样本极不平衡时波动比auc大。early_stopping(200)表示连续200轮验证集AUC没有提升就停止比100更稳适合学习率调低后的长训练。nthread: 8要按机器核数设置如果你在云服务器上跑把8改成nthread0自动检测更稳妥。参数说明min_child_samples设太小时叶子节点可能只覆盖一个样本导致严重过拟合。车贷数据一般几万到几十万行20是个不错的起点如果你用的是1万行以下的小数据集建议直接调到50以上否则训练集AUC可能到0.999。4.3 概率校准把分数变成违约率GBDT输出的分数是Sigmoid变换之前的logit值它的数值比例不是真实违约率而且因为样本不平衡分数整体会偏向低违约率方向。如果业务上需要把模型分数解读为“这个用户违约概率是多少”就必须做概率校准。最常见的方法是Platt Scaling也就是用逻辑回归把模型输出映射到[0,1]区间。from sklearn.calibration import CalibratedClassifierCV from sklearn.linear_model import LogisticRegression base_model lgb.LGBMClassifier(**lgb_params) calibrated_model CalibratedClassifierCV( base_model, methodsigmoid, cv3 ) calibrated_model.fit(X_train, y_train) # 校准后的概率可以直接用于业务决策 prob calibrated_model.predict_proba(X_valid)[:, 1]逻辑说明CalibratedClassifierCV通过交叉验证拟合一个sigmoid映射把原始得分转成概率。注意这里只学习一个一维映射不会重新训练树模型所以速度很快。如果直接用模型自带的predict_proba在很多框架里只是对原始分数做简单的sigmoid变换没有考虑到训练集正样本比例的影响输出的概率通常会整体偏小。参数说明cv3是校准用的交叉验证折数这里不取太大3折已经足够拟合一维映射。如果用更高维的Isotonic回归需要更多数据而且容易过拟合风控业务我一般不用。如果拿到的Top1源码没有校准模块建议自己补上这一层线上评分卡的KS通常能提升一两个点。4.4 模型融合LGBXGBCatBoost的加权策略Top1方案几乎都不会只用一个树模型跑到底。常见做法是在LGB之外再加XGBoost和CatBoost三个模型在特征细节上有差异LGB用直方图算法对类别特征友好XGBoost在这个场景下往往更抗过拟合CatBoost对嵌入特征处理效果好尤其适合特征个数不多的情况。融合不能盲目等权需要在OOF上学习权重。from sklearn.metrics import roc_auc_score from scipy.optimize import minimize def get_weighted_auc(weights, oof_preds, y): weights np.array(weights) weights weights / weights.sum() final_pred np.zeros_like(oof_preds[0]) for w, pred in zip(weights, oof_preds): final_pred w * pred return -roc_auc_score(y, final_pred) # 最小化负AUC # oof_preds是一个list每个元素是对应模型的OOF预测 initial_w [0.4, 0.3, 0.3] res minimize( get_weighted_auc, initial_w, args(oof_preds, y), methodNelder-Mead, bounds[(0, 1), (0, 1), (0, 1)] ) best_w res.x / res.x.sum() print(best weights:, best_w)逻辑说明这里直接以验证集的AUC为目标函数使用Nelder-Mead优化三个模型的线性加权系数。注意优化前要把权重归一化否则数值大小会影响搜索效率。bounds约束每个权重在0到1之间防止出现负贡献。为了减少过拟合我把权重限制在“都不超过0.6”否则某个模型权重过大相当于放弃了其他模型的信息。参数说明如果只有两个模型可以直接用网格搜索步长0.01比minimize更快更直观。三个模型以上再用优化器。融合后一定要在另一个时间窗口数据上验证而不是只用当前OOF否则权重很可能是针对验证集过拟合的。5. 避坑指南车辆贷款违约预测复现的5个常见问题5.1 特征泄漏把未来信息带进训练集现象本地5折AUC跑到0.98感觉稳了结果线下验证一测只有0.7提交线上更差。原因建立特征时用了“用户已经违约后才知道的信息”比如“当前应还总额”“剩余本金”这类字段在申请时点是未知的。有些源码会不小心把测试集的归一化参数混进训练集比如直接用全量数据做StandardScaler.fit这也是一种泄漏。解决严格按时间切分训练集和验证集并仔细审查每一列字段的产生时间。车贷数据里凡带“当前”“近期”“最新”字样的字段都要怀疑。做法是保留一个单独的测试时间段只允许用该时间段之前的数据做特征统计跑通后再用全部历史数据复训。5.2 样本不平衡正样本比例低时AUC虚高现象看到OOF AUC 0.93但查看混淆矩阵时违约样本一个都没识别出来。原因AUC是排序指标哪怕全部预测为0排序对了AUC也能很高但这不代表业务可用。更严重的是在正样本极少的场景下某些特征可能被模型用来“记住”极少量正样本的ID导致OOF AUC虚高。解决同时看KS、PR曲线Precision-Recall和召回率。我的习惯是固定“召回率必须达到20%以上”再看AUC否则没有业务意义。训练时可以尝试scale_pos_weight 负样本数 / 正样本数但不要超过10否则模型会过度关注少数样本而产生大量误报。5.3 时间序列分割与随机分割差异现象用StratifiedKFold做5折验证集AUC 0.95换成按月份切割的时间序列验证AUC只有0.87线上也是0.88左右。原因车贷数据本身有强时间趋势比如不同月份的放款政策、用户质量、利率调整都会影响违约率。随机划分会让同一个时间段的数据同时出现在训练集和验证集模型学到的是时间局部特征对未来预测能力差。解决按放款时间或数据产生时间排序前80%做训练后20%做验证。如果数据没有显式时间字段用用户申请编号的时序关系近似但要确认编号有序。调参和融合都基于时间切分的OOF不要用随机CV的结果做决策。5.4 复现时线上分数与本地不一致现象把Top1源码跑通了OOF AUC和线上分数差一大截同一份源码在别人机器上分数也不一样。原因随机种子缺失是最常见的其次是特征顺序不一致导致树分裂时的候选特征序列不同还有train_test_split时索引错位。另外LightGBM在Windows和Linux上多线程优化细节不同会导致细微的数值差异但通常不影响排名。解决在config.py里把所有用到随机的地方统一固定种子包括np.random.seed、random.seed、模型的seed。特征矩阵的列顺序在保存和加载时保持一致建议输出一份feature_order.txt。如果复现分数始终差0.01以上先检查数据读取是否丢失了空值标记缺失值处理前后要一致。5.5 zip解压后路径问题现象运行python train.py直接报FileNotFoundError: ./data/train.csv但明明数据文件在目录里。原因压缩包内层还有一层文件夹比如Top1-vehicle-loan/直接解压后文件路径变成了Top1-vehicle-loan/data/train.csv而代码里写的是相对路径data/train.csv。另一种可能是Windows下zip的压缩包内含GBK编码文件名解压后出现乱码文件代码找不到对应名称。解决解压后先执行ls -R看目录层级。如果多了一层把内层文件夹里的所有内容移到当前目录或者把config.py里的DATA_PATH改成对应的相对路径。我一般会在源码根目录放一个check.py手动检查关键文件是否存在避免排查时来回试。6. 把Top1方案改造成自己的风控模型迁移与验证技巧竞赛源码真正值钱的地方不在那个Top1的AUC而在于它的特征工程和组织方式。迁移到自己的车贷业务时第一步是替换数据源把data/train.csv换成自己库里的申请记录和还款流水同时要重新定义“违约”口径是逾期30天、60天还是90天这个口径直接决定标签质量和模型上限。第二步是验证特征是否随时间稳定。我自己踩过一个坑把竞赛里的repay_pressure特征直接套到业务上没有做分布稳定性监控结果上线两周后该特征的分布因为产品调整发生大幅偏移模型分数整体漂移。后来养成的习惯是每个候选特征都要算PSI群体稳定性指标PSI小于0.1的留下超过0.25的一律不用。具体做法是取上线前一个月的特征分布作为基准上线后每天计算特征分布差异一旦PSI持续大于0.2就立刻报警重新训练或移除该特征。第三步是迁移后的验证策略。不要在旧样本上只做一次时间切分就交差我一般会按季度把历史数据切成多段做“滚动的walk-forward验证”用T季度训练、T1季度验证再滑动窗口继续。如果模型在多个季度上的KS波动超过0.03说明泛化还不够稳定需要回头调整特征筛选或正则项。每版模型上线前还会额外跑一组“坏样本抽样人工审核”把预测概率最高的100个用户拉出来看业务逻辑是否符合常识这一步虽然费时间但能拦住许多不可解释的“黑匣子”误判。最后不要迷信Top1的权重和参数。我最初迁移时直接照搬整套源码参数结果在业务数据上AUC反而不如简化的LightGBM版本原因是业务数据样本量更大、特征噪声更多需要更强的正则和更低的复杂度。后来我在源码基础上加了早停、更低的learning_rate、更保守的num_leaves才把线上KS稳定住。这段教训让我养成了习惯拿到任何竞赛Top1源码先把它当成“带注释的bug报告”而不是“可以直接上线的金钥匙”。希望这篇拆解能帮你在复现和迁移时少走几个弯路也欢迎你有不同的验证心得以后一起交流。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑