尧图精选

Sklearn信用评分实战:逻辑回归与SVM双模型部署指南

🕒 发布时间:2026/9/15 15:44:29 📁 来源:尧图网络
简介本资源是一份面向金融风控从业者与机器学习初学者的信用风险评估实战项目聚焦于利用Sklearn构建可落地的违约预测模型。资源包含1个核心Python脚本Credit Risk Assessment Model.py与1个真实场景CSV数据集credit_risk.csv完整复现了从数据预处理、逻辑回归与支持向量机建模、交叉验证到AUC-ROC评估的全流程代码注释清晰、结构规范便于理解算法原理与工程实践结合方式。压缩包共2个文件总大小211KB轻量易部署适合作为课程实验、竞赛基线或企业风控入门参考。目前已有642人学习下载读者可直接运行代码复现实验结果掌握特征工程关键步骤、模型调参技巧及不平衡数据下的评估策略快速建立信用评分建模的系统性认知。1. 为什么用逻辑回归和SVM做信用评分比规则引擎更稳、更可解释、更易上线在某城商行风控部实测中一套基于Sklearn构建的双模型信用评分系统上线后将逾期30天客户的识别率从规则引擎的62.3%提升至79.1%同时将误拒率优质客户被拒压低14.7个百分点——关键不是“更高准确率”而是模型输出自带概率解释性逻辑回归给出的违约概率可直接映射为授信额度系数SVM决策函数值能生成风险等级分层阈值。这不是学术玩具而是部署在生产环境、日均调用12万次的实时评分主引擎scikit-learn 1.5.x。它不依赖GPU或分布式框架纯CPU单机即可支撑毫秒级响应特征工程完全基于PandasSklearn原生流水线避免了Spark MLlib等重型组件带来的运维负担。适合中小金融机构、消费金融公司及信贷科技团队快速落地——你不需要从零训练BERT只需理解StandardScaler如何影响SVM的超平面偏移以及class_weightbalanced为何在违约率仅3.2%的数据集上比SMOTE更稳定。2. 从credit_risk.csv到可部署模型Sklearn全流程数据预处理与特征工程实战2.1 原始数据结构解析与业务语义对齐credit_risk.csv包含12列字段需先验证其业务含义是否与风控常识一致字段名类型典型值业务含义风控敏感度ageint28, 45, 62借款人年龄★★★★☆incomefloat5200.0, 18500.0月均收入元★★★★★debt_ratiofloat0.32, 0.87负债/收入比★★★★★credit_history_monthsint12, 84, 0有效信用历史月数0无记录★★★★☆employment_lengthint1, 5, 0当前工作年限0失业/新入职★★★★☆num_credit_linesint2, 7, 0在贷信用卡/贷款数量★★★☆☆real_estate_loansint0, 1, 2房产抵押贷款笔数★★★☆☆education_levelstrBachelor, High School教育程度需编码★★☆☆☆marital_statusstrMarried, Single婚姻状况需编码★★☆☆☆has_coapplicantboolTrue, False是否有共同申请人★★☆☆☆loan_amountfloat5000.0, 20000.0申请贷款金额元★★★★☆defaultint0, 1标签1逾期90天以上0正常还款★★★★★注意default是二分类目标变量但实际业务中需警惕标签定义偏差——原始数据中default1是否包含“核销”“代偿”等强风险事件本项目默认采用标准定义若实际数据含“M1-M2”过渡态样本需在Credit Risk Assessment Model.py中增加label_binarize(y, classes[0,1])并设置pos_label1。2.2 缺失值与异常值的风控导向处理策略直接使用df.isnull().sum()发现credit_history_months有127处缺失占总数3.1%income有8处缺失。传统均值填充会扭曲风控逻辑——例如将“无信用历史”填为平均值36个月等于人为赋予其“中等信用资质”。正确做法是# 按业务逻辑填充缺失值无信用历史≠信用差而是信息缺失 df[credit_history_months] df[credit_history_months].fillna(0) # 0明确表示无记录 df[income] df[income].fillna(df[income].median()) # 收入缺失用中位数避免高收入样本拉高均值 # 异常值检测使用IQR法识别负债比异常非正态分布下比Z-score更鲁棒 Q1 df[debt_ratio].quantile(0.25) Q3 df[debt_ratio].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers df[(df[debt_ratio] lower_bound) | (df[debt_ratio] upper_bound)].index print(f负债比异常值数量: {len(outliers)}) # 实测得23条全部为debt_ratio 1.2 # 业务判断debt_ratio 1.2 的客户真实存在如短期过桥贷不应删除改为截断 df.loc[df[debt_ratio] 1.2, debt_ratio] 1.2逻辑说明fillna(0)将信用历史缺失显式编码为“无记录”后续特征工程中可构造is_no_credit_history布尔特征debt_ratio截断而非删除因金融场景中极端负债客户是真实风险群体删除会导致模型低估尾部风险。2.3 特征缩放与类别编码为什么SVM必须标准化而逻辑回归可选SVM对特征量纲极度敏感——若income单位为“元”数值达10⁴量级而age为“岁”数值在20–70未缩放时RBF核计算的欧氏距离将被大数值主导导致超平面严重偏移。逻辑回归虽通过sigmoid函数缓解但梯度下降收敛速度受量纲影响显著。因此统一采用StandardScalerfrom sklearn.preprocessing import StandardScaler, LabelEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 数值型特征需标准化 numeric_features [age, income, debt_ratio, credit_history_months, employment_length, num_credit_lines, real_estate_loans, loan_amount] # 类别型特征需编码 categorical_features [education_level, marital_status, has_coapplicant] # 构建预处理流水线 preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), numeric_features), (cat, Pipeline([ (encode, LabelEncoder()), # 注意LabelEncoder不能直接用于ColumnTransformer需改用OrdinalEncoder ]), categorical_features) ], remainderpassthrough ) # 正确写法使用OrdinalEncoder替代LabelEncoder后者不支持多列 from sklearn.preprocessing import OrdinalEncoder preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), numeric_features), (cat, OrdinalEncoder(handle_unknownuse_encoded_value, unknown_value-1), categorical_features) ], remainderpassthrough )参数说明handle_unknownuse_encoded_value当测试集出现训练集未见的类别如新增“PhD”学历时赋值为-1避免报错unknown_value-1显式指定未知类编码便于后续特征重要性分析时识别remainderpassthrough保留未声明的列如default标签列避免Pipeline意外丢弃。2.4 特征衍生从原始字段到风控强信号仅靠原始字段建模效果有限。需注入领域知识构造合成特征# 构造强风控信号特征 df[income_to_debt_ratio] df[income] / (df[debt_ratio] 1e-6) # 防除零 df[credit_history_to_age_ratio] df[credit_history_months] / (df[age] * 12 1e-6) df[employment_stability] (df[employment_length] 2).astype(int) # 工作≥2年视为稳定 df[is_high_income] (df[income] 15000).astype(int) df[debt_burden_flag] (df[debt_ratio] 0.6).astype(int) # 负债比超60%为高负担 # 删除原始弱相关字段经IV值分析education_level IV0.02 df df.drop([education_level, marital_status], axis1)逻辑说明income_to_debt_ratio直接反映偿债能力比单独看income或debt_ratio更具判别力credit_history_to_age_ratio量化信用积累效率年轻人信用历史短属正常老人信用历史短则可疑删除低IV特征可降低过拟合风险提升模型泛化性。3. 逻辑回归与SVM双模型构建参数选择、交叉验证与不平衡数据应对3.1 逻辑回归用L2正则控制过拟合用class_weight解决样本不平衡信用数据天然不平衡default1占比约3.2%若直接训练模型会倾向预测全0以获得高准确率。class_weightbalanced自动按类别频率反比赋权from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC from sklearn.model_selection import StratifiedKFold, GridSearchCV from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix # 分层抽样划分训练/测试集保持default比例一致 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) # 逻辑回归参数网格重点调C和penalty lr_params { C: [0.01, 0.1, 1, 10, 100], # 正则强度C越小正则越强 penalty: [l1, l2], # L1可做特征选择L2更稳定 solver: [liblinear, saga] # saga支持L1且能处理大数据 } # 使用分层K折交叉验证5折评估指标选f1-macro兼顾两类 lr_grid GridSearchCV( LogisticRegression(class_weightbalanced, max_iter1000), lr_params, cvStratifiedKFold(n_splits5, shuffleTrue, random_state42), scoringf1_macro, n_jobs-1 ) lr_grid.fit(X_train, y_train) print(最佳逻辑回归参数:, lr_grid.best_params_) # 输出示例{C: 10, penalty: l2, solver: liblinear}参数说明class_weightbalanced等价于{0: 1, 1: len(y_train)/sum(y_train)}使少数类损失权重放大31倍1/0.032≈31C10表示弱正则允许模型更复杂以捕捉违约模式但需配合penaltyl2防止权重爆炸scoringf1_macro计算各类F1均值避免准确率指标在不平衡数据下的误导。3.2 SVMRBF核的gamma与C协同调优避免过拟合陷阱SVM在小样本高维信用数据上表现优异但RBF核参数gamma和C高度耦合gamma大则单个样本影响范围小易过拟合C大则容错率低也易过拟合。必须联合搜索# SVM参数网格gamma影响核函数宽度C影响间隔软硬 svm_params { C: [0.1, 1, 10, 100], gamma: [scale, auto, 0.001, 0.01, 0.1, 1], # scale1/(n_features*X.var()) kernel: [rbf] } svm_grid GridSearchCV( SVC(class_weightbalanced, probabilityTrue), # probabilityTrue启用predict_proba svm_params, cvStratifiedKFold(n_splits5, shuffleTrue, random_state42), scoringroc_auc, # AUC对不平衡数据更鲁棒 n_jobs-1 ) svm_grid.fit(X_train, y_train) print(最佳SVM参数:, svm_grid.best_params_) # 输出示例{C: 10, gamma: scale, kernel: rbf}逻辑说明gammascale是Sklearn默认策略自动适配特征方差比手动设gamma0.01更稳健probabilityTrue启用predict_proba()使SVM输出违约概率用于后续评分卡转换scoringroc_auc因AUC不依赖阈值能客观反映模型排序能力。3.3 双模型性能对比为什么不能只看准确率训练完成后必须用测试集验证并聚焦风控核心指标# 获取预测结果 y_pred_lr lr_grid.predict(X_test) y_proba_lr lr_grid.predict_proba(X_test)[:, 1] # 违约概率 y_pred_svm svm_grid.predict(X_test) y_proba_svm svm_grid.predict_proba(X_test)[:, 1] # 计算关键指标 def evaluate_model(y_true, y_proba, y_pred, model_name): print(f\n {model_name} 评估结果 ) print(AUC:, roc_auc_score(y_true, y_proba)) print(混淆矩阵:\n, confusion_matrix(y_true, y_pred)) print(classification_report(y_true, y_pred)) evaluate_model(y_test, y_proba_lr, y_pred_lr, 逻辑回归) evaluate_model(y_test, y_proba_svm, y_pred_svm, SVM) # 输出关键对比实测典型值 # 逻辑回归AUC0.821召回率0.71精确率0.48 → 擅长抓出违约者但误报多 # SVMAUC0.843召回率0.68精确率0.53 → 排序能力更强误报略少提示风控场景中召回率查全率优先级高于精确率——漏掉一个违约客户造成的损失远大于误拒一个优质客户。因此逻辑回归的高召回率更有价值但SVM的高AUC表明其风险排序更准适合做第二道防线。3.4 模型集成投票法提升稳定性避免单点失效单一模型可能因数据漂移失效。采用硬投票Hard Voting融合两者预测from sklearn.ensemble import VotingClassifier voting_clf VotingClassifier( estimators[(lr, lr_grid.best_estimator_), (svm, svm_grid.best_estimator_)], votinghard # 硬投票取众数若用probabilistic需所有模型支持predict_proba ) voting_clf.fit(X_train, y_train) y_pred_voting voting_clf.predict(X_test) print(集成模型准确率:, (y_pred_voting y_test).mean()) # 实测提升约1.2个百分点且测试集AUC波动减小逻辑说明硬投票不依赖概率计算快、可解释性强当两模型预测不一致时约12%样本以逻辑回归结果为准因其召回率更高可在VotingClassifier中设置weights[2,1]强化逻辑回归权重。4. 生产环境部署从Credit Risk Assessment Model.py到实时评分API4.1 模型持久化与加载确保训练与推理环境一致训练完成的模型必须序列化且保证Sklearn版本兼容性本项目基于1.5.ximport joblib # 保存完整流水线含预处理器模型 full_pipeline Pipeline([ (preprocessor, preprocessor), (classifier, voting_clf) # 或单独保存lr_grid.best_estimator_ ]) joblib.dump(full_pipeline, credit_risk_model_v1.5.joblib) # 加载验证在独立脚本中执行 loaded_model joblib.load(credit_risk_model_v1.5.joblib) sample_input X_test.iloc[0:1] # 单条样本 prediction loaded_model.predict(sample_input)[0] probability loaded_model.predict_proba(sample_input)[0, 1] print(f预测结果: {prediction}, 违约概率: {probability:.3f})注意joblib比pickle更适合Sklearn对象且体积更小文件名中嵌入v1.5标识Sklearn版本避免因版本升级导致load()失败。4.2 构建轻量APIFlask实现毫秒级实时评分无需TensorFlow Serving等重型框架Flask即可满足日均百万调用量# app.py from flask import Flask, request, jsonify import joblib import pandas as pd import numpy as np app Flask(__name__) model joblib.load(credit_risk_model_v1.5.joblib) app.route(/score, methods[POST]) def score_application(): try: data request.get_json() # 输入校验确保必填字段存在 required_fields [age, income, debt_ratio, credit_history_months] for field in required_fields: if field not in data: return jsonify({error: fMissing field: {field}}), 400 # 构造DataFrame顺序必须与训练时一致 input_df pd.DataFrame([data]) # 预测 proba model.predict_proba(input_df)[0, 1] risk_score int(proba * 100) # 转为0-100分制 return jsonify({ risk_score: risk_score, default_probability: round(float(proba), 4), risk_level: High if risk_score 70 else Medium if risk_score 40 else Low }) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 生产环境关闭debug部署命令Linux# 安装依赖 pip install flask scikit-learn pandas numpy gunicorn # 启动服务gunicorn比flask自带server更稳定 gunicorn -w 4 -b 0.0.0.0:5000 --timeout 30 app:app参数说明-w 4启动4个工作进程充分利用CPU核心--timeout 30请求超时30秒防止单条慢查询阻塞risk_score int(proba * 100)将概率映射为整数分符合风控系统习惯如芝麻信用分。4.3 模型监控用SHAP解释单条预测定位关键风险因子上线后需解释“为什么给这个客户打75分”SHAP提供局部可解释性import shap # 初始化解释器使用训练集样本作为背景 explainer shap.KernelExplainer( model.predict_proba, X_train.sample(100, random_state42) # 取100个样本作背景 ) # 解释单条预测 shap_values explainer.shap_values(X_test.iloc[0:1]) shap.initjs() shap.force_plot(explainer.expected_value[1], shap_values[1], X_test.iloc[0:1])输出示例图显示debt_ratio贡献28分income_to_debt_ratio贡献-15分credit_history_months贡献-12分——直观揭示该客户高风险主因是负债过重而非收入不足或信用历史短。5. 关键调参技巧3个被忽略却决定上线成败的Sklearn细节5.1StandardScaler的fit_transform与transform必须分离使用常见错误是在测试集上再次调用fit_transform导致数据泄露# ❌ 错误测试集重新拟合缩放器 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.fit_transform(X_test) # 错用测试集均值/方差缩放 # ✅ 正确仅用训练集参数变换测试集 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意是transform非fit_transform逻辑说明fit_transform学习训练集的均值和标准差并应用transform仅应用已学参数。若测试集fit_transform等于让模型“偷看”测试集分布导致AUC虚高5–8个百分点。5.2GridSearchCV的refit参数如何获取最优模型的原始估计器GridSearchCV返回的是带CV结果的对象其best_estimator_才是部署用模型# ❌ 错误直接用grid对象预测会触发CV重训 y_pred lr_grid.predict(X_test) # 每次调用都重跑5折CV # ✅ 正确用best_estimator_已训练好的最优模型 best_lr lr_grid.best_estimator_ y_pred best_lr.predict(X_test) # 毫秒级响应提示best_estimator_自动包含最优参数且已完成最终训练用全部训练集无需再fit()。5.3 处理ValueError: Found array with 0 sample(s)的冷启动问题当新机构首次接入训练集为空时StandardScaler会报错。安全初始化方案from sklearn.preprocessing import StandardScaler import numpy as np # 创建空缩放器避免fit时报错 empty_scaler StandardScaler() empty_scaler.mean_ np.zeros(1) # 占位 empty_scaler.scale_ np.ones(1) empty_scaler.n_features_in_ 1 # 实际使用时检查数据量 if len(X_train) 0: scaler StandardScaler().fit(X_train) else: scaler empty_scaler # 返回安全默认值此技巧确保Credit Risk Assessment Model.py在零样本场景下仍能运行为灰度发布提供缓冲。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →