Kaggle数据集-贷款违约预测:从数据清洗到模型优化的实战解析
1. 初识Kaggle贷款违约数据集第一次接触Kaggle上的Loan_Default数据集时我被它的规模吓了一跳——14万条记录34个特征列。这个数据集记录了银行过去贷款客户的详细信息从收入水平、职业类型到贷款金额、还款记录应有尽有。最关键的Status列标记了客户是否违约1表示违约0表示正常这正是我们要预测的目标变量。仔细查看数据分布时发现一个棘手问题违约客户Status1只有36,639条而非违约客户Status0有112,031条典型的类别不平衡问题。如果不处理模型很可能会倾向于预测所有客户都不会违约因为这样也能获得不错的准确率——但这显然不是银行想要的结果。数据质量方面也存在挑战多个关键字段存在大量缺失值比如property_value房产价值字段缺失率高达30%。更麻烦的是某些特征间存在强相关性比如rate_of_interest和Interest_rate_spread这两个利率相关特征的相关系数达到0.9以上这会导致所谓的多重共线性问题。# 初始数据概览 import pandas as pd data pd.read_csv(Loan_Default.csv) print(f总样本数: {len(data)}) print(f违约样本数: {len(data[data[Status]1])}) print(f非违约样本数: {len(data[data[Status]0])})2. 数据清洗实战技巧2.1 处理缺失值的艺术面对缺失值我的第一反应是直接删除——但这是个危险的决定。测试发现如果简单删除所有含缺失值的行会丢失近40%的违约样本这对本就稀缺的违约数据简直是雪上加霜。更合理的做法是分步骤处理先删除明显无用的列ID、年份等然后删除缺失率超过25%的特征列最后对剩余缺失值使用中位数数值型或众数类别型填充# 删除高缺失率列 high_null_cols [rate_of_interest, Interest_rate_spread, Upfront_charges, property_value] data data.drop(high_null_cols, axis1) # 删除无用ID列 data data.drop([ID, year], axis1) # 用中位数填充剩余数值型缺失值 num_cols data.select_dtypes(include[int64,float64]).columns for col in num_cols: data[col].fillna(data[col].median(), inplaceTrue)2.2 特征相关性分析与处理绘制热力图是发现特征相关性的好方法。我发现有几个特征对几乎完全相关相关系数0.95比如loan_amount和approved_loan_amount。保留这些冗余特征不仅增加计算量还可能降低模型性能。import seaborn as sns import matplotlib.pyplot as plt # 计算相关系数矩阵 corr data.corr() # 绘制热力图 plt.figure(figsize(14,14)) sns.heatmap(corr, annotTrue, cmapReds, squareTrue, fmt.1f) plt.show() # 删除高相关性特征 data data.drop([approved_loan_amount], axis1)3. 特征工程深度优化3.1 类别特征编码实战数据集包含多个类别特征如loan_purpose贷款目的、occupation职业等。对于基数不同取值数量小于10的类别特征我使用独热编码对于基数大的特征如occupation有50种职业则采用目标编码Target Encoding用该类别下违约率的平均值来替代原始类别值。from category_encoders import TargetEncoder # 小基数类别特征 - 独热编码 small_cardinality [loan_purpose, gender] data pd.get_dummies(data, columnssmall_cardinality) # 大基数类别特征 - 目标编码 encoder TargetEncoder(cols[occupation]) data[occupation_encoded] encoder.fit_transform(data[occupation], data[Status]) data data.drop([occupation], axis1)3.2 创造有业务意义的特征原始特征往往不够需要结合领域知识创造新特征。例如债务收入比DTI月债务还款总额/月收入贷款价值比LTV贷款金额/抵押物价值信用历史密度信用账户数量/信用历史长度# 创建新特征 data[DTI] data[total_monthly_debt] / data[income] data[LTV] data[loan_amount] / data[collateral_value] data[credit_density] data[number_credit_lines] / data[credit_history_length]4. 解决类别不平衡的五大策略4.1 过采样与欠采样结合我尝试了SMOTE合成少数类过采样技术与随机欠采样的组合。SMOTE通过在特征空间中插值来创造新的少数类样本而随机欠采样则减少多数类样本。关键是要在训练集上应用这些技术测试集保持原始分布以评估真实性能。from imblearn.combine import SMOTEENN # 划分训练测试集 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 应用SMOTEENNSMOTEEdited Nearest Neighbors smote_enn SMOTEENN(random_state42) X_resampled, y_resampled smote_enn.fit_resample(X_train, y_train)4.2 代价敏感学习另一种思路是不改变数据分布而是让模型更关注少数类。通过设置class_weight参数让误判违约客户的代价是误判正常客户的5倍这个比例可以根据业务需求调整。from sklearn.ensemble import RandomForestClassifier # 代价敏感随机森林 model RandomForestClassifier( class_weight{0:1, 1:5}, # 误判违约的代价是误判正常的5倍 n_estimators200, max_depth10, random_state42 ) model.fit(X_train, y_train)5. 模型选择与性能对比5.1 四大经典模型PK我测试了逻辑回归、决策树、随机森林和XGBoost四种模型使用5折交叉验证评估。评估指标不仅看准确率更关注召回率捕捉违约客户的能力和AUC-ROC整体区分能力。模型准确率召回率(违约类)AUC-ROC训练时间(s)逻辑回归0.820.680.813决策树0.850.720.835随机森林0.870.790.89120XGBoost0.880.810.91605.2 为什么XGBoost表现最佳XGBoost的优异表现源于几个设计内置处理类别不平衡通过scale_pos_weight参数自动调整类别权重正则化防止过拟合gamma参数控制分裂的最小损失减少特征重要性自动计算便于特征选择from xgboost import XGBClassifier # 计算类别权重 scale_pos_weight len(y_train[y_train0]) / len(y_train[y_train1]) xgb XGBClassifier( scale_pos_weightscale_pos_weight, learning_rate0.1, max_depth6, subsample0.8, colsample_bytree0.8, eval_metricauc, n_estimators200 ) xgb.fit(X_train, y_train)6. 超参数调优实战6.1 网格搜索与随机搜索对比我对比了GridSearchCV网格搜索和RandomizedSearchCV随机搜索两种调优方式。对于XGBoost这种参数空间大的模型随机搜索效率更高——用20%的时间就能达到网格搜索95%的效果。from sklearn.model_selection import RandomizedSearchCV from scipy.stats import uniform, randint param_dist { max_depth: randint(3, 10), learning_rate: uniform(0.01, 0.3), subsample: uniform(0.6, 0.4), colsample_bytree: uniform(0.6, 0.4), gamma: uniform(0, 0.5) } random_search RandomizedSearchCV( xgb, param_distributionsparam_dist, n_iter50, scoringroc_auc, cv5, verbose1, n_jobs-1 ) random_search.fit(X_train, y_train)6.2 贝叶斯优化进阶更高级的优化方法是贝叶斯优化它基于已有评估结果智能选择下一组参数。使用BayesSearchCV可以比随机搜索更快找到最优参数。from skopt import BayesSearchCV from skopt.space import Real, Integer search_spaces { learning_rate: Real(0.01, 0.3, priorlog-uniform), max_depth: Integer(3, 10), subsample: Real(0.6, 1.0), colsample_bytree: Real(0.6, 1.0), gamma: Real(0, 0.5) } bayes_search BayesSearchCV( xgb, search_spaces, n_iter30, cv5, verbose1, n_jobs-1 ) bayes_search.fit(X_train, y_train)7. 模型部署与业务应用7.1 阈值调整策略默认的0.5分类阈值对不平衡数据往往不是最优选择。通过分析精确率-召回率曲线我发现将阈值调整为0.3可以在保持合理精确率的同时将违约客户召回率从0.81提升到0.89。from sklearn.metrics import precision_recall_curve # 获取预测概率 y_proba xgb.predict_proba(X_test)[:, 1] # 计算不同阈值下的精确率和召回率 precisions, recalls, thresholds precision_recall_curve(y_test, y_proba) # 找到召回率0.9时的最佳阈值 optimal_idx np.argmax(recalls 0.9) optimal_threshold thresholds[optimal_idx]7.2 模型解释性提升在金融领域模型不能是黑箱。SHAP值可以直观展示每个特征对预测的影响程度。例如我们发现DTI债务收入比是最重要的预测因素——当DTI超过0.4时违约概率显著上升。import shap # 计算SHAP值 explainer shap.TreeExplainer(xgb) shap_values explainer.shap_values(X_test) # 可视化 shap.summary_plot(shap_values, X_test, plot_typebar)8. 项目复盘与经验总结经过两个星期的迭代优化最终模型的测试集AUC达到0.92违约类召回率0.89。这意味着系统能捕捉近90%的高风险客户同时保持85%的整体准确率。几个关键经验数据质量决定上限花在数据清洗上的时间占项目60%但回报最大模型复杂度要适度尝试过复杂神经网络效果反而不如调优好的XGBoost业务指标优先最终选择模型时技术指标要让位于业务需求如高风险客户召回率这个项目让我深刻体会到好的数据科学项目需要技术能力与业务理解的完美结合。每次当模型表现不佳时回到数据本身、深入理解业务背景往往能找到突破方向。