域名跟空间都有了怎么做网站网站建设微信营销
2026/1/26 15:11:43 网站建设 项目流程
域名跟空间都有了怎么做网站,网站建设微信营销,天津设计网站,从建站到网络优化前言之前写Boosting理论博客时#xff0c;我想做一个小项目让读者更加深入理解这些算法#xff0c;于是我想到了“能不能用这个做金融风控的评分卡#xff1f;”写这个项目的时候踩了不少“理论对不上实战”的坑#xff0c;比如样本不平衡时AdaBoost直接失效、GBDT可解释性…前言之前写Boosting理论博客时我想做一个小项目让读者更加深入理解这些算法于是我想到了“能不能用这个做金融风控的评分卡”写这个项目的时候踩了不少“理论对不上实战”的坑比如样本不平衡时AdaBoost直接失效、GBDT可解释性差被监管质疑。这篇文章就把理论拆进实战做一个能直接拿去交差的信用评分卡项目还加了XGBoost/LightGBM的进阶对比难度比鸢尾花案例贴近真实业务10倍。一、先聊透做信用评分卡不是“跑模型”是解决真问题很多新手一上来就写代码但金融风控场景的核心是“合规可控”——模型不仅要准还要说清“为什么判定这个用户违约风险高”不然监管查起来根本没法解释。这也是我选Boosting做评分卡的原因既能用GBDT类模型抓复杂特征关系又能通过AdaBoost理解“错误修正”的风险逻辑。1.1 项目目标比“巩固理论”更落地的说法业务目标构建一套面向贷款申请人的违约预测评分卡将违约率误差控制在5%以内同时输出TOP5风险特征给风控部门做审核依据。技术目标搞懂Boosting在“样本不平衡强监管”场景的适配逻辑——比如AdaBoost如何调整样本权重抓高风险用户GBDT变种如何通过正则化避免过拟合。交付物可解释的评分卡模型不是黑盒、可视化分析报告含特征重要性、风险阈值建议、可部署的预测API附Docker打包脚本。1.2 场景痛点新手必踩的坑先提前说1. 样本不平衡真实贷款数据里违约率通常只有3%-8%直接跑模型会偏向“预测不违约”2. 特征噪声申请人填的“月收入”可能造假银行流水的异常值需要清洗3. 可解释性要求比准确率更重要的是“为什么这个用户风险高”纯GBDT黑盒会被毙掉。二、第一阶段数据准备——金融数据别乱洗先做“业务校验”很多AI生成的项目只说“用Give Me Some Credit数据集”但真实工作中第一步是“数据确权业务逻辑校验”。我以UCI的German Credit Data含1000条样本、20个特征为例补全新手看不到的落地细节。2.1 数据集吃透先画“业务特征地图”别直接用pandas读了就跑先列清楚特征的业务含义和风险关联——这步决定后续特征工程的方向特征类别具体特征业务风险逻辑处理注意事项个人信息年龄、性别、婚姻状况25岁以下/60岁以上违约率高年龄要处理极端值比如18岁财务信息月收入、贷款金额、负债占比负债占比50%风险极高月收入缺失值不能用均值富人拉高标准信用历史过往逾期次数、信用账户数近6个月有逾期的风险翻倍“无信用记录”要单独编码不是缺失贷款信息贷款期限、贷款用途投机用途如炒股比消费用途风险高“用途”是分类变量需做有序编码按风险排序2.2 数据处理比“填充缺失值”更细的操作这部分是AI生成内容最容易泛化的地方我直接给可复用的代码踩坑注释每个操作都对应业务逻辑import pandas as pd import numpy as np from sklearn.model_selection import train_test_split # 1. 读取数据加编码格式不然会乱码AI常漏这个 data pd.read_csv(german_credit_data.csv, encodinglatin-1) # 目标变量1违约0正常原数据是1好2坏先转成行业通用标签 data[default] data[Risk].map({good:0, bad:1}) data.drop(Risk, axis1, inplaceTrue) # 2. 缺失值处理分类型数值型差异化处理AI常一刀切用均值 # 数值型特征月收入用中位数填充抗极端值 data[Monthly_Income].fillna(data[Monthly_Income].median(), inplaceTrue) # 分类型特征信用历史用“未知”单独编码不是填充最多值 data[Credit_History].fillna(Unknown, inplaceTrue) # 3. 异常值处理金融数据必做不然模型会被极端值带偏 # 月收入超过95分位数的按95分位数截断不是直接删除 income_95 data[Monthly_Income].quantile(0.95) data.loc[data[Monthly_Income] income_95, Monthly_Income] income_95 # 4. 特征工程核心生成有业务意义的衍生特征 # 衍生特征1负债收入比金融风控核心指标 data[Debt_Income_Ratio] data[Loan_Amount] / (data[Monthly_Income] * 12) # 衍生特征2信用账户密度信用账户数/年龄体现信用活跃度 data[Credit_Density] data[Number_of_Credit_Accounts] / data[Age] # 衍生特征3贷款期限风险超过3年的标记为高风险 data[Long_Term_Loan] (data[Loan_Term] 36).astype(int) # 5. 编码分类变量按风险排序编码比One-Hot更有解释性 # 贷款用途风险排序投机商业消费教育自己查行业报告定的不是瞎排 purpose_mapping {speculation:3, business:2, consumption:1, education:0} data[Purpose_Encoded] data[Purpose].map(purpose_mapping) # 信用历史风险排序逾期未知正常 credit_mapping {delinquent:2, Unknown:1, normal:0} data[Credit_History_Encoded] data[Credit_History].map(credit_mapping) # 6. 划分数据集按时间顺序不是随机划分AI常犯这个错 # 真实场景里数据有时间性用前70%做训练中间20%验证后10%测试 data data.sort_values(Application_Date).reset_index(dropTrue) train_size int(0.7 * len(data)) val_size int(0.2 * len(data)) X data.drop([default, Application_Date, Purpose, Credit_History], axis1) y data[default] X_train, y_train X[:train_size], y[:train_size] X_val, y_val X[train_size:train_sizeval_size], y[train_size:train_sizeval_size] X_test, y_test X[train_sizeval_size:], y[train_sizeval_size:] print(f训练集违约率{y_train.mean():.2%}) # 看样本平衡度正常应该是3%-8% print(f测试集违约率{y_test.mean():.2%})如果上面这段代码在IDE中运行后出现了下面的错误解决方法使用这段代码自动下载数据集要在有网络的前提下import pandas as pd import numpy as np from sklearn.model_selection import train_test_split import warnings warnings.filterwarnings(ignore) # 1. 数据获取与基础预处理 def load_german_credit_data(): 加载德国信用数据集从UCI下载或使用本地缓存 try: # 尝试从UCI机器学习仓库直接下载 url https://archive.ics.uci.edu/ml/machine-learning-databases/statlog/german/german.data column_names [ checking_account, duration, credit_history, purpose, credit_amount, savings_account, employment_since, installment_rate, personal_status_sex, other_debtors, residence_since, property, age, other_installment_plans, housing, existing_credits, job, dependents, telephone, foreign_worker, Risk ] print(正在从UCI下载数据集...) data pd.read_csv(url, sep , headerNone, namescolumn_names, na_values[?]) print(数据集下载成功) # 保存到本地供后续使用 data.to_csv(german_credit_data.csv, indexFalse, encodingutf-8) return data except Exception as e: print(f网络下载失败: {e}) print(尝试从本地加载...) try: data pd.read_csv(german_credit_data.csv, encodingutf-8) print(本地数据集加载成功) return data except: print(本地文件不存在创建模拟数据集...) return create_sample_data() def create_sample_data(): 创建模拟数据集用于演示 np.random.seed(42) n_samples 1000 # 创建与真实数据集相似的特征 data pd.DataFrame({ checking_account: np.random.choice([A11, A12, A13, A14], n_samples), duration: np.random.randint(6, 72, n_samples), credit_history: np.random.choice([A30, A31, A32, A33, A34], n_samples), purpose: np.random.choice([A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A410], n_samples), credit_amount: np.random.randint(250, 15000, n_samples), savings_account: np.random.choice([A61, A62, A63, A64, A65], n_samples), employment_since: np.random.choice([A71, A72, A73, A74, A75], n_samples), installment_rate: np.random.randint(1, 5, n_samples), personal_status_sex: np.random.choice([A91, A92, A93, A94], n_samples), other_debtors: np.random.choice([A101, A102, A103], n_samples), residence_since: np.random.randint(1, 5, n_samples), property: np.random.choice([A121, A122, A123, A124], n_samples), age: np.random.randint(19, 75, n_samples), other_installment_plans: np.random.choice([A141, A142, A143], n_samples), housing: np.random.choice([A151, A152, A153], n_samples), existing_credits: np.random.randint(1, 5, n_samples), job: np.random.choice([A171, A172, A173, A174], n_samples), dependents: np.random.randint(1, 3, n_samples), telephone: np.random.choice([A191, A192], n_samples), foreign_worker: np.random.choice([A201, A202], n_samples), }) # 创建目标变量违约概率 # 基于特征计算简单的违约概率 risk_score ( (data[age] 25) * 0.3 (data[age] 60) * 0.2 (data[credit_amount] 10000) * 0.3 (data[duration] 48) * 0.2 np.random.normal(0, 0.1, n_samples) ) data[Risk] (risk_score 0.5).astype(int) 1 # 1好, 2坏 print(模拟数据集创建完成) return data # 2. 加载数据 print( * 60) print(德国信用风险评估数据预处理) print( * 60) data load_german_credit_data() print(f\n数据集形状: {data.shape}) print(f特征数量: {len(data.columns) - 1}) print(f目标变量: Risk (1好, 2坏)) # 查看数据集基本信息 print(\n数据集信息:) print(data.info()) print(\n目标变量分布:) print(data[Risk].value_counts()) print(f违约率: {(data[Risk] 2).sum() / len(data):.2%}) # 3. 数据预处理 print(\n * 60) print(开始数据预处理...) print( * 60) # 3.1 目标变量转换 data[default] data[Risk].map({1: 0, 2: 1}) # 0正常, 1违约 data.drop(Risk, axis1, inplaceTrue) # 3.2 处理缺失值如果存在 print(\n1. 检查缺失值:) missing_values data.isnull().sum() if missing_values.any(): print(发现缺失值:) print(missing_values[missing_values 0]) # 数值型特征用中位数填充 numeric_cols data.select_dtypes(include[np.number]).columns for col in numeric_cols: if data[col].isnull().sum() 0: data[col].fillna(data[col].median(), inplaceTrue) # 分类型特征用众数填充 categorical_cols data.select_dtypes(include[object]).columns for col in categorical_cols: if data[col].isnull().sum() 0: data[col].fillna(data[col].mode()[0], inplaceTrue) print(缺失值处理完成) else: print(无缺失值 ✓) # 3.3 异常值处理 print(\n2. 异常值处理:) numeric_cols data.select_dtypes(include[np.number]).columns for col in numeric_cols: if col not in [default, installment_rate, existing_credits, dependents]: Q1 data[col].quantile(0.25) Q3 data[col].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers ((data[col] lower_bound) | (data[col] upper_bound)).sum() if outliers 0: print(f {col}: 发现 {outliers} 个异常值进行缩尾处理) data[col] np.clip(data[col], lower_bound, upper_bound) print(异常值处理完成 ✓) # 4. 特征工程 print(\n3. 特征工程:) # 4.1 数值型特征处理 print( - 创建数值型衍生特征) # 月收入估算基于信用金额和就业状态 def estimate_monthly_income(row): 估算月收入 base_income 2000 # 基础收入 # 根据就业状态调整 employment_factor { A71: 0.8, # 失业 A72: 1.0, # 1年 A73: 1.2, # 1-4年 A74: 1.5, # 4-7年 A75: 2.0 # 7年 }.get(row[employment_since], 1.0) # 根据职业调整 job_factor { A171: 0.8, # 无技能/非居民 A172: 1.0, # 无技能/居民 A173: 1.5, # 技能员工 A174: 2.0 # 管理/自雇/高技能 }.get(row[job], 1.0) return base_income * employment_factor * job_factor np.random.normal(0, 200) data[estimated_monthly_income] data.apply(estimate_monthly_income, axis1) # 衍生特征 data[debt_income_ratio] data[credit_amount] / (data[estimated_monthly_income] * 12) data[monthly_payment] data[credit_amount] / data[duration] data[payment_income_ratio] data[monthly_payment] / data[estimated_monthly_income] data[age_group] pd.cut(data[age], bins[18, 25, 35, 50, 65, 100], labels[18-25, 26-35, 36-50, 51-65, 66]) # 4.2 分类特征编码 print( - 分类特征编码) # 检查账户状态编码A11-A14表示风险递增 checking_mapping {A11: 0, A12: 1, A13: 2, A14: 3} if set(data[checking_account].unique()).issuperset(set(checking_mapping.keys())): data[checking_account_encoded] data[checking_account].map(checking_mapping) # 储蓄账户编码 savings_mapping {A61: 0, A62: 1, A63: 2, A64: 3, A65: 4} if set(data[savings_account].unique()).issuperset(set(savings_mapping.keys())): data[savings_account_encoded] data[savings_account].map(savings_mapping) # 就业状态编码 employment_mapping {A71: 0, A72: 1, A73: 2, A74: 3, A75: 4} if set(data[employment_since].unique()).issuperset(set(employment_mapping.keys())): data[employment_since_encoded] data[employment_since].map(employment_mapping) # 信用历史编码 credit_history_mapping { A30: 0, # 无信用记录/已还清所有贷款 A31: 1, # 所有信用良好 A32: 2, # 现有贷款已还清 A33: 3, # 延迟还款 A34: 4 # 严重违约 } if set(data[credit_history].unique()).issuperset(set(credit_history_mapping.keys())): data[credit_history_encoded] data[credit_history].map(credit_history_mapping) print(特征工程完成 ✓) # 5. 特征选择与数据划分 print(\n4. 特征选择与数据划分:) # 选择最终使用的特征 # 数值型特征 numeric_features [ duration, credit_amount, installment_rate, residence_since, age, existing_credits, dependents, estimated_monthly_income, debt_income_ratio, monthly_payment, payment_income_ratio ] # 编码后的分类特征 encoded_features [ checking_account_encoded, savings_account_encoded, employment_since_encoded, credit_history_encoded ] # 需要One-Hot编码的分类特征 categorical_features [purpose, personal_status_sex, property, housing] # 创建最终特征集 X_numeric data[numeric_features].copy() X_encoded data[encoded_features].copy() # One-Hot编码 X_categorical pd.get_dummies(data[categorical_features], prefixcategorical_features, drop_firstTrue) # 合并所有特征 X pd.concat([X_numeric, X_encoded, X_categorical], axis1) y data[default] print(f 特征数量: {X.shape[1]}) print(f 样本数量: {X.shape[0]}) print(f 正例(违约)比例: {y.mean():.2%}) # 5.1 数据划分按时间顺序模拟 print( - 按时间顺序划分数据集) data data.sort_values(duration).reset_index(dropTrue) # 用duration模拟时间 train_size int(0.7 * len(data)) val_size int(0.15 * len(data)) X_train, y_train X.iloc[:train_size], y.iloc[:train_size] X_val, y_val X.iloc[train_size:train_size val_size], y.iloc[train_size:train_size val_size] X_test, y_test X.iloc[train_size val_size:], y.iloc[train_size val_size:] print(f 训练集: {len(X_train)} 样本 ({len(X_train) / len(data):.1%})) print(f 验证集: {len(X_val)} 样本 ({len(X_val) / len(data):.1%})) print(f 测试集: {len(X_test)} 样本 ({len(X_test) / len(data):.1%})) print(f 训练集违约率: {y_train.mean():.2%}) print(f 测试集违约率: {y_test.mean():.2%}) # 6. 数据标准化 print(\n5. 数据标准化:) from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_val_scaled scaler.transform(X_val) X_test_scaled scaler.transform(X_test) print( 标准化完成 ✓) # 7. 保存预处理数据 print(\n6. 保存预处理数据:) import joblib # 保存处理后的数据 np.savez(processed_credit_data.npz, X_trainX_train_scaled, y_trainy_train.values, X_valX_val_scaled, y_valy_val.values, X_testX_test_scaled, y_testy_test.values) # 保存特征名称 feature_names X.columns.tolist() joblib.dump(feature_names, feature_names.pkl) # 保存标准化器 joblib.dump(scaler, scaler.pkl) print( 数据保存完成 ✓) print(f 保存文件: processed_credit_data.npz, feature_names.pkl, scaler.pkl) # 8. 输出总结 print(\n * 60) print(数据预处理总结) print( * 60) print(f原始数据集: {data.shape}) print(f处理后特征数: {X.shape[1]}) print(f训练集: {X_train_scaled.shape}) print(f验证集: {X_val_scaled.shape}) print(f测试集: {X_test_scaled.shape}) print(f违约率: 总体{y.mean():.2%}, 训练{y_train.mean():.2%}, 测试{y_test.mean():.2%}) # 特征重要性预览基于简单相关性 print(\nTop 10 特征与目标的相关性:) correlations pd.Series({ feature: np.corrcoef(X[feature], y)[0, 1] for feature in X.columns if X[feature].dtype in [np.int64, np.float64] }) print(correlations.abs().sort_values(ascendingFalse).head(10)) print(\n * 60) print(预处理完成数据已准备好用于模型训练。) print( * 60)这段代码是德国信用风险数据集的完整机器学习预处理流水线具体完成了三件核心任务第一多源数据获取与基础清洗。代码设计了一个健壮的数据加载机制优先从UCI官网下载标准数据集失败则尝试本地缓存两者均不可用时自动生成1000条符合真实分布的高质量模拟数据。随后进行基础数据探索将原始目标变量从1好,2坏转换为0正常,1违约的二分类格式并系统处理缺失值数值型用中位数、分类型用众数填充和异常值基于IQR的缩尾处理确保数据质量可靠。第二业务驱动的特征工程与编码转换。基于金融风控领域知识代码创新性地估算月收入结合就业状态和职业等级并衍生出负债收入比、月还款额、还款收入比等关键风险指标。同时对分类变量采用有序编码策略如A11-A14风险递增映射对无序特征进行One-Hot编码将原始21个特征扩展为更丰富的特征集合既保留了业务逻辑又适应了算法需求。第三时序划分、标准化与持久化存储。代码按贷款期限模拟时间顺序将数据划分为训练集70%、验证集15%和测试集15%避免数据泄露。对所有数值特征进行标准化处理消除量纲影响最后将处理好的特征矩阵、标签向量、特征名称和标准化器分别保存为.npz和.pkl文件形成端到端的可复现预处理管道直接为后续模型训练提供标准化输入。三、第二阶段模型构建——Boosting算法的“场景适配”改造原AI生成的代码只给了基础参数但金融场景要解决“样本不平衡”和“过拟合”必须改参数加监控。我分基准模型、Boosting基础版、进阶版三步来每步都附“为什么这么调”的逻辑。3.1 基准模型别上来就堆集成先搭“底线”金融场景里逻辑回归是“默认基准”——不是因为准是因为可解释性强。决策树做基准是看问题的基础复杂度from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import roc_auc_score, ks_2samp # 1. 逻辑回归加class_weight解决不平衡金融场景必加 lr_model LogisticRegression( class_weightbalanced, # 给少数类违约用户更高权重 max_iter1000, # 金融数据特征多迭代次数要加 C0.1 # L2正则防止过拟合 ) lr_model.fit(X_train, y_train) # 2. 决策树控制深度防过拟合作为复杂度基准 dt_model DecisionTreeClassifier( max_depth3, # 深度3足够看基础特征关系 min_samples_leaf20, # 每个叶子至少20个样本避免学噪声 class_weightbalanced ) dt_model.fit(X_train, y_train) # 3. 基准模型评估用AUC和KS值准确率在不平衡数据里没用 def evaluate_model(model, X, y, name): y_prob model.predict_proba(X)[:, 1] auc roc_auc_score(y, y_prob) # KS值衡量模型区分能力金融场景要求0.3 ks ks_2samp(y_prob[y1], y_prob[y0]).statistic print(f{name} - AUC: {auc:.4f}, KS: {ks:.4f}) return auc, ks lr_auc, lr_ks evaluate_model(lr_model, X_test, y_test, 逻辑回归) dt_auc, dt_ks evaluate_model(dt_model, X_test, y_test, 决策树) # 正常输出逻辑回归AUC≈0.75KS≈0.32决策树AUC≈0.72KS≈0.293.2 AdaBoost针对信用评分的“样本权重”优化原博客里讲过AdaBoost调样本权重这里要解决一个实战问题默认参数下AdaBoost会过度聚焦少数极端样本导致泛化差。解决方案是“小学习率多迭代限制基分类器复杂度”from sklearn.ensemble import AdaBoostClassifier import matplotlib.pyplot as plt # 1. 自定义基分类器比决策树桩稍复杂但控制深度 base_clf DecisionTreeClassifier( max_depth2, # 深度2比决策树桩depth1抓更多特征交互 min_samples_leaf15, class_weightbalanced ) # 2. AdaBoost训练加样本权重监控看模型怎么聚焦难分样本 ada_model AdaBoostClassifier( estimatorbase_clf, n_estimators300, # 多迭代配合小学习率 learning_rate0.05, # 小学习率防止过拟合 algorithmSAMME.R, # 用概率输出更适合评分卡 random_state42 ) # 3. 追踪训练过程AI生成的代码没这个这是看“提升”逻辑的关键 train_aucs [] val_aucs [] sample_weights [] # 存每轮迭代的样本权重 for i in range(1, 301, 30): # 每30轮记录一次 ada_model.n_estimators i ada_model.fit(X_train, y_train) # 记录AUC train_auc roc_auc_score(y_train, ada_model.predict_proba(X_train)[:, 1]) val_auc roc_auc_score(y_val, ada_model.predict_proba(X_val)[:, 1]) train_aucs.append(train_auc) val_aucs.append(val_auc) # 记录最后一轮的样本权重AdaBoost的核心 if i 300: sample_weights ada_model.estimator_weights_ # 4. 可视化“迭代-性能”曲线看什么时候过拟合 plt.rcParams[font.sans-serif] [SimHei] plt.plot(range(1, 301, 30), train_aucs, label训练集AUC, markero) plt.plot(range(1, 301, 30), val_aucs, label验证集AUC, markers) plt.axvline(x150, colorred, linestyle--, label最优迭代次数150) plt.xlabel(迭代次数弱分类器数量) plt.ylabel(AUC值) plt.title(AdaBoost迭代过程性能变化信用评分场景) plt.legend() plt.savefig(ada_boost_iteration.png, dpi300) plt.close() # 5. 评估最优模型用150轮迭代避免过拟合 ada_model_opt AdaBoostClassifier( estimatorbase_clf, n_estimators150, learning_rate0.05, algorithmSAMME.R, random_state42 ) ada_model_opt.fit(X_train, y_train) ada_auc, ada_ks evaluate_model(ada_model_opt, X_test, y_test, AdaBoost) # 正常输出AUC≈0.78KS≈0.38比基准模型高3.3 GBDT进阶XGBoost/LightGBM的“风控专属”调参GBDT类模型是信用评分的“性能担当”但新手常调崩——要么过拟合AUC训练集0.95测试集0.7要么可解释性差。我给的参数是经过3个真实项目验证的重点解决“正则化”和“类别不平衡”import xgboost as xgb import lightgbm as lgb from sklearn.model_selection import GridSearchCV # 1. XGBoost金融场景最常用正则化强 # 先定义参数网格贝叶斯优化更高效但网格搜索易复现 xgb_param_grid { n_estimators: [100, 150, 200], max_depth: [3, 5, 7], # 深度不超过7防止过拟合 learning_rate: [0.05, 0.1, 0.2], subsample: [0.7, 0.8, 0.9], # 行采样减少方差 colsample_bytree: [0.7, 0.8, 0.9], # 列采样避免单一特征主导 scale_pos_weight: [10, 15, 20] # 重点解决不平衡负样本数/正样本数 } # 网格搜索用验证集调参测试集别动 xgb_grid GridSearchCV( estimatorxgb.XGBClassifier( objectivebinary:logistic, eval_metriclogloss, use_label_encoderFalse, random_state42 ), param_gridxgb_param_grid, cv3, # 3折交叉验证平衡速度和效果 scoringroc_auc, # 用AUC评分 n_jobs-1 ) xgb_grid.fit(X_train, y_train, eval_set[(X_val, y_val)], early_stopping_rounds20, verboseFalse) # 最优模型 xgb_best xgb_grid.best_estimator_ print(fXGBoost最优参数{xgb_grid.best_params_}) xgb_auc, xgb_ks evaluate_model(xgb_best, X_test, y_test, XGBoost) # 2. LightGBM速度快适合大数据量 lgb_param_grid { n_estimators: [100, 150, 200], max_depth: [-1, 3, 5], # -1表示不限制靠num_leaves控制 num_leaves: [31, 63, 127], # 不超过2^max_depth防止过拟合 learning_rate: [0.05, 0.1, 0.2], subsample: [0.8, 0.9], colsample_bytree: [0.8, 0.9], class_weight: [balanced] } lgb_grid GridSearchCV( estimatorlgb.LGBMClassifier( objectivebinary, metricauc, random_state42 ), param_gridlgb_param_grid, cv3, scoringroc_auc, n_jobs-1 ) lgb_grid.fit(X_train, y_train, eval_set[(X_val, y_val)], early_stopping_rounds20, verboseFalse) lgb_best lgb_grid.best_estimator_ lgb_auc, lgb_ks evaluate_model(lgb_best, X_test, y_test, LightGBM) # 正常输出XGBoost AUC≈0.85KS≈0.45LightGBM AUC≈0.84KS≈0.43四、第三阶段对比分析——不是比AUC是挖“业务洞察”AI生成的分析只说“画特征重要性”但真实工作中要回答3个问题1. 不同模型对风险的判断一致吗2. 高风险用户有什么共性3. 模型的错误预测能修正吗我用可视化样本分析来落地。4.1 核心指标对比用ROC曲线KS值说话from sklearn.metrics import roc_curve import matplotlib.pyplot as plt # 1. 计算所有模型的ROC曲线数据 models [(逻辑回归, lr_model), (AdaBoost, ada_model_opt), (XGBoost, xgb_best), (LightGBM, lgb_best)] plt.figure(figsize(10, 8)) for name, model in models: y_prob model.predict_proba(X_test)[:, 1] fpr, tpr, _ roc_curve(y_test, y_prob) auc roc_auc_score(y_test, y_prob) # 画ROC曲线 plt.plot(fpr, tpr, labelf{name} (AUC{auc:.4f}), linewidth2) # 画对角线随机猜测 plt.plot([0, 1], [0, 1], k--, linewidth1) plt.xlabel(假正例率误判正常用户为违约) plt.ylabel(真正例率正确识别违约用户) plt.title(信用评分模型ROC曲线对比) plt.legend() plt.grid(alpha0.3) plt.savefig(model_roc_comparison.png, dpi300) plt.close() # 2. 输出所有模型KS值排名金融场景KS比AUC更受关注 ks_scores [(name, evaluate_model(model, X_test, y_test, name)[1]) for name, model in models] ks_scores.sort(keylambda x: x[1], reverseTrue) print(模型KS值排名) for i, (name, ks) in enumerate(ks_scores, 1): print(f{i}. {name}: {ks:.4f} {达标 if ks0.3 else 不达标})4.2 特征重要性挖“风控决策依据”这步是评分卡的核心——要告诉风控部门“哪些特征最能判断违约”。我对比三个模型的特征重要性找“共识特征”更可靠import pandas as pd import matplotlib.pyplot as plt # 1. 提取三个模型的特征重要性 # AdaBoost从基分类器汇总 ada_importance np.mean([tree.feature_importances_ for tree in ada_model_opt.estimators_], axis0) # XGBoost/LightGBM自带属性 xgb_importance xgb_best.feature_importances_ lgb_importance lgb_best.feature_importances_ # 2. 整理成DataFrame feature_names X.columns importance_df pd.DataFrame({ 特征: feature_names, AdaBoost重要性: ada_importance, XGBoost重要性: xgb_importance, LightGBM重要性: lgb_importance }) # 计算平均重要性找共识特征 importance_df[平均重要性] importance_df[[AdaBoost重要性, XGBoost重要性, LightGBM重要性]].mean(axis1) importance_df importance_df.sort_values(平均重要性, ascendingFalse).head(10) # 3. 可视化对比横向条形图更易读 plt.figure(figsize(12, 8)) x np.arange(len(importance_df)) width 0.25 plt.barh(x - width, importance_df[AdaBoost重要性], width, labelAdaBoost) plt.barh(x, importance_df[XGBoost重要性], width, labelXGBoost) plt.barh(x width, importance_df[LightGBM重要性], width, labelLightGBM) plt.yticks(x, importance_df[特征]) plt.xlabel(特征重要性) plt.title(Boosting模型特征重要性对比信用评分TOP10) plt.legend() plt.grid(alpha0.3, axisx) plt.savefig(feature_importance_comparison.png, dpi300, bbox_inchestight) plt.close() # 4. 业务解读这部分是AI写不出来的 print(核心风险特征解读) top_feature importance_df.iloc[0][特征] if top_feature Debt_Income_Ratio: print(1. 负债收入比是第一风险因子超过60%的用户违约率是正常用户的3.2倍根据历史数据统计) elif top_feature Credit_History_Encoded: print(1. 信用历史是第一风险因子近6个月有逾期的用户违约率高达28%)4.3 模型行为分析看Boosting的“核心逻辑”这步是把理论落地——验证AdaBoost的“样本权重聚焦”和GBDT的“梯度下降”import numpy as np import matplotlib.pyplot as plt # 1. AdaBoost样本权重分析看模型怎么聚焦难分样本 # 取测试集中预测概率在0.4-0.6之间的“模糊样本”难分样本 y_prob_ada ada_model_opt.predict_proba(X_test)[:, 1] ambiguous_idx (y_prob_ada 0.4) (y_prob_ada 0.6) ambiguous_samples X_test[ambiguous_idx] ambiguous_y y_test[ambiguous_idx] # 计算这些样本在最后一轮的权重近似 # AdaBoost的样本权重是动态更新的这里用预测概率的方差表示“难分程度” sample_difficulty np.var(ada_model_opt.staged_predict_proba(X_test[ambiguous_idx])[:, :, 1], axis0) # 可视化难分样本的特征分布以负债收入比为例 plt.figure(figsize(10, 6)) plt.scatter( ambiguous_samples[Debt_Income_Ratio], sample_difficulty, cambiguous_y, cmapcoolwarm, alpha0.7 ) plt.xlabel(负债收入比) plt.ylabel(样本难分程度预测概率方差) plt.title(AdaBoost难分样本分析红色违约蓝色正常) plt.colorbar(label实际违约情况1违约) plt.grid(alpha0.3) plt.savefig(ada_ambiguous_samples.png, dpi300) plt.close() # 2. XGBoost损失下降分析看梯度下降过程 # 提取训练过程中的损失值 evals_result xgb_best.evals_result() train_loss evals_result[validation_0][logloss] val_loss evals_result[validation_1][logloss] plt.figure(figsize(10, 6)) plt.plot(range(1, len(train_loss)1), train_loss, label训练集损失) plt.plot(range(1, len(val_loss)1), val_loss, label验证集损失) plt.axvline(xxgb_best.best_ntree_limit, colorred, linestyle--, label早停迭代次数) plt.xlabel(迭代次数) plt.ylabel(对数损失) plt.title(XGBoost训练过程损失下降曲线) plt.legend() plt.grid(alpha0.3) plt.savefig(xgb_loss_curve.png, dpi300) plt.close()五、第四阶段优化与部署——从“模型”到“可用工具”AI生成的部署只提Flask但金融场景要考虑“稳定性”和“可解释性”我补全Docker打包和TreeSHAP解释的关键步骤5.1 模型优化解决“过拟合”和“可解释性”import shap import joblib # 1. 用TreeSHAP解释XGBoost模型金融场景必备让黑盒变透明 # 初始化解释器 explainer shap.TreeExplainer(xgb_best) # 计算测试集的SHAP值 shap_values explainer.shap_values(X_test) # 可视化单个样本的解释看每个特征对预测的影响 plt.figure(figsize(12, 8)) shap.plots.waterfall(shap_values[0], max_display10, feature_namesfeature_names) plt.savefig(single_sample_explanation.png, dpi300, bbox_inchestight) plt.close() # 可视化所有特征的全局影响看特征对风险的正负向作用 plt.figure(figsize(10, 8)) shap.summary_plot(shap_values, X_test, feature_namesfeature_names, plot_typebeeswarm) plt.savefig(shap_summary_plot.png, dpi300, bbox_inchestight) plt.close() # 2. 模型融合进一步提升稳定性用AdaBoost和XGBoost加权融合 def ensemble_predict(ada_model, xgb_model, X, weight_ada0.3, weight_xgb0.7): # 加权融合概率 y_prob_ada ada_model.predict_proba(X)[:, 1] y_prob_xgb xgb_model.predict_proba(X)[:, 1] return weight_ada * y_prob_ada weight_xgb * y_prob_xgb # 融合模型评估 ensemble_prob ensemble_predict(ada_model_opt, xgb_best, X_test) ensemble_auc roc_auc_score(y_test, ensemble_prob) ensemble_ks ks_2samp(ensemble_prob[y_test1], ensemble_prob[y_test0]).statistic print(f融合模型 - AUC: {ensemble_auc:.4f}, KS: {ensemble_ks:.4f}) # 正常输出AUC≈0.86KS≈0.47比单一模型好 # 3. 保存最优模型用joblib比pickle更适合大数据 joblib.dump(xgb_best, credit_scorecard_xgb.pkl) joblib.dump(ensemble_predict, ensemble_predictor.pkl)# 1. 预测API脚本credit_api.py from flask import Flask, request, jsonify import joblib import pandas as pd app Flask(__name__) # 加载模型 model joblib.load(credit_scorecard_xgb.pkl) feature_names [Age, Monthly_Income, Loan_Amount, Loan_Term, Number_of_Credit_Accounts, Debt_Income_Ratio, Credit_Density, Long_Term_Loan, Purpose_Encoded, Credit_History_Encoded] app.route(/predict, methods[POST]) def predict(): try: # 接收请求数据 data request.get_json() # 转换为DataFrame保证特征顺序和训练时一致 input_df pd.DataFrame([data], columnsfeature_names) # 预测违约概率 default_prob model.predict_proba(input_df)[:, 1][0] # 转换为评分金融评分卡常用0-1000分 score int(1000 - default_prob * 500) # 风险等级判定 if score 800: level 低风险A suggestion 可直接放款 elif score 600: level 中风险B suggestion 需人工审核 else: level 高风险C suggestion 拒绝放款 # 返回结果 return jsonify({ default_probability: round(default_prob, 4), credit_score: score, risk_level: level, suggestion: suggestion }) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: app.run(host0.0.0.0, port5000)# 2. Dockerfile构建镜像避免环境冲突 FROM python:3.9-slim # 设置工作目录 WORKDIR /app # 复制依赖文件 COPY requirements.txt . # 安装依赖指定版本保证复现性 RUN pip install --no-cache-dir -r requirements.txt # 复制代码和模型 COPY credit_api.py . COPY credit_scorecard_xgb.pkl . # 暴露端口 EXPOSE 5000 # 启动服务 CMD [python, credit_api.py]# 3. requirements.txt依赖列表 flask2.0.1 xgboost1.5.1 lightgbm3.3.2 scikit-learn1.0.2 pandas1.3.5 numpy1.21.6 matplotlib3.5.3 shap0.40.0 joblib1.1.0六、最后项目总结实战忠告这个项目比AI生成的版本多了3个核心价值1. 每个操作都有业务逻辑支撑不是纯技术堆砌2. 补全了新手必踩的坑如随机划分数据集、过拟合3. 输出了风控部门能直接用的洞察风险特征、评分标准。6.1 模型选型建议真实业务决策逻辑小数据量1万样本选AdaBoost训练快且易解释KS值能到0.35-0.4。大数据量10万样本选LightGBM速度比XGBoost快3倍内存占用少。强监管场景用“XGBoostTreeSHAP逻辑回归”混合模型既保证性能又能解释。6.2 新手忠告我踩过的坑别再踩了1. 别沉迷调参先保证数据质量我曾花一周调参提升AUC 0.02后来发现是缺失值处理错了改完直接提升0.082. 可解释性比准确率重要金融场景里AUC 0.8且能解释的模型比AUC 0.85的黑盒模型更有用3. 一定要做业务校验比如模型说“年龄越大风险越高”但实际60岁以上申请的都是优质用户这时候要检查特征工程是不是错了。七、项目源代码 德国信用评分卡项目 - Boosting算法实战 作者free-elcmacom 功能完整的信用风险评估机器学习流水线 import pandas as pd import numpy as np import warnings import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import AdaBoostClassifier from sklearn.metrics import roc_auc_score, roc_curve, classification_report, confusion_matrix import xgboost as xgb import lightgbm as lgb import joblib from scipy.stats import ks_2samp import shap # 设置中文字体和样式 plt.rcParams[font.sans-serif] [SimHei, DejaVu Sans] plt.rcParams[axes.unicode_minus] False warnings.filterwarnings(ignore) # 1. 数据获取与基础预处理 def load_german_credit_data(): 加载德国信用数据集从UCI下载或使用本地缓存 try: # 尝试从UCI机器学习仓库直接下载 url https://archive.ics.uci.edu/ml/machine-learning-databases/statlog/german/german.data column_names [ checking_account, duration, credit_history, purpose, credit_amount, savings_account, employment_since, installment_rate, personal_status_sex, other_debtors, residence_since, property, age, other_installment_plans, housing, existing_credits, job, dependents, telephone, foreign_worker, Risk ] print(正在从UCI下载数据集...) data pd.read_csv(url, sep , headerNone, namescolumn_names, na_values[?]) print(数据集下载成功) # 保存到本地供后续使用 data.to_csv(german_credit_data.csv, indexFalse, encodingutf-8) return data except Exception as e: print(f网络下载失败: {e}) print(尝试从本地加载...) try: data pd.read_csv(german_credit_data.csv, encodingutf-8) print(本地数据集加载成功) return data except: print(本地文件不存在创建模拟数据集...) return create_sample_data() def create_sample_data(): 创建模拟数据集用于演示 np.random.seed(42) n_samples 1000 # 创建与真实数据集相似的特征 data pd.DataFrame({ checking_account: np.random.choice([A11, A12, A13, A14], n_samples), duration: np.random.randint(6, 72, n_samples), credit_history: np.random.choice([A30, A31, A32, A33, A34], n_samples), purpose: np.random.choice([A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A410], n_samples), credit_amount: np.random.randint(250, 15000, n_samples), savings_account: np.random.choice([A61, A62, A63, A64, A65], n_samples), employment_since: np.random.choice([A71, A72, A73, A74, A75], n_samples), installment_rate: np.random.randint(1, 5, n_samples), personal_status_sex: np.random.choice([A91, A92, A93, A94], n_samples), other_debtors: np.random.choice([A101, A102, A103], n_samples), residence_since: np.random.randint(1, 5, n_samples), property: np.random.choice([A121, A122, A123, A124], n_samples), age: np.random.randint(19, 75, n_samples), other_installment_plans: np.random.choice([A141, A142, A143], n_samples), housing: np.random.choice([A151, A152, A153], n_samples), existing_credits: np.random.randint(1, 5, n_samples), job: np.random.choice([A171, A172, A173, A174], n_samples), dependents: np.random.randint(1, 3, n_samples), telephone: np.random.choice([A191, A192], n_samples), foreign_worker: np.random.choice([A201, A202], n_samples), }) # 创建目标变量违约概率 risk_score ( (data[age] 25) * 0.3 (data[age] 60) * 0.2 (data[credit_amount] 10000) * 0.3 (data[duration] 48) * 0.2 np.random.normal(0, 0.1, n_samples) ) data[Risk] (risk_score 0.5).astype(int) 1 # 1好, 2坏 print(模拟数据集创建完成) return data def evaluate_model(model, X, y, name): 评估模型性能 y_prob model.predict_proba(X)[:, 1] auc roc_auc_score(y, y_prob) # KS值衡量模型区分能力金融场景要求0.3 ks ks_2samp(y_prob[y1], y_prob[y0]).statistic print(f{name:15s} - AUC: {auc:.4f}, KS: {ks:.4f}) return auc, ks, y_prob # 主程序开始 print( * 60) print(德国信用评分卡项目 - Boosting算法实战) print( * 60) # 加载数据 data load_german_credit_data() print(f\n数据集形状: {data.shape}) print(f目标变量分布: {data[Risk].value_counts().to_dict()}) # 目标变量转换 data[default] data[Risk].map({1: 0, 2: 1}) data.drop(Risk, axis1, inplaceTrue) # 特征工程 print(\n * 60) print(开始特征工程...) print( * 60) # 估算月收入 def estimate_monthly_income(row): base_income 2000 employment_factor { A71: 0.8, A72: 1.0, A73: 1.2, A74: 1.5, A75: 2.0 }.get(row[employment_since], 1.0) job_factor { A171: 0.8, A172: 1.0, A173: 1.5, A174: 2.0 }.get(row[job], 1.0) return base_income * employment_factor * job_factor np.random.normal(0, 200) data[estimated_monthly_income] data.apply(estimate_monthly_income, axis1) # 创建衍生特征 data[debt_income_ratio] data[credit_amount] / (data[estimated_monthly_income] * 12) data[monthly_payment] data[credit_amount] / data[duration] data[payment_income_ratio] data[monthly_payment] / data[estimated_monthly_income] data[age_group] pd.cut(data[age], bins[18, 25, 35, 50, 65, 100], labels[18-25, 26-35, 36-50, 51-65, 66]) # 分类特征编码 checking_mapping {A11: 0, A12: 1, A13: 2, A14: 3} if set(data[checking_account].unique()).issuperset(set(checking_mapping.keys())): data[checking_account_encoded] data[checking_account].map(checking_mapping) savings_mapping {A61: 0, A62: 1, A63: 2, A64: 3, A65: 4} if set(data[savings_account].unique()).issuperset(set(savings_mapping.keys())): data[savings_account_encoded] data[savings_account].map(savings_mapping) employment_mapping {A71: 0, A72: 1, A73: 2, A74: 3, A75: 4} if set(data[employment_since].unique()).issuperset(set(employment_mapping.keys())): data[employment_since_encoded] data[employment_since].map(employment_mapping) credit_history_mapping {A30: 0, A31: 1, A32: 2, A33: 3, A34: 4} if set(data[credit_history].unique()).issuperset(set(credit_history_mapping.keys())): data[credit_history_encoded] data[credit_history].map(credit_history_mapping) # 数据划分 print(\n数据划分...) # 选择特征 numeric_features [ duration, credit_amount, installment_rate, residence_since, age, existing_credits, dependents, estimated_monthly_income, debt_income_ratio, monthly_payment, payment_income_ratio ] encoded_features [ checking_account_encoded, savings_account_encoded, employment_since_encoded, credit_history_encoded ] categorical_features [purpose, personal_status_sex, property, housing] # 创建特征矩阵 X_numeric data[numeric_features].copy() X_encoded data[encoded_features].copy() X_categorical pd.get_dummies(data[categorical_features], prefixcategorical_features, drop_firstTrue) X pd.concat([X_numeric, X_encoded, X_categorical], axis1) y data[default] feature_names X.columns.tolist() # 按时间顺序划分数据集 data data.sort_values(duration).reset_index(dropTrue) train_size int(0.7 * len(data)) val_size int(0.15 * len(data)) X_train, y_train X.iloc[:train_size], y.iloc[:train_size] X_val, y_val X.iloc[train_size:train_sizeval_size], y.iloc[train_size:train_sizeval_size] X_test, y_test X.iloc[train_sizeval_size:], y.iloc[train_sizeval_size:] print(f训练集: {len(X_train)} 样本违约率: {y_train.mean():.2%}) print(f验证集: {len(X_val)} 样本违约率: {y_val.mean():.2%}) print(f测试集: {len(X_test)} 样本违约率: {y_test.mean():.2%}) # 数据标准化 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_val_scaled scaler.transform(X_val) X_test_scaled scaler.transform(X_test) # 2. 模型构建 print(\n * 60) print(开始模型训练...) print( * 60) # 2.1 基准模型 print(\n1. 基准模型训练...) lr_model LogisticRegression(class_weightbalanced, max_iter1000, C0.1, random_state42) lr_model.fit(X_train_scaled, y_train) lr_auc, lr_ks, _ evaluate_model(lr_model, X_test_scaled, y_test, 逻辑回归) dt_model DecisionTreeClassifier(max_depth3, min_samples_leaf20, class_weightbalanced, random_state42) dt_model.fit(X_train_scaled, y_train) dt_auc, dt_ks, _ evaluate_model(dt_model, X_test_scaled, y_test, 决策树) # 2.2 AdaBoost模型 - 修复algorithm参数问题 print(\n2. AdaBoost模型训练...) base_clf DecisionTreeClassifier(max_depth2, min_samples_leaf15, class_weightbalanced, random_state42) # 追踪AdaBoost训练过程 train_aucs [] val_aucs [] # 修复移除algorithm参数或使用正确的值 for i in range(1, 301, 30): ada_model_temp AdaBoostClassifier( estimatorbase_clf, n_estimatorsi, learning_rate0.05, random_state42 # 移除algorithm参数使用默认值 ) ada_model_temp.fit(X_train_scaled, y_train) train_auc roc_auc_score(y_train, ada_model_temp.predict_proba(X_train_scaled)[:, 1]) val_auc roc_auc_score(y_val, ada_model_temp.predict_proba(X_val_scaled)[:, 1]) train_aucs.append(train_auc) val_aucs.append(val_auc) # 选择最优迭代次数验证集AUC最高 best_iter 30 * (val_aucs.index(max(val_aucs)) 1) print(f最优迭代次数: {best_iter}) # 训练最优AdaBoost模型 ada_model AdaBoostClassifier( estimatorbase_clf, n_estimatorsbest_iter, learning_rate0.05, random_state42 ) ada_model.fit(X_train_scaled, y_train) ada_auc, ada_ks, _ evaluate_model(ada_model, X_test_scaled, y_test, AdaBoost) # 2.3 XGBoost模型 - 修复版本兼容性问题 print(\n3. XGBoost模型训练...) # 计算正负样本比例用于解决不平衡 scale_pos_weight len(y_train[y_train0]) / len(y_train[y_train1]) # 检查XGBoost版本并适配参数 xgb_version xgb.__version__ print(f检测到XGBoost版本: {xgb_version}) # 根据版本调整参数 if xgb_version.startswith(2.): # XGBoost 2.0 版本 xgb_model xgb.XGBClassifier( objectivebinary:logistic, eval_metriclogloss, # XGBoost 2.0 不再需要 use_label_encoder scale_pos_weightscale_pos_weight, max_depth5, learning_rate0.1, n_estimators150, subsample0.8, colsample_bytree0.8, early_stopping_rounds20, # 在构造函数中设置early_stopping_rounds random_state42 ) xgb_model.fit( X_train_scaled, y_train, eval_set[(X_val_scaled, y_val)], verboseFalse ) else: # XGBoost 1.x 版本 xgb_model xgb.XGBClassifier( objectivebinary:logistic, eval_metriclogloss, use_label_encoderFalse, # 1.x版本需要这个参数 scale_pos_weightscale_pos_weight, max_depth5, learning_rate0.1, n_estimators150, subsample0.8, colsample_bytree0.8, random_state42 ) xgb_model.fit( X_train_scaled, y_train, eval_set[(X_val_scaled, y_val)], early_stopping_rounds20, # 在fit方法中设置early_stopping_rounds verboseFalse ) xgb_auc, xgb_ks, _ evaluate_model(xgb_model, X_test_scaled, y_test, XGBoost) # 2.4 LightGBM模型 - 修复版本兼容性问题 print(\n4. LightGBM模型训练...) # 检查LightGBM版本 lgb_version lgb.__version__ print(f检测到LightGBM版本: {lgb_version}) # 根据版本调整参数 if lgb_version.startswith(4.): # LightGBM 4.0 版本 - 使用新的API lgb_model lgb.LGBMClassifier( objectivebinary, metricauc, class_weightbalanced, max_depth5, num_leaves31, learning_rate0.1, n_estimators150, subsample0.8, colsample_bytree0.8, early_stopping_round20, # 注意参数名是early_stopping_round单数 verbose-1, # 在构造函数中设置verbose-1表示不输出日志 random_state42 ) # LightGBM 4.0 版本fit方法中不需要verbose参数 lgb_model.fit( X_train_scaled, y_train, eval_set[(X_val_scaled, y_val)] ) else: # LightGBM 3.x 或更早版本 lgb_model lgb.LGBMClassifier( objectivebinary, metricauc, class_weightbalanced, max_depth5, num_leaves31, learning_rate0.1, n_estimators150, subsample0.8, colsample_bytree0.8, random_state42 ) # LightGBM 3.x 版本fit方法中需要verbose参数 lgb_model.fit( X_train_scaled, y_train, eval_set[(X_val_scaled, y_val)], early_stopping_rounds20, verboseFalse ) lgb_auc, lgb_ks, _ evaluate_model(lgb_model, X_test_scaled, y_test, LightGBM) # 3. 模型对比分析 print(\n * 60) print(模型对比分析...) print( * 60) # 3.1 ROC曲线对比 plt.figure(figsize(10, 8)) models [ (逻辑回归, lr_model, lr_auc), (决策树, dt_model, dt_auc), (AdaBoost, ada_model, ada_auc), (XGBoost, xgb_model, xgb_auc), (LightGBM, lgb_model, lgb_auc) ] for name, model, auc_score in models: y_prob model.predict_proba(X_test_scaled)[:, 1] fpr, tpr, _ roc_curve(y_test, y_prob) plt.plot(fpr, tpr, labelf{name} (AUC{auc_score:.4f}), linewidth2) plt.plot([0, 1], [0, 1], k--, linewidth1) plt.xlabel(假正例率 (FPR), fontsize12) plt.ylabel(真正例率 (TPR), fontsize12) plt.title(Boosting模型ROC曲线对比, fontsize14, fontweightbold) plt.legend(loclower right) plt.grid(True, alpha0.3) plt.savefig(model_roc_comparison.png, dpi300, bbox_inchestight) plt.close() print(✓ ROC曲线已保存为 model_roc_comparison.png) # 3.2 特征重要性对比 print(\n生成特征重要性对比图...) # 获取特征重要性 ada_importance np.mean([tree.feature_importances_ for tree in ada_model.estimators_], axis0) xgb_importance xgb_model.feature_importances_ lgb_importance lgb_model.feature_importances_ # 创建重要性DataFrame importance_df pd.DataFrame({ 特征: feature_names, AdaBoost: ada_importance, XGBoost: xgb_importance, LightGBM: lgb_importance }) importance_df[平均重要性] importance_df[[AdaBoost, XGBoost, LightGBM]].mean(axis1) top_features importance_df.sort_values(平均重要性, ascendingFalse).head(10) # 绘制特征重要性对比图 fig, ax plt.subplots(figsize(12, 8)) x np.arange(len(top_features)) width 0.25 ax.barh(x - width, top_features[AdaBoost], width, labelAdaBoost, alpha0.8) ax.barh(x, top_features[XGBoost], width, labelXGBoost, alpha0.8) ax.barh(x width, top_features[LightGBM], width, labelLightGBM, alpha0.8) ax.set_yticks(x) ax.set_yticklabels(top_features[特征]) ax.set_xlabel(特征重要性, fontsize12) ax.set_title(Top 10 特征重要性对比, fontsize14, fontweightbold) ax.legend() ax.grid(True, alpha0.3, axisx) plt.tight_layout() plt.savefig(feature_importance_comparison.png, dpi300, bbox_inchestight) plt.close() print(✓ 特征重要性对比图已保存为 feature_importance_comparison.png) # 3.3 AdaBoost迭代过程可视化 print(\n生成AdaBoost迭代过程图...) plt.figure(figsize(10, 6)) iterations range(1, 301, 30) plt.plot(iterations, train_aucs, o-, label训练集AUC, linewidth2, markersize8) plt.plot(iterations, val_aucs, s-, label验证集AUC, linewidth2, markersize8) plt.axvline(xbest_iter, colorred, linestyle--, labelf最优迭代({best_iter}), linewidth2) plt.xlabel(迭代次数弱分类器数量, fontsize12) plt.ylabel(AUC值, fontsize12) plt.title(AdaBoost迭代过程性能变化, fontsize14, fontweightbold) plt.legend() plt.grid(True, alpha0.3) plt.savefig(ada_boost_iteration.png, dpi300, bbox_inchestight) plt.close() print(✓ AdaBoost迭代过程图已保存为 ada_boost_iteration.png) # 3.4 模型性能总结 print(\n * 60) print(模型性能总结) print( * 60) performance_summary pd.DataFrame({ 模型: [逻辑回归, 决策树, AdaBoost, XGBoost, LightGBM], AUC: [lr_auc, dt_auc, ada_auc, xgb_auc, lgb_auc], KS值: [lr_ks, dt_ks, ada_ks, xgb_ks, lgb_ks] }) performance_summary[KS是否达标] performance_summary[KS值] 0.3 print(performance_summary.sort_values(AUC, ascendingFalse)) # 4. 模型解释与部署准备 print(\n * 60) print(模型解释与部署准备...) print( * 60) # 4.1 SHAP解释仅对XGBoost # 4.1 SHAP解释仅对XGBoost try: print(生成SHAP解释图...) explainer shap.TreeExplainer(xgb_model) shap_values explainer.shap_values(X_test_scaled) # 摘要图 plt.figure(figsize(12, 8)) shap.summary_plot(shap_values, X_test_scaled, feature_namesfeature_names, showFalse) plt.tight_layout() plt.savefig(shap_summary.png, dpi300, bbox_inchestight) plt.close() print(✓ SHAP摘要图已保存为 shap_summary.png) # 单个样本解释 - 修复瀑布图代码 plt.figure(figsize(12, 6)) # 方法1使用force_plot更稳定 # shap.force_plot(explainer.expected_value, shap_values[0], # X_test_scaled[0], feature_namesfeature_names, showFalse, matplotlibTrue) # 方法2使用waterfall_plot需要创建Explanation对象 try: # 尝试创建Explanation对象 exp shap.Explanation(shap_values[0], explainer.expected_value, dataX_test_scaled[0], feature_namesfeature_names) shap.plots.waterfall(exp, max_display10, showFalse) except: # 如果失败使用force_plot print(瀑布图失败使用force_plot代替) shap.force_plot(explainer.expected_value, shap_values[0], X_test_scaled[0], feature_namesfeature_names, showFalse, matplotlibTrue) plt.tight_layout() plt.savefig(shap_waterfall.png, dpi300, bbox_inchestight) plt.close() print(✓ SHAP瀑布图已保存为 shap_waterfall.png) except Exception as e: print(fSHAP解释失败需要安装shap库: pip install shap: {e}) # 4.2 模型融合 print(\n尝试模型融合...) def ensemble_predict(models, weights, X): 加权融合多个模型的预测 predictions np.zeros(len(X)) for model, weight in zip(models, weights): if hasattr(model, predict_proba): predictions weight * model.predict_proba(X)[:, 1] else: predictions weight * model.predict(X) return predictions # 使用AdaBoost和XGBoost融合 ensemble_proba ensemble_predict( models[ada_model, xgb_model], weights[0.3, 0.7], XX_test_scaled ) ensemble_auc roc_auc_score(y_test, ensemble_proba) ensemble_ks ks_2samp(ensemble_proba[y_test1], ensemble_proba[y_test0]).statistic print(f融合模型 (AdaBoostXGBoost) - AUC: {ensemble_auc:.4f}, KS: {ensemble_ks:.4f}) # 4.3 保存模型和预处理对象 print(\n保存模型和预处理对象...) joblib.dump(scaler, scaler.pkl) joblib.dump(xgb_model, credit_scorecard_xgb.pkl) joblib.dump(feature_names, feature_names.pkl) print(✓ 模型已保存: scaler.pkl, credit_scorecard_xgb.pkl, feature_names.pkl) # 5. 部署代码生成 print(\n * 60) print(生成部署文件...) print( * 60) # 5.1 生成Flask API脚本 flask_api_code 信用评分卡API服务 使用方法: python credit_api.py from flask import Flask, request, jsonify import joblib import pandas as pd import numpy as np app Flask(__name__) # 加载模型和预处理对象 scaler joblib.load(scaler.pkl) model joblib.load(credit_scorecard_xgb.pkl) feature_names joblib.load(feature_names.pkl) app.route(/health, methods[GET]) def health_check(): 健康检查接口 return jsonify({status: healthy, model: credit_scorecard}) app.route(/predict, methods[POST]) def predict(): 信用评分预测接口 try: # 获取请求数据 data request.get_json() # 检查必需特征 required_features feature_names for feature in required_features: if feature not in data: return jsonify({ success: False, error: f缺失特征: {feature} }), 400 # 转换为DataFrame并确保特征顺序 input_df pd.DataFrame([data], columnsfeature_names) # 数据预处理 X_scaled scaler.transform(input_df) # 预测违约概率 default_prob model.predict_proba(X_scaled)[:, 1][0] # 转换为信用评分 (300-850分类似FICO评分) credit_score int(850 - default_prob * 550) # 风险等级判定 if credit_score 700: risk_level 低风险 suggestion 建议批准可提供优惠利率 elif credit_score 600: risk_level 中风险 suggestion 建议人工审核可考虑批准但需提高利率 else: risk_level 高风险 suggestion 建议拒绝申请 # 返回结果 return jsonify({ success: True, default_probability: round(default_prob, 4), credit_score: credit_score, risk_level: risk_level, suggestion: suggestion }) except Exception as e: return jsonify({ success: False, error: str(e) }), 400 app.route(/batch_predict, methods[POST]) def batch_predict(): 批量预测接口 try: data request.get_json() records data.get(records, []) if not records: return jsonify({success: False, error: 未提供数据}), 400 # 批量处理 results [] for record in records: input_df pd.DataFrame([record], columnsfeature_names) X_scaled scaler.transform(input_df) default_prob model.predict_proba(X_scaled)[:, 1][0] credit_score int(850 - default_prob * 550) if credit_score 700: risk_level 低风险 elif credit_score 600: risk_level 中风险 else: risk_level 高风险 results.append({ default_probability: round(default_prob, 4), credit_score: credit_score, risk_level: risk_level }) return jsonify({ success: True, predictions: results }) except Exception as e: return jsonify({ success: False, error: str(e) }), 400 if __name__ __main__: print(信用评分卡API服务启动...) print(接口地址: http://localhost:5000) print(可用接口:) print( GET /health 健康检查) print( POST /predict 单条预测) print( POST /batch_predict 批量预测) app.run(host0.0.0.0, port5000, debugFalse) with open(credit_api.py, w, encodingutf-8) as f: f.write(flask_api_code) print(✓ Flask API脚本已生成: credit_api.py) # 5.2 生成Dockerfile dockerfile_code # 信用评分卡Docker镜像 FROM python:3.9-slim # 设置工作目录 WORKDIR /app # 复制依赖文件 COPY requirements.txt . # 安装系统依赖 RUN apt-get update apt-get install -y gcc g rm -rf /var/lib/apt/lists/* # 安装Python依赖 RUN pip install --no-cache-dir --upgrade pip \ pip install --no-cache-dir -r requirements.txt # 复制应用代码和模型 COPY credit_api.py . COPY scaler.pkl . COPY credit_scorecard_xgb.pkl . COPY feature_names.pkl . # 暴露端口 EXPOSE 5000 # 健康检查 HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit 1 # 启动命令 CMD [python, credit_api.py] with open(Dockerfile, w, encodingutf-8) as f: f.write(dockerfile_code) print(✓ Dockerfile已生成: Dockerfile) # 5.3 生成requirements.txt requirements_code flask2.3.3 pandas2.0.3 numpy1.24.4 scikit-learn1.3.0 xgboost1.7.6 lightgbm4.1.0 joblib1.3.2 matplotlib3.7.2 seaborn0.12.2 shap0.42.1 with open(requirements.txt, w, encodingutf-8) as f: f.write(requirements_code) print(✓ 依赖文件已生成: requirements.txt) # 5.4 生成测试API的示例代码 test_api_code 测试信用评分卡API import requests import json def test_health(): 测试健康检查接口 response requests.get(http://localhost:5000/health) print(f健康检查: {response.json()}) def test_predict(): 测试预测接口 # 创建一个示例请求数据请根据实际特征调整 sample_data { duration: 24, credit_amount: 5000, installment_rate: 4, residence_since: 2, age: 35, existing_credits: 2, dependents: 1, estimated_monthly_income: 2500, debt_income_ratio: 0.25, monthly_payment: 208.33, payment_income_ratio: 0.083, checking_account_encoded: 1, savings_account_encoded: 2, employment_since_encoded: 3, credit_history_encoded: 1, # 添加其他特征的默认值 purpose_A41: 0, purpose_A410: 0, purpose_A42: 0, purpose_A43: 1, purpose_A44: 0, purpose_A45: 0, purpose_A46: 0, purpose_A48: 0, purpose_A49: 0, personal_status_sex_A92: 1, personal_status_sex_A93: 0, personal_status_sex_A94: 0, property_A122: 1, property_A123: 0, property_A124: 0, housing_A152: 1, housing_A153: 0 } response requests.post( http://localhost:5000/predict, headers{Content-Type: application/json}, datajson.dumps(sample_data) ) if response.status_code 200: result response.json() print(f预测结果: {json.dumps(result, indent2, ensure_asciiFalse)}) else: print(f请求失败: {response.status_code}) print(response.text) if __name__ __main__: print(测试信用评分卡API...) test_health() print() test_predict() with open(test_api.py, w, encodingutf-8) as f: f.write(test_api_code) print(✓ API测试脚本已生成: test_api.py) # 6. 项目总结 print(\n * 60) print(项目总结) print( * 60) print(\n 项目完成以下是生成的文件:) print(\n 分析报告:) print(1. model_roc_comparison.png - ROC曲线对比图) print(2. feature_importance_comparison.png - 特征重要性对比图) print(3. ada_boost_iteration.png - AdaBoost迭代过程图) print(4. shap_summary.png - SHAP特征重要性摘要图) print(5. shap_waterfall.png - SHAP单个样本解释瀑布图) print(\n 模型文件:) print(1. scaler.pkl - 数据标准化器) print(2. credit_scorecard_xgb.pkl - 训练好的XGBoost模型) print(3. feature_names.pkl - 特征名称列表) print(\n 部署文件:) print(1. credit_api.py - Flask API服务) print(2. Dockerfile - Docker容器配置) print(3. requirements.txt - Python依赖列表) print(4. test_api.py - API测试脚本) print(\n 模型性能总结:) print(f最佳模型: XGBoost (AUC{xgb_auc:.4f}, KS{xgb_ks:.4f})) print(f融合模型: AdaBoostXGBoost (AUC{ensemble_auc:.4f})) print(\n * 60) print(使用说明:) print( * 60) print(\n1. 启动API服务:) print( python credit_api.py) print(\n2. 测试API:) print( python test_api.py) print(\n3. Docker部署:) print( docker build -t credit-scorecard .) print( docker run -p 5000:5000 credit-scorecard) print(\n4. 安装SHAP库如果需要特征解释:) print( pip install shap) print(\n * 60) print(信用评分卡项目完成) print( * 60)1. ROC曲线对比图 (model_roc_comparison.png)图片内容应该包含5条不同颜色的ROC曲线逻辑回归、决策树、AdaBoost、XGBoost、LightGBM一条黑色的对角线随机猜测基准线每条曲线上标注对应模型的AUC值X轴假正例率FPRY轴真正例率TPR图例说明各个曲线对应的模型如何解读曲线位置曲线越靠近左上角模型性能越好AUC值面积越大越好理论上最大为1.00.5-0.7模型效果一般0.7-0.8模型效果较好0.8-0.9模型效果很好0.9-1.0模型效果极好对角线对比所有曲线都应该在对角线之上否则模型不如随机猜测实际意义您可以直观比较哪个模型在信用风险评估上表现最佳XGBoost和LightGBM通常应该表现最好逻辑回归作为基准线性能应该相对较低但稳定2. 特征重要性对比图 (feature_importance_comparison.png)图片内容应该包含横向条形图展示Top 10最重要的特征每个特征对应三个并排的条形分别代表AdaBoost、XGBoost、LightGBM的特征重要性X轴特征重要性分数0-1之间Y轴特征名称如何解读排名靠前的特征对预测违约最重要的特征特征重要性的一致性如果三个模型都认为某个特征很重要说明这个特征确实关键如果某个特征只在某个模型中重要可能是该模型的特有发现重要特征示例根据您的特征工程duration贷款期限通常越长风险越高credit_amount贷款金额通常越大风险越高debt_income_ratio负债收入比越高风险越大age年龄U形关系年轻人和老年人风险较高实际意义了解哪些因素最影响信用风险评估可以指导业务决策比如重点关注高负债收入比的申请人用于模型解释和合规要求3. AdaBoost迭代过程图 (ada_boost_iteration.png)图片内容应该包含两条曲线训练集AUC蓝色和验证集AUC橙色X轴迭代次数1-300间隔30Y轴AUC值一条垂直的红色虚线标记最优迭代次数如何解读训练集曲线随着迭代增加训练集AUC应该持续上升验证集曲线先上升后可能趋于平稳或下降过拟合判断如果验证集AUC在某个点后开始下降说明模型过拟合了最优迭代次数应该选择验证集AUC最高的点模型稳定性验证集曲线越平稳模型越稳定实际意义展示Boosting算法的学习过程帮助确定合适的迭代次数避免过拟合理解集成学习如何通过组合弱学习器提升性能4. SHAP特征重要性摘要图 (shap_summary.png)图片内容应该包含一个包含多个小点的图Y轴特征名称按重要性从上到下排列X轴SHAP值特征对模型输出的影响颜色红色表示特征值高蓝色表示特征值低如何解读特征位置越靠上的特征对模型影响越大SHAP值分布正值右半部分增加违约概率负值左半部分降低违约概率颜色模式红色点主要在右侧高特征值增加违约概率蓝色点主要在左侧低特征值降低违约概率分散程度点的分散程度表示该特征影响的变异性实际意义比传统特征重要性提供更多信息方向和大小可以理解每个特征如何影响具体预测符合可解释AI的要求对金融监管很重要5. SHAP瀑布图 (shap_waterfall.png)图片内容应该包含一个类似瀑布的图表底部基准值所有样本的平均预测概率中间各个特征对最终预测的贡献正向或负向顶部最终预测值每个条的颜色红色表示增加违约概率蓝色表示降低如何解读基准值不考虑具体特征时平均的违约概率特征贡献从上到下显示每个特征如何改变预测方向性向右的箭头增加违约概率向左的箭头降低违约概率累积效应所有特征贡献叠加得到最终预测

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询