ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python机器学习实战:个人信用评估模型从数据清洗到评分卡全流程

Python机器学习实战:个人信用评估模型从数据清洗到评分卡全流程 简介这份资源面向Python机器学习入门与进阶学习者、金融风控方向的学生及课程设计开发者围绕个人信用评估与贷款违约预测任务提供一套可复现的完整项目方案。资源包共82个文件以62张png可视化图表、13个py脚本为主另含2份csv数据、1份docx设计报告及md说明等压缩包约5.16MB结构清晰便于按模块查阅。数据集取自阿里天池贷款违约预测比赛总量超120万条、47列变量含15列匿名特征并划分80万训练集与20万测试集A、B对employmentTitle、purpose、postCode、title等字段做了脱敏处理。读者可获取从数据校验、缺失填充、编码处理到网格搜索调参、PCA降维、相关性热力图与分布可视化的一整套脚本配合设计报告理解建模思路与特征工程细节适合作为课程设计、竞赛练手或风控建模的参考模板。目前已有740人学习下载。1. 个人信用评估模型从一份 zip 到一个能跑通的评分卡个人信用评估这件事听起来像银行风控部门的专属活儿但真落到代码层面它就是一个标准的二分类问题给你一批借款人的历史数据预测这个人未来会不会违约。标题里的「Python 通过机器学习实现对个人信用评估.zip」本质上就是把这条链路打包成了一个可复现的工程数据清洗、特征工程、模型训练、评分输出。适合谁看手上有类似数据集、想用 Python 机器学习跑通一个完整项目的人尤其是做数据分析、风控建模、或者课程设计需要交付物的从业者和学生。热搜里「python 机器学习」「机器学习项目」「python 数据分析与可视化」这几个词恰好对应了这条链路上的三个关键环节。我见过太多人卡在第一步——数据拿到手不知道怎么处理缺失值和类别变量模型跑出来 AUC 只有 0.6 还以为是算法不行。这篇就把这条链路拆开从环境配置到模型调参把能抄的代码和会翻车的地方都讲清楚。2. 数据到手先别急着 fit信用数据的清洗与特征工程2.1 信用评估数据集长什么样先做一次体检个人信用评估的原始数据通常包含几类字段借款人基本信息年龄、职业、婚姻状况、信贷历史贷款金额、期限、还款记录、以及标签列是否违约。这类数据的特点是类别不平衡严重违约样本往往只占 5% 到 15%而且缺失值和异常值扎堆。拿到数据第一件事不是df.describe()而是先看每一列的类型和缺失比例。import pandas as pd import numpy as np df pd.read_csv(credit_data.csv) # 体检类型、缺失率、唯一值数量 check pd.DataFrame({ dtype: df.dtypes, missing_rate: df.isnull().mean().round(4), nunique: df.nunique() }) print(check.sort_values(missing_rate, ascendingFalse))这段代码输出三列关键信息字段类型决定后续怎么编码缺失率决定是删还是填唯一值数量帮你区分连续变量和类别变量。缺失率超过 60% 的列我一般直接砍掉因为填充引入的噪声比信息量还大。唯一值只有个位数的列基本就是类别变量需要做编码。参数上没什么好调的但有一个判断标准值得记住如果某一列的缺失率在 20% 到 60% 之间先别删用业务逻辑判断它是不是「缺失本身就有意义」。比如「担保人信息」为空可能恰恰说明这笔贷款没有担保这本身就是个风险信号。2.2 缺失值填充和类别编码三个必须做的决策信用数据里最常见的两类字段是数值型和类别型处理方式完全不同。数值型字段比如月收入、贷款金额用中位数填充比均值稳因为收入这类字段长尾分布严重均值会被极端值拉偏。类别型字段比如职业、学历用众数填充或者单独归为「未知」类。from sklearn.impute import SimpleImputer from sklearn.preprocessing import LabelEncoder, StandardScaler # 数值列中位数填充 标准化 num_cols df.select_dtypes(include[np.number]).columns.tolist() num_cols.remove(default) # 标签列不参与 num_imputer SimpleImputer(strategymedian) df[num_cols] num_imputer.fit_transform(df[num_cols]) scaler StandardScaler() df[num_cols] scaler.fit_transform(df[num_cols]) # 类别列众数填充 标签编码 cat_cols df.select_dtypes(include[object]).columns.tolist() cat_imputer SimpleImputer(strategymost_frequent) df[cat_cols] cat_imputer.fit_transform(df[cat_cols]) for col in cat_cols: le LabelEncoder() df[col] le.fit_transform(df[col].astype(str))逻辑说明SimpleImputer的strategy参数有三个选项median适合偏态分布most_frequent适合类别变量mean只在近似正态分布时用。StandardScaler对逻辑回归和 SVM 是必须的对树模型不是必须的但也没坏处。LabelEncoder把字符串转成整数注意它会产生「序关系」的假象如果类别之间没有大小关系更严谨的做法是OneHotEncoder但信用数据里职业、学历这类字段往往有隐含排序标签编码反而能保留信息。这里有个容易翻车的地方填充和编码必须只在训练集上 fit然后 transform 测试集。如果全量数据一起 fit测试集的信息就泄漏到训练过程里了模型评估结果会虚高。正确做法是先用train_test_split切分再分别处理。2.3 特征筛选别把几十个字段一股脑塞给模型信用数据字段多但真正有区分度的可能就十来个。特征筛选有两个常用手段方差过滤和相关性分析。方差接近零的字段直接删因为它对所有样本的取值几乎一样没有区分能力。相关性太高的字段保留一个就行否则模型系数会不稳定。from sklearn.feature_selection import VarianceThreshold # 方差过滤删掉方差低于阈值的列 selector VarianceThreshold(threshold0.01) X df.drop(default, axis1) y df[default] X_selected selector.fit_transform(X) kept_cols X.columns[selector.get_support()] print(f保留特征数{len(kept_cols)}删除{X.shape[1] - len(kept_cols)}) # 相关性矩阵找出高度相关的特征对 corr_matrix pd.DataFrame(X_selected, columnskept_cols).corr().abs() upper corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k1).astype(bool)) to_drop [col for col in upper.columns if any(upper[col] 0.85)] print(f高相关待删特征{to_drop})threshold0.01这个值不是固定的数据标准化之后方差普遍在 1 附近0.01 能过滤掉几乎不变的字段。相关性阈值 0.85 是我常用的经验值超过这个数的两个字段保留业务含义更清晰的那个。注意相关性只反映线性关系两个字段可能非线性相关但线性相关系数很低这种情况需要靠业务理解补充判断。3. 模型选型与训练逻辑回归、随机森林和 XGBoost 怎么选3.1 三个基线模型先跑通再谈优化信用评估领域最常用的三个模型是逻辑回归、随机森林和 XGBoost。逻辑回归可解释性强银行监管要求模型能解释每个变量的影响方向所以它至今仍是生产环境的主力。随机森林和 XGBoost 精度更高但可解释性差一些。我的建议是三个都跑用交叉验证对比 AUC 和 KS。from sklearn.model_selection import train_test_split, cross_val_score from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier X_train, X_test, y_train, y_test train_test_split( X_selected, y, test_size0.3, random_state42, stratifyy ) models { LR: LogisticRegression(class_weightbalanced, max_iter1000), RF: RandomForestClassifier(n_estimators200, class_weightbalanced, random_state42), XGB: XGBClassifier(n_estimators200, scale_pos_weight10, random_state42) } for name, model in models.items(): scores cross_val_score(model, X_train, y_train, cv5, scoringroc_auc) print(f{name} AUC: {scores.mean():.4f} (/- {scores.std():.4f}))class_weightbalanced和scale_pos_weight都是用来处理类别不平衡的。逻辑回归和随机森林用class_weightXGBoost 用scale_pos_weight值一般设成负样本数除以正样本数。stratifyy保证切分后训练集和测试集的标签比例一致这个参数在类别不平衡时必加不加的话可能测试集里违约样本只有个位数评估结果没有统计意义。3.2 评估指标AUC 之外KS 和召回率更贴近业务信用评估不能只看准确率因为违约样本少全预测为「不违约」也能有 90% 以上的准确率但这样的模型毫无价值。AUC 衡量的是模型区分正负样本的能力KS 值衡量的是最大区分度这两个是风控领域最常用的指标。from sklearn.metrics import roc_auc_score, roc_curve, recall_score, precision_score # 用随机森林做示例 rf RandomForestClassifier(n_estimators200, class_weightbalanced, random_state42) rf.fit(X_train, y_train) y_prob rf.predict_proba(X_test)[:, 1] auc roc_auc_score(y_test, y_prob) fpr, tpr, thresholds roc_curve(y_test, y_prob) ks max(tpr - fpr) # 按 0.5 阈值算召回率和精确率 y_pred (y_prob 0.5).astype(int) recall recall_score(y_test, y_pred) precision precision_score(y_test, y_pred) print(fAUC: {auc:.4f}, KS: {ks:.4f}) print(fRecall: {recall:.4f}, Precision: {precision:.4f})AUC 到 0.75 以上算可用0.8 以上算不错。KS 值一般要求 0.3 以上0.4 以上算优秀。召回率在信用评估里比精确率重要因为漏掉一个会违约的人损失远大于误拒一个好人。阈值 0.5 不是固定的可以根据业务需求调整比如把阈值降到 0.3召回率会上升但精确率下降适合风控严格的场景。3.3 超参数调优网格搜索和随机搜索的取舍调参这件事网格搜索适合参数少、范围小的情况随机搜索适合参数多、范围大的情况。信用评估模型里随机森林的n_estimators、max_depth、min_samples_split是三个关键参数XGBoost 的learning_rate、max_depth、subsample是重点。from sklearn.model_selection import RandomizedSearchCV from scipy.stats import randint, uniform param_dist { n_estimators: randint(100, 500), max_depth: randint(3, 15), min_samples_split: randint(2, 20), min_samples_leaf: randint(1, 10) } rf_search RandomizedSearchCV( RandomForestClassifier(class_weightbalanced, random_state42), param_dist, n_iter50, cv5, scoringroc_auc, random_state42, n_jobs-1 ) rf_search.fit(X_train, y_train) print(f最佳参数{rf_search.best_params_}) print(f最佳 AUC{rf_search.best_score_:.4f})n_iter50表示随机采样 50 组参数组合n_jobs-1用满所有 CPU 核心。max_depth别设太大超过 15 层基本就是过拟合了。min_samples_leaf设大一点能防止模型记住噪声样本。调参之后一定要在测试集上验证一遍交叉验证的分数和测试集分数差距超过 0.05说明调参过拟合了。4. 避坑与排查信用评估模型最常见的五个翻车现场4.1 数据泄漏AUC 0.95 的幻觉现象模型在训练集和测试集上 AUC 都超过 0.95但上线后效果一塌糊涂。原因特征里混入了标签相关的信息比如「是否催收过」这个字段催收过的用户大概率已经违约了这个字段在预测时根本拿不到。解决逐个检查特征问自己「这个字段在预测时刻是否已知」未知的字段一律删掉。另外填充和编码必须在切分之后做全量 fit 也是泄漏。4.2 类别不平衡导致模型只会预测多数类现象召回率极低违约样本几乎全被预测为不违约。原因没有处理类别不平衡模型倾向于预测多数类来降低整体损失。解决加class_weightbalanced或scale_pos_weight还可以用 SMOTE 过采样但要注意 SMOTE 只能在训练集上做测试集保持原始分布。4.3 特征编码后维度爆炸现象用OneHotEncoder处理职业字段职业有 50 个取值一下子多出 50 列模型训练慢且过拟合。原因高基数类别变量直接独热编码。解决改用目标编码Target Encoding或者把低频类别合并成「其他」。目标编码要注意用交叉验证的方式计算否则也会泄漏。4.4 阈值设错导致业务不可用现象模型 AUC 不错但按 0.5 阈值输出的通过率只有 30%业务方无法接受。原因没有根据业务需求调整阈值。解决画出 KS 曲线找到 KS 最大点对应的阈值或者根据通过率反推阈值。信用评估里通过率通常要控制在 70% 到 90% 之间。4.5 模型可解释性不足被风控驳回现象XGBoost 效果最好但风控部门要求解释每个变量的影响方向。原因树模型本身是黑匣子。解决用 SHAP 值做事后解释或者用逻辑回归做基线模型把 XGBoost 的预测结果作为参考。SHAP 能输出每个特征对单个样本预测值的贡献风控部门通常能接受。5. 从模型到评分卡把概率转成 300 到 850 的信用分模型输出的违约概率不能直接给业务用需要转成信用分。标准做法是用score offset factor * ln(odds)其中odds是违约概率除以不违约概率。factor和offset由分数范围和基准 odds 决定。import numpy as np def prob_to_score(prob, base_score600, base_odds50, pdo20): prob: 违约概率 base_score: 基准分数 base_odds: 基准 odds好/坏 pdo: 分数翻倍所需的 odds 倍数 factor pdo / np.log(2) offset base_score - factor * np.log(base_odds) odds (1 - prob) / prob score offset factor * np.log(odds) return np.clip(score, 300, 850) # 对测试集输出分数 scores [prob_to_score(p) for p in y_prob] print(f分数范围{min(scores):.0f} - {max(scores):.0f}) print(f平均分{np.mean(scores):.0f})base_score600表示基准 odds 对应的分数base_odds50表示好用户是坏用户的 50 倍pdo20表示 odds 翻一倍分数加 20 分。这三个参数根据业务定没有标准答案。np.clip把分数限制在 300 到 850 之间这是常见的信用分范围。验证方法上我会把分数按区间分桶看每个桶里的实际违约率是否单调递减。如果某个分数段的违约率突然跳高说明模型在这个区间有问题需要回去检查特征或样本。最后说一个我踩过的坑别在测试集上反复调阈值和参数调多了测试集就变成训练集了。正确做法是切三份训练集、验证集、测试集。验证集用来调参和选阈值测试集只在最后评估一次。这个习惯让我少走了很多弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表