资讯动态

Python机器学习数据建模实操:从数据清洗到模型避坑的完整指南

发布时间:2026/10/14 14:44:24 来源:尧图企业网站定制
简介面向机器学习初学者、数据分析师与竞赛选手这份Python建模代码集覆盖从数据清洗、特征探索、模型训练到结果评估的完整流程旨在解决实际建模中模型选型与调优难题适合从零到一的系统学习。压缩包共184个文件以117个ipynb代码笔记为核心这些笔记可直接在Jupyter环境运行另附35个csv数据集、13个txt说明、8个png可视化图表及少量xlsx表格整体大小约304MB。代码内含广义可加模型GAM、梯度提升决策树GBDT、决策树CART、反向传播神经网络BP与深度神经网络DNN等主流算法每套代码均配套独立数据集既可直接训练验证也便于横向对比不同模型在同类数据上的性能差异。代码中保留了完整训练记录和结果分析适合初学者按步骤跟练也便于从业者快速验证模型效果目前已有110人学习。1. Python机器学习全套代码与数据建模决定成败的前 80% 工作提到“Python 机器学习全套代码”不少人以为拿到一份能跑通的脚本就够了真到做数据建模与分析时才发现最难的不是模型而是把数据收拾干净、让流程可复现。这套思路不是某个现成工具包而是一条完整可复制的建模流水线从环境搭建、数据清洗、特征工程到模型训练、评估与避坑。新手照着一章章走能避免“数据直接进模型、结果只跑一次”的翻车熟手可以把它当项目骨架把精力放在业务问题上。代码都按可直接运行的常见写法组织你只需要把路径、字段名换成自己的数据。读完你会得到一套能扩展到真实项目的最小基座知道参数怎么设、坑在哪。2. 搭一套可复制的建模环境与项目骨架从零到能跑通的最小结构2.1 为什么先订项目骨架而不是直接开 notebook我见过太多建模现场是在一个空白 notebook 里从 pandas 读取开始一路敲到模型评估靠滚动缓存里那些临时变量重复几十次。数据一换、参数一改前面某个单元格忘了重跑后面的结果就全错了。血泪经验是数据建模与分析先有骨架再有模型。骨架决定了你能不能把一次成功复现成一百次而不是靠运气。一个我常用的最小项目结构如下五个目录加一个依赖文件足够支撑中小型建模任务data-modeling/ ├── data/ # 原始数据与清洗后数据按 raw/processed 分层 │ ├── raw/ │ └── processed/ ├── src/ # 核心代码按环节拆分模块 │ ├── config.py │ ├── preprocess.py │ ├── features.py │ └── train.py ├── models/ # 保存训练产物与评估结果 ├── notebooks/ # 探索性分析EDA专用不写正式流程 └── requirements.txt划分原则是notebook 只做探索正式流程全部写进 src 下的模块。data/raw 保存原始文件不动processed 放清洗结果这样任何时候都能回看“数据从哪来、改了什么”。models 目录里同时留一份模型的参数快照和评估指标方便事后对比。这个结构在单人项目里够用多人协作时再按需求拆细。为什么要把依赖文件单独列出来因为没锁依赖的项目三个月后可能因为一个库升级同样的代码跑出完全不同的结果。config.py 我一般只放全局路径、随机种子和公共参数不写任何业务逻辑随机种子统一管理后面调参才谈得上可复现。如果你接手的是别人没做骨架的项目第一件事也不是急着跑通而是先按这个结构把文件归位否则后面每一步排查都要找半天。2.2 依赖管理锁定环境是避免“动一下就翻车”的底线Python 机器学习项目最常见的技术债不是代码写得乱而是环境版本漂移。你用某天的 pandas 和 scikit-learn 调通了一条链路同事或一周后的你重建环境时装上了新版本接口变了、默认参数变了结果对不上。解决办法不是“别升级”而是把当前环境里真实跑过的版本固化下来。我之前维护的某项目三个月后重跑同一个脚本AUC 掉了好几个点排查到最后发现是某个库的最小版本要求变了默认参数跟着变。那次之后环境锁定就成了我的固定动作。常见做法是在虚拟环境里装齐依赖后用下面两条命令生成并规范化依赖清单pip install pandas numpy scikit-learn matplotlib jupyter pip freeze requirements.lock.txt第一条命令安装主干依赖第二条把当前环境已安装的所有包及版本号导出到锁定文件。requirements.lock.txt 是给精确复现用的里面可能有一百多行包含传递依赖如果只想给项目记个大致范围再单独维护一份精简的 requirements.txt只写你用到的顶层库名称和最低版本。我的习惯是锁文件提交进版本管理重建环境时直接执行pip install -r requirements.lock.txt。这样不会有“我机器上能跑啊”的问题。如果你用 conda可以用conda env export environment.yml达到同样效果。说到底这一步是在给建模流程买后悔药环境变差、结果对不上时至少知道回到哪个版本去查。2.3 入口脚本把清洗、特征、训练串成一条流水线骨架目录搭好了最后一个关键动作是写一个入口脚本把各环节按固定顺序串起来。不要在每个 notebook 里重复粘贴清洗代码而是提供一个总入口传入数据路径和输出路径一气跑完。下面是最小可用的入口脚本省略每个模块的内部实现只展示调用顺序# run_pipeline.py import pandas as pd from src.config import config from src.preprocess import clean_data from src.features import build_features from src.train import train_and_evaluate def main(): # 1. 读取原始数据 raw pd.read_csv(config.RAW_DATA_PATH) # 2. 数据清洗缺失值、异常值、重复值 clean clean_data(raw) # 3. 特征构建派生、编码、归一化全部在这里完成 featured build_features(clean) # 4. 训练模型并输出评估指标 metrics, model_path train_and_evaluate(featured) print(metrics:, metrics) print(model saved:, model_path) if __name__ __main__: main()这里的核心逻辑是环节固定、输入输出明确。clean_data 接收 DataFrame 返回 DataFramebuild_features 同样只做变换不做丢弃train_and_evaluate 返回指标与模型路径。每一步都可以单独拿出来做单元测试任何环节出错时只改对应模块不需要重跑整个 notebook。如果数据源路径经常变建议给 main() 加一个命令行参数比如用 argparse 接收--input和--output这样换数据集时不用改代码只改启动命令。参数方面config.py 里至少要有四个东西原始数据路径、清洗后数据输出路径、随机种子、测试集比例。路径用绝对路径或相对路径都行但要统一约定随机种子建议在一个地方声明下游所有 random_state 都引它不要在每个函数里写一个不同的魔数。测试集比例按常见做法设 0.2分类任务记得在划分时开启分层这一点在第 4 章会专门讲。3. 数据清洗与特征构建建模前最花时间、也最值得抠细节的环节3.1 缺失值与异常值先定量看全局再逐列决定策略建模的第一步绝不是选算法而是把每一列的“健康状态”摸清楚。拿到 DataFrame 后我一般先跑一段全局体检看看缺失率、类型、极值分布大概什么情况import pandas as pd import numpy as np df pd.read_csv(data/raw/your_data.csv) # 1. 缺失率概览按降序排列一眼看出重灾列 missing_ratio df.isna().mean().sort_values(ascendingFalse) print(缺失率:\n, missing_ratio[missing_ratio 0]) # 2. 数值列的分布摘要重点看 min/max 是否离谱 numeric_cols df.select_dtypes(include[np.number]).columns print(数值列分布:\n, df[numeric_cols].describe().T)missing_ratio 用 isna().mean() 得到每列缺失百分比只打印存在缺失的列避免输出几十行无信息内容。describe() 输出的是每列计数、均值、标准差、四分位数和最大最小值min 和 max 离四分位数过远时大概率需要查异常值。这一步也顺手检查列类型比如时间列读进来是 object 而不是 datetime后面 pd.to_datetime 转换后出现 NaT又会多一批缺失早点发现能省去来回排查。缺失值的处理没有万能公式按列决定比全局填一个值靠谱。数值列我一般优先用中位数而不是均值因为均值容易受过大的异常值影响偏态严重的列中位数更稳。分类型列用众数填充或者单独填一个“未知”类别。时间类、文本类字段如果缺失比例过高比如超过 50%常见做法是直接丢弃该列免得把大量“不知道”当作信息送进模型。填充本身不复杂复杂的是记住“这些值是我填的”所以处理完最好把每一列的填充策略写进一个说明文件否则三个月后没人记得 0 到底是真的 0 还是缺失补的。异常值识别。一个简单又不依赖分布的思路是用四分位距IQR画边界def flag_outliers_iqr(s: pd.Series) - pd.Series: q1 s.quantile(0.25) q3 s.quantile(0.75) iqr q3 - q1 lower q1 - 1.5 * iqr upper q3 1.5 * iqr return (s lower) | (s upper)这个函数返回一个布尔序列标记出落在 [Q1 - 1.5×IQR, Q3 1.5×IQR] 之外的样本。为什么不用 3σ 法则因为 3σ 隐含正态分布假设真实业务数据大多偏态用均值加减三倍标准差会把正常的长尾样本误杀。IQR 方法不要求分布形态适合先用来自动扫描。需要注意异常值不等于错误值。它们可能代表风险极高的真实样本比如信贷场景里的欺诈交易特征跨度本身就大。所以我的习惯是分两步先用上面的函数找出候选再去业务上确认而不是直接删除。凡是未经确认就大范围删行的基本都是给自己埋坑。3.2 编码与归一化让算法读到对的数值而不是读到玄学机器学习模型只吃数值所以字符串类型必须转成数字量纲差异大的数值列也要统一尺度。这两步做不对模型能跑但结果解释起来就成了玄学。分类型字段的编码先区分是“有序”还是“无序”。学历、会员等级这类有明显顺序的可以用从 0 开始递增的整数映射城市、产品品类这类没有大小之分的一定要用独热编码否则模型会误以为 3 号城市比 1 号城市“大三倍”。用 scikit-learn 的 ColumnTransformer 可以把多个字段的编码统一管理from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder from sklearn.compose import ColumnTransformer # 无序类别字段 nominal_cols [channel, region] # 有序类别字段按业务含义给定顺序 ordinal_cols [member_level] ordinal_categories [[访客, 普通, 会员, 高价值]] preprocessor ColumnTransformer( transformers[ (onehot, OneHotEncoder(handle_unknownignore), nominal_cols), (ordinal, OrdinalEncoder(categoriesordinal_categories), ordinal_cols), ], remainderpassthrough, # 其余列原样保留 )handle_unknownignore 是个关键参数训练集里没出现过的新类别在预测阶段不会被报错而是编码成全零向量。业务上经常出现上个月没有的新产品线这个参数能省去很多线上报警。OrdinalEncoder 的 categories 参数显式传入业务顺序避免算法按字母序排序后得到错乱的级别关系。数值列的归一化要按模型类型来决定而不是无脑用。线性回归、逻辑回归、SVM、KNN 这类对距离或梯度敏感的模型特征尺度不统一会导致某些字段主导结果需要标准化。树模型随机森林、梯度提升树对单调变换不敏感归一化不改变分裂点做不做影响很小。标准化的正确姿势是先 fit 训练集再 transform 训练集和测试集。后面的 5.4 节会专门讲这里的数据泄漏问题这是新手最容易踩的坑。一句话记住归一化用的均值和标准差只能来自训练集。3.3 特征派生与选择业务先验和机器筛选哪边都不能少数据建模和分析里特征决定上限模型只是逼近上限。即使是最强的树模型也必须在原始字段里找到可分割的信息如果原始字段本身就缺信息再深的网络、再宽的集成也救不回来。特征派生先看日期和文本这两类最容易出信息的字段。日期列不要直接当数值传给模型年份、月份、星期、是否月底这些周期信号往往比时间戳本身更有用。df[create_time] pd.to_datetime(df[create_time]) df[create_year] df[create_time].dt.year df[create_month] df[create_time].dt.month df[create_weekday] df[create_time].dt.dayofweek df[create_is_month_end] df[create_time].dt.is_month_end.astype(int)dt.dayofweek 返回 0~60 代表周一is_month_end 是布尔值转成 0/1 后就能被模型当分类特征使用。如果数据没有明显周期性这些派生可能没用但成本极低建议保留。文本类字段常见做法是先取长度、是否含特定关键词等统计量而不是直接把原始文本扔进传统模型。特征选择这块我一般先看相关系数再做模型重要性。相关系数用 DataFrame 的 corr() 可以快速筛出与目标变量线性相关较高的字段但对非线性关系不敏感。随机森林训练后输出 feature_importances_ 表面上看很方便它只能反映该模型在训练集上的分裂收益不反映真正的因果贡献而且对高基数特征有偏向。盲目把重要性排序低的字段删光通常不是最优解。更稳妥的方式是把业务认为必须保留的字段比如业务规则里明确的强变量强制留下再用机器筛选处理剩余字段。这样既尊重业务先验又避免人工拍脑袋漏掉交叉信息。特征数量的控制也有个朴素原则样本量小于几万时特征数尽量控制在几十个以内特征太多模型过拟合的风险会明显上升。如果原始字段实在太多先做相关性聚类把相关性高于 0.8 的字段里挑一个代表留下比直接丢给模型去“学习”要稳得多。4. 模型训练与调参从默认基线到可上线的模型4.1 数据划分与交叉验证不要让评估结果变成黑匣子建模流程里拿到评估指标只是结果更需要关心的是这个指标怎么算出来的。数据划分方式决定了指标的可靠性常见做法是留出法加交叉验证。train_test_split 是每天都会用到的函数但有两个参数经常被忽略。分类任务必须加 stratify保证训练集和测试集里类别比例和原始数据一致回归任务不需要。random_state 固定下来这样每次跑同一个脚本得到相同的划分调参才能对比。from sklearn.model_selection import train_test_split X featured.drop(target, axis1) y featured[target] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42, )test_size0.2 是多数数据集下够用的常见设置数据特别少比如不足几千条时可以把 test_size 调到 0.3宁可训练数据少一点也要保证测试集能代表总体分布。stratify 只在 y 是分类时才需要如果 y 是连续值传进去会直接报错。类别极不平衡时光 stratify 还不够建议分层之后再确认一下最小类别的样本量如果连 30 个都没有这个类别的评估分数基本不可信后续要考虑采样或换指标。交叉验证的作用是不把命运押在一次性划分上。GridSearchCV 内部默认用分层抽样但如果你直接用 cross_val_score也要留意是否传了 cvStratifiedKFold(n_splits5)。普通 KFold 在类别不平衡时会形成某个折里几乎没有正样本的极端情况导致评估分数忽高忽低。用分层折是处理不平衡的底线操作。from sklearn.model_selection import StratifiedKFold, cross_val_score from sklearn.ensemble import RandomForestClassifier model RandomForestClassifier(random_state42) cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) scores cross_val_score(model, X_train, y_train, cvcv, scoringroc_auc) print(CV AUC:, scores.mean(), -, scores.std())shuffleTrue 配合 random_state 是很重要的一组设定不 shuffle 的数据如果本身有排序前 80% 和后 20% 分布不同验证分数会失真。加 shuffle 之后打乱样本顺序让每一折都更接近整体分布。scores 的标准差如果超过 0.03说明模型在不同数据子集上表现波动大这时候要优先怀疑特征质量或样本量而不是继续调参。4.2 基线模型与网格搜索先把对照做出来再谈深度调参很多刚接触机器学习的人上来就调 XGBoost 的十几个参数调了一下午结果还搞不清楚提升到底来自数据还是参数。更可靠的做法是先训练一个默认参数的简单模型当基线然后再逐步优化。我常用逻辑回归当基线尤其分类任务。它简单、可解释、训练快而且给的是一个概率输出。基线不需要多好它的意义是保证后续每个复杂模型都有对照——如果随机森林调参后的效果还不如逻辑回归那说明特征工程有问题而不是模型不够强。这个对照能避免在一堆超参数里自嗨。from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report baseline LogisticRegression(max_iter1000, random_state42) baseline.fit(X_train, y_train) print(classification_report(y_test, baseline.predict(X_test)))max_iter1000 是因为默认 100 在某些数据上无法收敛多给一些迭代次数避免提前停止造成权重没学完。classification_report 一行输出精确率、召回率、F1 和各类样本数比只打印 accuracy 有用得多。如果数据明显不平衡比如正样本只占 5%可以在 LogisticRegression 里加 class_weightbalanced让模型自动调整类别权重基线分数通常能明显上涨。有了基线再用网格搜索找复杂模型的关键参数。网格搜索不是把所有参数都搜一遍那样计算量会爆炸。常见做法是固定不敏感的参数只搜两三个对模型影响最大的参数每个参数给三个左右的候选值就够。from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestClassifier param_grid { n_estimators: [200, 400], max_depth: [5, 7, None], min_samples_leaf: [1, 3], } model RandomForestClassifier(random_state42, n_jobs-1) search GridSearchCV( model, param_grid, cvStratifiedKFold(5, shuffleTrue, random_state42), scoringroc_auc, n_jobs-1, ) search.fit(X_train, y_train) print(best params:, search.best_params_)n_estimators 从 100 加到 400效果基本收敛再翻倍也只是浪费计算时间max_depth 控制在 5 到无限制之间深树更容易过拟合min_samples_leaf 控制每个叶子最少样本数数值越大模型越保守。n_jobs-1 表示用满所有 CPU 核心数据量大时网格搜索的并行度几乎决定等待时间。网格搜索跑完先看 best_params_再看测试集上的表现测试集分数远低于交叉验证分数说明模型过拟合。最后从 search.best_estimator_ 里取最终模型不要手动重建参数避免和搜索结果不一致。4.3 评估指标准确率不够用的时候到底该看哪几个数准确率是最直观的指标但它只在类别均衡时可靠。比如二分类里负样本占 90%模型全部预测为负类准确率也有 90%但这个模型没有任何决策价值。在欺诈检测、流失预测这类正样本稀少的场景里看准确率等于自我欺骗。分类任务的指标组合我按场景这样选业务关注“抓出多少坏人”时看召回率关注“预测为坏人的里面有多少真坏人”时看精确率两者都要兼顾时看 F1。二分类概率输出还能进一步看 ROC 曲线下的面积AUC它不依赖阈值适合做模型排序能力的对比但不是业务口径。回归任务则是另一套。均方误差MSE对大误差惩罚重容易受极端值影响平均绝对误差MAE更稳健但无法体现大误差的严重程度。我一般两个都打印再附一个 R² 看整体拟合度。三个指标放在一起才能判断模型是“大体准确但偶尔离谱”还是“处处小偏差”。from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score y_pred model.predict(X_test) print(MAE:, mean_absolute_error(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) print(R2:, r2_score(y_test, y_pred))RMSE 就是 MSE 开根号单位回到和 y 一致解释起来比 MSE 自然。如果 RMSE 明显大于 MAE说明存在少数预测误差很大的样本这时候要回头查这些样本的特征而不是只调模型。回归里 R² 接近 1 不等于业务可用比如预测对象本身波动很小R² 高但绝对误差照样可能超出业务容忍范围所以指标一定要结合业务口径一起看。5. 机器学习建模避坑五个常见翻车现场与排查方法下面五个问题我几乎每个项目都至少撞上一个按出现频率从高到低排。每一条都按“现象、原因、解决”拆开排查时可以直接对照。5.1 数据泄漏验证集分数虚高的头号原因现象训练时交叉验证分数很高一到线上真实预测就崩或者测试集分数高得让团队不敢相信。最常见的场景是在填充缺失值和归一化时用了全量数据的统计量再把切分后的数据交给模型。原因任何用到“全局信息”的操作都发生在训练集、验证集切分之前导致验证集在无形中接触了训练集的分布信息。模型评估的不是真实泛化能力而是“开卷考试”的分数。数据量越大、特征越多这种虚高越隐蔽因为人眼很难发现哪一步泄漏了。比如填充缺失值用的是全列均值这个均值已经包含测试样本的信息。解决把所有状态型操作填充值的统计量、标准化里的均值和方差、编码器的类别表都放进流水线只在训练集上 fit在验证集和测试集上只 transform。用 scikit-learn 的 Pipeline 把预处理和模型包在一起交叉验证时每一步都自动在训练折上拟合是规避这个坑最省事的手段。我在检查代码时会搜索每一个 fit 调用确保没有在整份数据上执行过。5.2 哑变量陷阱线性模型系数不可解释的根因现象独热编码后用逻辑回归或线性回归特征间出现完全共线性模型系数忽大忽小正负号也和常识相反。用 pandas 的 get_dummies 时最容易触发因为默认会生成原始类别数的完整几列类别之间彼此线性相关。原因一个三分类字段独热编码后会有三列任意一列可以由另外两列推算出来和为 1。线性模型要求特征矩阵列满秩完全共线性会让系数估计不稳定某个系数的微小变化都可能被另一个系数抵消解释性直接归零。解决独热编码时丢弃第一个类别。get_dummies(drop_firstTrue)OneHotEncoder(dropfirst)。这样三分类字段变成两列信息不丢但线性模型的可解释性回归正常。如果不影响树模型这一步可做可不做但统一做了总没错免得后面切线性模型时又要回头补课。排查时可以直接看编码后特征矩阵的秩如果秩小于列数就说明存在完全共线性。5.3 时间序列乱抽样用未来数据预测过去还自我感觉良好现象时间序列数据用随机切分训练集和测试集验证指标高得离谱但拿最近时间段的数据做真实预测时效果明显变差。业务里典型的例子是销量预测训练集里混进了未来几个月的真实销量模型等于提前看到了答案。原因时间序列样本之间存在先后依赖随机切分会把未来的样本混进训练集让模型提前“看到”答案。连续业务中的销量、点击、库存数据尤其明显。即使加 stratify 也解决不了问题因为 stratify 只保证类别比例不保证时间顺序。解决时间序列必须按时间顺序划分用前 80% 的时间段做训练后 20% 做测试不能直接用 train_test_split 的随机划分。交叉验证也要改为 TimeSeriesSplit 或自定义时序折保证每个训练集都在验证集之前。看到数据和事件相关的项目我第一步就会确认有没有按时间分。5.4 归一化时机只统计训练集不碰测试集现象标准化完成后测试集分数正常但重构环境、换一批数据后预测结果整体偏差分布对不上。更隐蔽的版本是训练集和测试集的均值方差被算成同一个测试分数看起来很高实际是“信息穿越”。原因StandardScaler 如果用全量数据 fit均值和标准差里包含了测试集信息。表面上测试分数是真实的因为它的分布已经被“预告”过到了全新样本上真实均值、方差和 fit 用的对不上表现自然下降。这和数据泄漏是同一族问题但单独拎出来是因为很多人不知道标准化这一步也算泄漏。解决先切分再 fit 训练集最后 transform 训练集和测试集。更彻底的做法是把 StandardScaler 放进 Pipelinecross_val_score 的每一折都自动在训练子集上 fit。检查代码时重点看 StandardScaler 有没有出现在 train_test_split 之前。5.5 类别编码当数值用顺序信息与名义信息的混淆现象把城市编号、产品类别直接原样传给模型AUC 看起来也还行但换一个数据重训后结果极不稳定特征重要性里这些编号还总是排得很靠前。把类别列显式转成 category 类型也一样只要底层还是整数模型就会拿它当数值切分。原因整数编码让树模型有机会按编号大小做切割但编号本身没有含义。编号排序与业务真实现象无关时模型学到的是编号的巧合不是规律。比如城市编号 1 和 100 的“距离”是 99但真实业务里两个城市可能毫无关联。解决名义类别做独热编码有序类别才做整数编码并显式指定顺序。如果类别特别多几十个以上可以先做类别频率编码把类别替换成其出现频率比独热编码省维度也比整数编码更能反映真实分布。这个技巧在用户 ID、区域 ID 这类高基数特征上经常能挽回不少效果但也别把 ID 本身当作可学习的数值特征它的作用主要是做分组统计而不是直接进模型。6. 进阶让模型结果不只是一个概率还能被业务信任6.1 用置换重要性替代特征重要性树模型自带的 feature_importances_ 只在训练集上计算分裂收益对高基数特征有偏向跨模型也不可比。置换重要性permutation importance的做法是打乱某一列的值观察模型评估指标下降多少下降越多说明模型越依赖这列。它不吃训练集记忆更接近一个泛化口径。from sklearn.inspection import permutation_importance result permutation_importance( model, X_test, y_test, scoringroc_auc, n_repeats5, random_state42, ) print(result.importances_mean)n_repeats5 表示每列打乱五次取均值打乱次数太少时重要性受随机波动影响大数据量大时n_repeats 降到 3 可以省时间。这个结果我一般只用于解释不直接拿来删特征删特征之前会再结合业务确认。6.2 把预测转换成分层排序验证模型评估指标再好看业务方真正关心的是高分的真的比低分的更有风险吗。我的习惯是把测试集的预测概率按十分位分桶统计每个桶内的实际正样本率做成一张排序校验表。这个检查能从排序能力上验证模型也是调阈值前不可缺少的环节。import pandas as pd result_df pd.DataFrame({ prob: model.predict_proba(X_test)[:, 1], actual: y_test, }) result_df[bin] pd.qcut(result_df[prob], 10, labelsFalse) calib result_df.groupby(bin).agg( avg_prob(prob, mean), actual_rate(actual, mean), ).round(4) print(calib)最终 bin 里 avg_prob 应和 actual_rate 同方向单调变化。如果第 5 桶平均概率 0.5、实际坏样本率 0.3而第 7 桶平均概率 0.6、实际率 0.28说明概率输出校准有问题不能直接拿 0.5 当阈值线需要做概率标定。遇到这类情况我一般会回到数据划分和预处理环节重新检查流程而不是急着调模型。这是我做数据分析项目时收尾固定的步骤指标给结果置换重要性给解释分桶表给业务信心。三者都过模型才敢从实验环境迁到真实流程里。若只留一个验证习惯我会留这张分桶表因为排错成本最低。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑