资讯动态

结构化数据竞赛避坑:缺失值处理、过采样与baseline训练

发布时间:2026/10/27 6:07:52 来源:尧图企业网站定制
上一篇把数据的读取、字段含义和初步的分布摸了一遍这一篇接着往下走聊的是真正决定分数下限的那三件事缺失值处理、过采样、baseline训练。这三件事听起来都像是调个库就能搞定的活儿但我见过太多队伍在这一步埋雷——填充做完才发现把测试集信息漏进了训练集过采样跑完发现交叉验证分数虚高得离谱baseline 明明跑通了但提交上去排名倒数。问题不在代码写错了而在于每一步背后的判断逻辑没想清楚这份数据里的缺失到底是没记录还是没发生不平衡比到什么程度才值得上 SMOTEbaseline 该用哪个模型打底才不至于后面越调越乱这篇就按我自己的实操顺序把这三个环节拆开讲透包括参数怎么算、管道怎么封、坑在哪里适合刚接手这类结构化赛题、想快速搭出一套能迭代的框架的朋友参考。1. 赛题数据整体诊断与处理思路拆解1.1 先给缺失情况画一张体检报告拿到一份几十列、几万行的结构化数据最忌讳的就是上来就df.fillna(0)。我习惯先做三件事把缺失的形状摸清楚。第一件是算缺失率并按列排序把字段分成四档零缺失、轻度缺失低于 5%、中度缺失5% 到 50%、重度缺失超过 50%。这个分档不是为了套公式而是为了决定处理方向——轻度缺失怎么填影响都不大重度缺失要么直接砍列要么保留缺失标记让模型自己判断中间那档才是真正需要花心思的地方。第二件是看缺失的分布是否与标签相关。这一步很多人跳过但它极其关键。具体做法很简单生成一个布尔列is_missing然后分组统计正负样本比例。如果某一列的缺失率在正样本里是 30%、负样本里是 12%那这个缺失本身就是强特征你在填充的时候就必须额外保留一个xxx_isnull的标记列否则填完之后信息就全丢了。我在一次风控类的赛题里就吃过这个亏某个字段的缺失和违约高度相关填充完中位数之后模型分数直接掉了两个点回头加上标记列才补回来。第三件是抽查具体值判断是真缺失还是伪缺失。有些字段的空值其实是业务里的未发生比如消费金额为空可能意味着这个用户根本没消费过填均值就是把零消费扭曲成了平均消费还有些字段的缺失被编码成了 0、-1、unknown、未知这类占位符pandas 的isna()根本识别不出来需要手动替换成np.nan再统一处理。这一步没有捷径只能对着字段说明一个个过但花半小时能省掉后面几小时的返工。import pandas as pd import numpy as np def missing_report(df, targetNone): rep pd.DataFrame({ missing_cnt: df.isna().sum(), missing_rate: df.isna().mean().round(4), dtype: df.dtypes.astype(str), nunique: df.nunique() }).sort_values(missing_rate, ascendingFalse) if target is not None: rate_by_pos df[df[target] 1].isna().mean() rate_by_neg df[df[target] 0].isna().mean() rep[gap] (rate_by_pos - rate_by_neg).round(4) return rep这段函数输出一张表重点看两列missing_rate决定处理策略gap决定要不要保留标记。gap绝对值超过 0.05 的字段我基本都会加一列缺失标记。1.2 处理顺序为什么必须是划分→填充→编码→采样→训练顺序错了分数虚高这是这类赛题最常见的翻车点。举个具体的例子如果你先在全量数据上算中位数做填充再划分训练集和验证集那么验证集的填充值里已经包含了训练集的信息这就叫数据泄漏。同理先做 SMOTE 再划分数据集等于让模型在训练阶段就见过由验证集样本插值生成的合成样本交叉验证分数会漂亮得不像话提交到线上直接现原形。正确的流水线顺序是先按标签分层划分训练/验证集 → 只在训练集上fit填充器和编码器 → 用fit好的对象去transform验证集 → 只在训练集上做过采样 → 训练模型 → 在原始分布的验证集上评估。注意最后一步验证集绝对不能碰任何采样操作它必须保持真实的不平衡比例否则你评估出来的指标毫无意义。我的做法是直接把整条链封进imblearn的 Pipeline 里让fit和transform的作用域由框架来保证而不是靠人肉记忆。这样写的好处是做交叉验证的时候每一折的填充、编码、采样都只在该折的训练部分完成验证部分只做 transform泄漏问题从结构上就被堵死了。注意sklearn的 Pipeline 和imblearn的 Pipeline 不是同一个东西。只要流程里包含采样器SMOTE、RandomUnderSampler 等就必须用imblearn.pipeline.Pipeline用 sklearn 那个会在交叉验证时报错或者静默出错。1.3 采样策略的取舍逻辑不是所有不平衡都需要动手很多人一看到标签分布是 1:10 就急着上 SMOTE其实没必要。经验上不平衡比在 1:4 以内树模型基本能自己扛住调class_weight就够了1:4 到 1:20 之间是 SMOTE 的主战场收益明显超过 1:50 就要谨慎了这时候少数类的样本量可能只有几十条SMOTE 插值出来的样本高度同质化反而会加剧过拟合更稳妥的路线是欠采样 集成或者干脆把问题转成异常检测的思路。这里的不平衡比算法很简单IR 多数类样本数 / 少数类样本数。假设训练集有 8000 条负样本、400 条正样本IR 20属于典型的值得上采样区间。但还要注意另一个数——少数类的绝对样本量。如果少数类只有 50 条就算 IR 是 100我也更倾向于先做数据增强或者干脆放弃这类样本的精细建模因为 5 折交叉验证下每折的少数类只有 40 条SMOTE 用它生成新样本的可靠性很低。2. 缺失值处理的实操细节与判断依据2.1 数值型字段的四种填充方案与适用条件数值型缺失的填充方案我按复杂度从低到高排了四档实际用的时候按字段重要性挑而不是全表统一。第一档是中位数填充。选它而不是均值理由很实在赛题数据里数值字段几乎都有长尾和离群点均值会被极端值拽偏中位数对异常值不敏感。代价是所有缺失样本被赋了同一个值方差被人为压缩了。对于树模型来说这个代价可以接受因为树只关心分裂点的位置不关心方差。第二档是分组填充。如果字段之间存在明显的层级关系比如用户下面有多个订单那用用户级的中位数去填订单级的缺失比全表中位数合理得多。实现上就是groupby加transform两行代码的事但效果比全局填充好一个档次。第三档是 KNN 填充。KNNImputer的思路是找特征空间里最近的 k 个样本用它们的值加权平均。用之前必须先标准化否则量纲大的字段会主导距离计算填出来的结果毫无意义。k 值我一般取 5 到 10样本量大就取小一点。这个方案的缺点是慢几万行乘几十列可能跑几分钟而且对高维稀疏数据效果差。第四档是模型填充。把有缺失的列当标签其他列当特征训练一个回归或分类模型来预测缺失值。理论上最优雅实际上最容易出事——如果填充模型本身过拟合会把噪声当成规律灌进数据里而且训练成本高。我一般只在少数几个关键字段上用它而且会做对比实验确认它真的比中位数填充带来提升才留下。方案实现成本适用场景主要风险中位数填充极低缺失率低于 20%字段重要性一般压缩方差忽略组间差异分组中位数低有明确层级/分类结构小分组样本不足时不稳定KNN 填充中特征相关性强样本量中等需标准化慢高维失效模型填充高关键字段缺失率中等过拟合误差传播2.2 树模型原生支持缺失为什么还要填这是个很实际的问题。LightGBM 和 XGBoost 确实能直接吃np.nan内部会为缺失值单独走一个分支方向理论上不填也能跑。但我实际的做法是baseline 阶段不填直接喂给 LightGBM 先跑通进入迭代阶段后对关键字段做填充并保留标记列做 A/B 对比。理由是树模型的缺失处理是默认方向机制它把所有缺失样本统一送到某个子节点这在大规模数据上很高效但丢失了缺失样本内部的差异性。如果你的数据里缺失本身携带信息前面提到的gap明显那么显式填充 标记列的组合往往比让模型自己猜要好。反过来如果缺失完全是随机的那填不填差别不大省点代码直接喂进去更划算。我在一次比赛里做过对照同一份数据、同一个 LightGBM 参数方案 A 直接喂 NaN方案 B 用分组中位数填充 12 个标记列。5 折交叉验证的结果是方案 B 的 F1-macro 高出 0.008提升不大但稳定。这个量级的差异在排行榜上可能就是几十名所以值得做但不值得花太多时间调填充策略。2.3 类别型缺失把缺失变成一个新类别类别型字段的缺失我一律不填众数而是填一个固定的字符串比如__MISSING__然后在独热编码时让它成为一个独立维度。这么做的逻辑是如果这个字段的缺失和标签无关那模型学到的这一维权重就接近零没有损失如果相关模型就能直接利用这个信号。填众数的做法等于强行把缺失样本归到最常见的那一类凭空制造了一批伪同类风险更大。实现的时候有个细节要注意OneHotEncoder默认遇到训练时没见过的类别会报错必须设置handle_unknownignore。这在比赛里特别重要因为验证集或者测试集里出现训练集没有的类别值是很常见的不设这个参数会直接崩掉。还有一个容易被忽略的点是高基数类别字段。比如某个 ID 类字段有几千个不同取值独热编码后维度爆炸这时候要么做频次编码用出现次数替代原值要么做目标编码用该类别下的标签均值替代要么直接交给 LightGBM 的类别特征支持。这三种我按优先级排LightGBM 原生类别特征最省事目标编码效果最好但必须做折内交叉防止泄漏频次编码最安全但信息损失大。2.4 缺失率阈值的取法为什么我定在 80% 而不是 50%删列阈值这个东西没有标准答案我的经验值是80%。理由是缺失率超过 80% 的字段剩余的有效信息已经很少就算保留标记列模型能学到的也只是这批样本缺这个字段这一个比特的信息而它带来的噪声和过拟合风险往往大于收益。50% 到 80% 之间我倾向于保留因为这类字段常常还是有区分度的砍掉可惜。删行则要谨慎得多。只有在缺失率极低比如某一行缺了 90% 的字段、而且这一行明显是脏数据的情况下才删。比赛数据本来就宝贵尤其是少数类样本删一条少一条宁可填一个粗糙的值也不要轻易丢样本。我见过有队伍为了数据干净删掉了 15% 的行结果少数类样本被删掉一半模型直接学不动了。注意缺失率统计必须在划分数据集之前做但填充值的计算必须在划分之后做。这两件事经常被混在一起前者是描述性统计后者是特征变换性质完全不同。3. 过采样的正确姿势与参数选择3.1 先换评估指标再谈采样在动手采样之前有一件事必须先做把评估指标从 accuracy 换掉。假设正负样本比例是 1:20一个把所有样本都预测为负类的模型accuracy 是 95%看起来很美实际上毫无用处。这类赛题里我用的指标组合是F1-macro主指标、AUC、以及混淆矩阵。F1-macro 对每个类别一视同仁不会被多数类淹没AUC 衡量排序能力对阈值不敏感混淆矩阵用来确认模型是不是在一边倒。如果赛题官方指定了评估指标那就以官方为准但私下做实验的时候我还是会同时看 F1-macro 和 AUC因为官方指标有时候不够敏感看不出模型结构的差异。比如某些赛题用加权 F1权重按样本数分配那它本质上还是偏向多数类需要额外关注少数类的召回率。3.2 SMOTE 家族怎么选k_neighbors 怎么定SMOTE 的核心逻辑是对每个少数类样本在它的 k 个最近邻里随机挑一个在这两点连线上随机取一点作为新样本。理解了这一点参数就好定了。k_neighbors默认是 5含义是参考 5 个邻居来插值。这个值不能超过少数类样本数减一否则会报错。如果少数类只有 30 条样本k 最多设到 29而实际上设到 5 就够了设太大反而会让新样本分布过于弥散、失去局部性。我的经验是少数类样本少于 100 条时 k 取 3 到 5100 到 1000 条时取 5 到 10上千条时可以取到 10 以上。至于用哪个变体标准 SMOTE 最通用Borderline-SMOTE只在类别边界附近生成样本适合决策边界模糊的场景ADASYN根据密度自适应决定每个样本生成多少新样本适合少数类分布不均匀的情况。我在实际比赛里用得最多的是标准 SMOTE因为它最不容易出幺蛾子ADASYN偶尔能带来提升但结果波动较大不适合作为主力。还有一个重要限制SMOTE 只能处理数值特征。如果数据里有类别列要么先编码成数值独热后可以但插值出来的值没有意义要么用SMOTENC专门处理混合类型。文本或高维稀疏特征上更是不建议用 SMOTE因为线性插值在两个稀疏向量之间生成的样本往往既不接近任何真实样本也没有业务含义纯属噪声。from imblearn.over_sampling import SMOTE # 计算采样比例把少数类补到多数类的 60% n_maj, n_min 8000, 400 target_ratio 0.6 n_min_target int(n_maj * target_ratio / (1 - target_ratio) * (n_min / n_maj) * 0) # 简化写法 sampling_strategy {1: int(n_maj * 0.6)} # 把标签为 1 的类补到 4800 条 smote SMOTE( sampling_strategysampling_strategy, k_neighbors5, random_state42 )这里sampling_strategy我不建议直接设成auto补到与多数类等量因为把 400 条补到 8000 条意味着要生成 7600 条合成样本合成比例高达 19:1数据里全是插值出来的假样本模型会严重过拟合。补到多数类的 50% 到 70% 通常是个甜点既缓解了不平衡又保留了真实样本的主导地位。3.3 用 imblearn Pipeline 锁死作用域前面提过采样器必须包在imblearn的 Pipeline 里。下面是我常用的完整结构包含缺失填充、编码、采样、建模四个环节from sklearn.compose import ColumnTransformer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.impute import SimpleImputer from imblearn.pipeline import Pipeline from imblearn.over_sampling import SMOTE from lightgbm import LGBMClassifier num_cols [...] # 数值列 cat_cols [...] # 类别列 num_pipe Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, StandardScaler()) ]) cat_pipe Pipeline([ (imputer, SimpleImputer(strategyconstant, fill_value__MISSING__)), (ohe, OneHotEncoder(handle_unknownignore, sparse_outputFalse)) ]) preprocess ColumnTransformer([ (num, num_pipe, num_cols), (cat, cat_pipe, cat_cols) ], remainderdrop) clf_pipe Pipeline([ (prep, preprocess), (smote, SMOTE(sampling_strategy0.6, k_neighbors5, random_state42)), (model, LGBMClassifier( n_estimators600, learning_rate0.05, num_leaves31, min_child_samples20, subsample0.8, colsample_bytree0.8, random_state42, n_jobs-1 )) ])注意这里的sampling_strategy0.6它表示采样后少数类样本数占多数类的 60%等价于我前面手算的比例。用浮点数比手算整数更省心改动起来也方便。再强调一次验证集不做任何采样。做交叉验证时cross_val_score或者cross_validate会自动在每一折的测试部分跳过采样器这是 imblearn Pipeline 的设计保证。但如果你是手动划分训练验证集那就必须自己在验证集上只调prep.transform而不碰smote。3.4 采样之外的三条补充路线SMOTE 不是唯一解我通常还会并行试三条路线做对比。路线一是class_weight。LightGBM 里设class_weightbalanced或者手动传{0: 1, 1: 20}让损失函数对少数类更敏感。这个方案零成本、零风险很多时候效果和 SMOTE 差不多甚至更好因为它是从损失函数层面修正不会引入合成样本的偏差。路线二是阈值移动。训练完模型后不直接用 0.5 做判定而是用precision_recall_curve找 F1 最大的那个阈值。这个方法特别适合评估指标是 F1 或者 recall 的场景几乎不花额外成本就能拿到一两个点的提升。我一般的做法是交叉验证时用 OOF 预测算最优阈值然后在测试集上套用这个阈值。路线三是欠采样混合。先用RandomUnderSampler把多数类砍掉一部分比如砍到原来的 50%再用 SMOTE 补少数类。这样做的收益是数据量可控、训练速度快代价是丢了部分多数类信息。在多数类样本极多十万级以上的时候这个方法比纯过采样实用得多。方案实现成本提升稳定性主要副作用class_weight极低稳定概率校准偏移SMOTE 过采样低中等合成样本同质化阈值移动极低稳定需 OOF 预测配合欠采样过采样中中等丢失多数类信息4. baseline 训练与迭代路径设计4.1 baseline 的意义是可对比不是分数高我见过不少人在 baseline 阶段就疯狂调参想把初始分数拉到最高结果后面做特征工程的时候没有干净的参照系改了半天也说不清到底哪一步起了作用。baseline 的正确定位是一个稳定、可复现、可对比的下界。它不需要多强但必须满足三个条件固定的随机种子、固定的划分方式、固定的评估函数。有了这三样后面任何一次改动都能做 A/B 对比。我的 baseline 一般是三件套并行跑逻辑回归线性下界、随机森林非线性下界、LightGBM梯度提升下界。逻辑回归必须配标准化否则收敛慢甚至不收敛随机森林几乎不用调参n_estimators300就能用LightGBM 用一套保守参数学习率 0.05、树数 600、叶子数 31。三个跑完看分数分布如果 LightGBM 明显领先后面就以它为基座迭代如果三者差距不大说明特征的信息量有限重点应该放在特征工程而不是模型调参。from sklearn.model_selection import StratifiedKFold, cross_validate from sklearn.metrics import make_scorer, f1_score, roc_auc_score cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) scoring { f1_macro: make_scorer(f1_score, averagemacro), auc: make_scorer(roc_auc_score, needs_probaTrue) } scores cross_validate(clf_pipe, X_train, y_train, cvcv, scoringscoring, n_jobs-1, return_train_scoreTrue) print(CV f1_macro: %.4f ± %.4f % (scores[test_f1_macro].mean(), scores[test_f1_macro].std())) print(CV auc: %.4f ± %.4f % (scores[test_auc].mean(), scores[test_auc].std()))标准差这一项很值得看。如果 5 折的标准差超过 0.03说明模型对数据划分太敏感这时候光看均值没意义得先解决方差问题——要么增加折数比如用 10 折要么换更稳定的模型要么检查是不是有某几折的少数类样本特别少。4.2 交叉验证的分层设计不能省不平衡数据上做交叉验证KFold是绝对不能用的必须用StratifiedKFold。原因很直接KFold随机切分可能出现某一折里少数类样本只有个位数甚至零个那一折的 F1 会直接崩到零均值被拉得面目全非。除了分层还有两个细节值得注意。第一是折数5 折是默认选择但如果少数类样本量少于 200 条我会加到 10 折让每折的验证集小一点这样每一折的评估更充分均值的估计更可靠。第二是重复如果数据集不大几千行单次 5 折的结果偶然性很强用RepeatedStratifiedKFold(n_splits5, n_repeats3)跑 15 次取平均结果会扎实很多代价是训练时间翻三倍。还有一个隐藏的坑分组交叉验证。如果数据里有用户 ID、设备 ID 这类字段同一个 ID 对应多条样本那必须用GroupKFold保证同一个 ID 的样本不会同时出现在训练折和验证折里。否则模型会记住这个 ID 对应的标签验证分数虚高线上直接掉分。这类赛题里这种情况非常普遍值得花两分钟检查一下。4.3 最小可用特征集与实验日志baseline 阶段的特征工程我建议只做最小可用集也就是原始字段加上少量低风险的衍生特征比如数值列的比值、时间列的星期几、类别列的频次。不要在这个阶段做目标编码、聚类特征这些复杂的东西否则一旦分数不对你分不清是模型的问题还是特征的问题。比特征更重要的是实验日志。我见过太多队伍改到后期完全忘了哪次改了什么分数为什么涨的不知道跌的也不知道。我的做法是在项目根目录维护一个 CSV每次跑完实验追加一行字段包括实验编号、数据版本、特征版本、模型参数摘要、CV 均值、CV 标准差、备注。看着土但真到了需要复现最佳结果的时候这张表能救命。实验编号关键改动CV F1-macroCV AUC结论E01中位数填充 LR0.58210.8103线性下界E02同上 RF0.61040.8367非线性有效E03同上 LGBM0.62880.8512当前最佳E04加缺失标记列0.63510.8549保留E05加 SMOTE(0.6)0.64170.8533F1 升 AUC 略降E06SMOTE 阈值移动0.65020.8541保留4.4 从 baseline 往上迭代的四条主线跑通 baseline 之后提升路径我按投入产出比排序大概是这么四条。第一条是补特征。这是收益最大也最耗时的方向。具体做什么取决于赛题领域通用的思路是交叉组合两个类别列拼接成新列、聚合统计用户级均值、最大、最小、计数、时间差首次到末次的时间间隔、比率两个数值列相除。每加一批特征都要回到实验日志里记录并且确认 CV 分数是涨的、标准差没有变大。第二条是调参。LightGBM 的参数里learning_rate和n_estimators是一对学习率小则树数多一般 0.03 到 0.1 之间搭配 500 到 2000 棵树num_leaves控制复杂度31 是安全值数据量大可以往上调min_child_samples防过拟合20 到 50 之间subsample和colsample_bytree一般设 0.7 到 0.9。我的习惯是用 Optuna 跑 50 到 100 组但只在固定的交叉验证划分上调调完立刻在另一套随机种子的划分上验证防止过拟合到验证集。第三条是模型集成。LightGBM、XGBoost、CatBoost 三个跑完做加权平均权重用 OOF 预测搜索。这一步通常能带来千分之几到百分之一的提升性价比不错。再往上是 stacking用 OOF 预测作为第二层的输入但要注意第二层必须用简单的模型逻辑回归就行否则容易过拟合。第四条是重采样策略的精细化。比如对不同的少数类子群分别做采样或者用SMOTE加上Tomek Links做混合采样先过采样再清理边界噪声。这条路的收益不稳定我一般放在最后试。5. 常见问题与排查技巧实录5.1 踩过的坑与对应解法速查下面这张表是我和队友在几次比赛里实际遇到过的问题按出现频率排序遇到报错或者分数异常的时候可以直接对照。现象可能原因排查与解法CV 分数 0.95线上 0.6采样或填充在划分前做了数据泄漏检查是否用了 imblearn Pipeline验证集是否被采样某折 F1 突然为 0该折验证集无少数类样本改用 StratifiedKFold增加折数OneHotEncoder 报未知类别测试集出现了训练集没有的类别设handle_unknownignoreSMOTE 报 k_neighbors 错误少数类样本数小于 k1调小 k或先合并稀有类别过采样后内存爆掉数据量翻十倍以上改用欠采样过采样混合或用生成器分批训练集分数高验证集低合成样本同质化过拟合降低采样比例到 0.3-0.5加正则CatBoost 报类别特征格式错误类别列含 NaN 或类型不一致统一转成字符串后再声明 cat_features同一份代码两次结果不同随机种子未固定在划分、采样、模型三处都设 random_state有一类问题特别隐蔽值得单独说标签编码错位。用过采样之后y_train的索引会变如果你之前手动维护了一个和原索引对齐的辅助数组比如样本权重、或者原样本的 ID采样后这个对齐关系就断了。表现出来是模型能训练但分数莫名其妙地低。解法很简单采样后重新从smote.fit_resample的返回值里取y_res所有辅助信息要么一起重采样要么干脆别用。5.2 几条不太写在文档里的实操心得心得一先跑通再优化别追求一步到位。我见过有人第一天就搭了个七层 stacking结果因为中间某一层的数据泄漏分数虚高到 0.98等发现的时候所有实验记录全部作废。正确顺序永远是能跑通的最简版本 → 加特征 → 调参 → 集成。每一步都留一个可回退的版本。心得二把随机种子当成超参数来管。同样的流程用三个不同种子跑如果 F1 的波动超过 0.01说明你的结论不可靠多试几次再下判断。我一般的做法是快速实验用单次 5 折候选方案用 3 个种子各跑一次取平均最终方案用 5 个种子的 RepeatedStratifiedKFold。心得三验证集一定要留一份绝对不碰的。交叉验证用训练集内部划分除此之外再从训练集里切出 10% 作为 holdout全程只在最后确认模型的时候用一次。这份数据被污染了你就失去了判断模型是否过拟合到 CV 的唯一标尺。心得四过采样之后概率不再可信。SMOTE 改变了先验分布模型输出的概率是偏向少数类的不能直接当置信度用。如果要输出概率得做先验校正公式是p_calibrated p / (p (1-p) * (prior_orig / prior_sampled))。评委如果看的是概率排序这一步不影响结果但如果看的是阈值为 0.5 的分类结果那必须做阈值移动。心得五记录每次失败的实验。成功的原因往往相似失败的原因千奇百怪。我有一份否决清单记着某类特征加上以后 CV 涨了但 holdout 跌了判定为过拟合、某组参数在 5 折上很好但在 10 折上崩了判定为不稳定。这些东西比成功记录更有价值能让你少走很多回头路。我个人在这类赛题上的体会是缺失值、过采样、baseline 这三件事代码层面的工作量其实很小加起来不到两百行真正花时间的是每一次决策背后的判断——这个字段的缺失值不值得保留标记、这个不平衡比该不该采样、这次涨分是真的还是噪声。把这几个判断做扎实了后面加特征、调参、集成才有稳固的地基可踩反过来如果这三步里埋了一处泄漏后面所有的努力都是在错误的分数上做优化。下一篇我会接着聊特征工程那部分尤其是时间窗聚合和类别编码的具体做法包括我踩过的一个目标编码泄漏的坑。

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

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

免费获取报价 →
↑