ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SMOTE过采样:解决类不平衡分类问题的原理与Python实战

SMOTE过采样:解决类不平衡分类问题的原理与Python实战 当分类任务里某一类样本少得可怜而另一类占了绝大多数你训练的模型表面上准确率逼近99%真正想识别的那批对象却几乎全被漏掉。我最早对这个问题有切肤之痛是在做信贷违约预测的时候一万条样本里真正违约的只有两百多条基线模型把所有客户都预测为正常还款准确率高达98%可风控部门根本不会认可这种模型——他们需要的是从人群里捞出那两百个危险份子哪怕误伤一部分也能接受。想解决这类类不平衡问题SMOTE算法过采样是我第一个推荐的方案也是实用度最高、和现有模型兼容性最好的方案之一。这篇文章适合所有在做分类任务的机器学习从业者你在做信用评分、疾病预测、故障检测、反欺诈或者任何正负样本比例悬殊的任务都可以参考。我会把SMOTE的原理讲清楚附完整可复现的Python代码再把那些只有踩过坑才知道的细节一并写出来——比如过采样必须在数据切分之后进行否则你的模型评估结果全是虚的。1. 当99%的准确率是一场完美骗局类不平衡到底怎么毁掉分类模型1.1 一个全都猜负类的模型为什么看起来很强先做一道简单的算术题。假设你手里有一万条样本其中9500条属于正常类别500条属于少数类别。你要训练一个二分类器目标是精准识别这500条少数样本。一个极其偷懒的模型把所有样本都预测成多数类它的准确率是多少9500除以1000095%。如果多数类比例达到99%你什么都不干模型准确率就是99%。这才是类不平衡最阴险的地方——准确率这个指标天然向多数类倾斜。你辛辛苦苦调了一个月的模型准确率从99%升到99.1%你以为提升了实际上模型对少数类可能一点识别能力都没有。更极端的情况一个全猜负类的模型在业务方眼里的价值等于零信贷场景里违约客户一个没抓到医疗场景里病患者一个没筛出来故障场景里坏样本一个没报警。所以处理类不平衡问题第一步不是调参而是先把评估指标从准确率上挪开。后续我会专门讲评估部分这里先记住一个结论遇到类不平衡准确率失去参考意义召回率、精确率、F1、PR曲线才是真正要盯的东西。1.2 类不平衡的两种成因绝对稀少与相对悬殊按成因区分类不平衡大致有两种情况。一种是绝对稀少少数类样本本身就极少再怎么采集也凑不够例如某种罕见疾病的病例数据、大型设备故障的现场数据。你能拿到的原本就只有几十条这不是比例悬殊的问题是数据总量不足。另一种是相对悬殊少数类样本绝对数量并不少但和多数类一比比例低得吓人例如电商平台正常订单与欺诈订单欺诈订单其实每年有上万条只是几百万条真实订单的零头都不到。这两种成因决定了方案思路的走向。相对悬殊的情况过采样、欠采样都有发挥空间SMOTE算法的用武之地最大绝对稀少的情况SMOTE依然可以帮忙但要清醒地认识到合成样本终究不是真实样本过采样做出来的数据再多也不代表你掌握了真实世界里更多的证据。遇到绝对稀少型不平衡更稳妥的策略是考虑迁移学习、半监督学习或者干脆把这当成异常检测任务来处理。我在第三章给出的代码案例属于相对悬殊这类场景这也是SMOTE最典型的应用环境。1.3 数据层面、算法层面、评估层面三条应对路径怎么选解决类不平衡问题业界大致有三大方向。数据层面扩充少数类数据或者压缩多数类数据。过采样属于前者欠采样属于后者。算法层面修改模型内部机制让分类器对少数类更敏感。比如代价敏感学习给少数类样本的误分惩罚更高再比如集成学习中的一些特殊处理让每个子模型从不同角度观测少数类。评估层面更换评估指标和决策阈值让模型追求的目标从猜对多数类转向识别少数类。这三条路径可以组合使用但如果让我给一个初阶建议先从数据层面上过采样入手再配合一套科学的评估方法。原因很简单——过采样不改变算法结构不要求你精通代价函数推导它纯粹是在数据准备阶段做文章任何分类器拿来都能直接喂进去普适性极强。这也是SMOTE算法能成为类不平衡问题首选方案的根本原因。接下来我就用一整章把SMOTE到底做了什么讲透。2. SMOTE的合成魔法为什么造数据比复制数据高级2.1 随机过采样的致命缺陷复制粘贴不等于有效信息在没有SMOTE之前大家怎么处理少数类样本太少的问题最朴素的想法就是过采样少数类样本少是吧那就复制几份复制到和多数类数量差不多再训练。这个方法有个明显问题模型训练时看到的少数类样本是同一份数据的多份拷贝相当于一个学生把同一道题抄了十遍考试时换个数字就不会了。复制不会给模型带来任何关于样本空间的新信息只会放大重复样本的权重和过拟合风险。在决策边界附近重复复制的少数类样本会让模型对这些点的信任异常膨胀但真实世界里的边界区域远比这几个点要宽广得多。随机欠采样也有类似的窘境——直接扔掉大量多数类样本信息损失太大尤其是当多数类本身内部结构复杂、包含多个子簇时简单欠采样可能把重要的多数类边界信息一并丢掉。SMOTESynthetic Minority Over-sampling Technique合成少数类过采样技术解决的正是这个问题它不再复制已有样本而是基于已有样本之间的相似性去合成新样本。模型看到的不是同一道题的十遍抄写而是围绕原题变式出的不同新题。2.2 三步生成一批新为少数类样本近邻寻找、随机选点、插值合成SMOTE的核心逻辑用一句话概括在少数类样本的特征空间里沿着样本间的连线人为长出一些新样本。具体分三步第一步对每个少数类样本找到它的K个近邻。这里用欧氏距离来衡量样本在特征空间中的远近。假设当前处理的是少数类中的样本 \(x_i\)在所有少数类样本里选出距离最近的K个邻居默认K5。注意这里的近邻范围限制在少数类内部不是看所有样本。第二步从K个近邻里随机挑一个样本\(x_j\)。第三步在\(x_i\)和\(x_j\)之间做线性插值生成新样本\(x_{new}\)。插值公式也很简单选了离 x_i 最近的一个邻居后随机取一个给系数 λ取值从0到1之间然后新样本 \(x_{new}x_iλ×(x_j-x_i)\)。什么意思就是在这两个样本点连接起来的线段上随机取一个中间位置作为新样本。λ越接近0新样本越靠近原始样本\(x_i\)λ越接近1就越靠近邻居\(x_j\)。我当初学这个算法时觉得最妙的就是线段插值这一步。它相当于对整个少数类样本空间内部做填充新样本不是凭空臆造而是落在真实样本之间的合理过渡区域。用个接地气的比方随机过采样是拿复印机把一张照片多印几份SMOTE则是请一个画师照着这张照片画出同一人物的不同神态画出来的每张都有细微变化但整体仍不失真。2.3 SMOTE生成出的数据为什么分类器吃得下你可能会想算法生成的数据分类器真的信任吗从分类器的角度看真实样本和合成样本在特征空间里的分布是可以平滑过渡的。假设某个少数类样本分布在特征空间的一个紧凑区域内从原始样本出发沿着与邻居的连线方向生成新样本这些新样本不会跑到特征空间里完全不着边际的角落它们仍然保持少数类的局部结构特征。还有一种更直觉的解释方向少数类样本之所以难学是因为数量太少分类器没法看清这块区域的边界和形状。SMOTE相当于给分类器多配了几双眼睛从不同角度把少数类所在的区域观察了一遍。决策树或随机森林这类模型训练时就能在少数类区域定出更真实的划分边界逻辑回归这类线性模型也能得到更有意义的决策面偏移。不过也要明确一点SMOTE默认假设少数类样本在特征空间里是互相靠近、聚类成形的。如果少数类样本内部本身就是东一簇西一簇相距极远SMOTE插值会在两簇之间生成本不属于任何一类的桥接点反而引入噪声。这个限制我在第五章会展开讲属于非常重要的实战前提。3. 跑通第一个SMOTE模型用Python完成从过采样到评估的完整链路3.1 环境准备与数据场景设定实操之前先把环境备好。SMOTE最成熟的库有两个一个来自imbalanced-learn简称imblearn另一个是smote-variants库偏向新式变体算法。日常项目我几乎都用imblearn因为它和scikit-learn风格高度统一调用方式几乎零学习成本。你可以直接在终端里执行pip install imbalanced-learn scikit-learn pandas如果你已经有sklearn环境通常只需要补装imbalanced-learn一个包就够了。老版本请注意imblearn在版本更新后对sklearn版本有最低要求装完建议顺手检查一下版本兼容性import sklearn print(sklearn.__version__)为了演示我选一个和数据分布贴合实际的高频场景企业员工离职预测。这类场景天然存在类不平衡——在职员工永远是多数离职员工在任意时间切片里都只占很小的比例。我直接构造一份模拟数据特征包括工龄、月收入、加班时长、绩效评分、工作满意度目标列是是否离职。之所以用离职预测而不是网上烂大街的信用卡欺诈数据集是因为你要真实上手练的时候离职数据的特征既有数值也有离散更能模拟真实项目里的脏状态。当然核心逻辑对所有分类任务完全一致。3.2 关键一步先切分数据集再做SMOTE过采样这个顺序问题是无数人踩过的坑我把它提前到操作之前讲。正确顺序永远是先把数据切成训练集和测试集再只在训练集上做SMOTE。为什么因为测试集必须代表真实世界的数据分布。真实世界里少数类就是少的你上线的模型面对的从来都是低比例的样本群。如果你在所有数据上做完SMOTE再切分合成样本会同时混进训练集和测试集这相当于考试前偷看了试卷答案——测试集里出现了训练数据的亲戚模型评估出的分数自然虚高。更严重的是这样做会掩盖模型在真实不平衡数据上的真实表现。一个模型能不能在真实环境下揪出少数类衡量的标准就是它面对的那些没有经过任何人工增广的数据。所以测试集保持原始分布是评估本身的意义所在。import pandas as pd import numpy as np from sklearn.model_selection import train_test_split # 构造一份模拟的离职预测数据10000条样本8500人留任1500人离职 np.random.seed(42) n_samples 10000 data pd.DataFrame({ 工龄_年: np.random.randint(0, 15, n_samples), 月收入_k: np.random.randint(3, 30, n_samples), 月加班时长: np.random.randint(0, 80, n_samples), 绩效评分: np.random.randint(1, 5, n_samples), 工作满意度: np.random.randint(1, 10, n_samples) / 10, }) # 离职概率由几个特征共同决定少数类约占15% logit ( -2.5 0.15 * data[工龄_年] 0.08 * data[月加班时长] / 10 - 0.1 * data[月收入_k] - 0.6 * data[工作满意度] ) prob 1 / (1 np.exp(-logit)) data[是否离职] np.random.binomial(1, prob) print(正样本数(离职):, (data[是否离职] 1).sum()) print(负样本数(在职):, (data[是否离职] 0).sum()) X data.drop(是否离职, axis1) y data[是否离职] # 先切分再过采样 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy )我说一下这几行设计意图stratifyy保证了切分后训练集和测试集中的离职比例和原始数据一致不会出现训练集一个离职样本都没有的尴尬局面。数据构造里用了逻辑函数控制离职概率实际正常情况下跑出来的离职占比大致落在12%到18%之间。如果你的环境随机数和我的不一样偏差一点没关系只要确认训练集里少数类条数足以让SMOTE发挥即可。3.3 过采样核心代码与训练对比现在进入关键部分把SMOTE应用到训练集上from imblearn.over_sampling import SMOTE from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, roc_auc_score, precision_recall_curve, auc # 初始化SMOTE smote SMOTE(random_state42) # 只在训练集上执行过采样 X_train_res, y_train_res smote.fit_resample(X_train, y_train) # 查看过采样前后的样本分布 from collections import Counter print(过采样前训练集分布:, Counter(y_train)) print(过采样后训练集分布:, Counter(y_train_res))fit_resample是imblearn的标准接口。老版本里常用fit_sample新版已经废弃了统一用fit_resample装的是最新库就不会出这种兼容问题。过采样完成后你训练集上的两类样本数量基本趋于一致。接下来同时训练两个模型做一个对照一个在原始不平衡训练集上训练一个在SMOTE过采样后的训练集上训练。# 模型1直接在不平衡数据上训练 clf_origin LogisticRegression(max_iter1000, random_state42) clf_origin.fit(X_train, y_train) # 模型2在SMOTE过采样数据上训练 clf_smote LogisticRegression(max_iter1000, random_state42) clf_smote.fit(X_train_res, y_train_res) # 模型3过采样XGBoost也可以但这里先用随机森林看效果对比 rf_smote RandomForestClassifier(n_estimators200, random_state42) rf_smote.fit(X_train_res, y_train_res) print( 原始数据 逻辑回归 ) print(classification_report(y_test, clf_origin.predict(X_test))) print(AUC: , roc_auc_score(y_test, clf_origin.predict_proba(X_test)[:, 1])) print( SMOTE 逻辑回归 ) print(classification_report(y_test, clf_smote.predict(X_test))) print(AUC: , roc_auc_score(y_test, clf_smote.predict_proba(X_test)[:, 1])) print( SMOTE 随机森林 ) print(classification_report(y_test, rf_smote.predict(X_test))) print(AUC: , roc_auc_score(y_test, rf_smote.predict_proba(X_test)[:, 1]))实际操作中你大概率会看到这样的结果直接在不平衡数据上训练的逻辑回归精确率看着很高但少数类离职的召回率惨不忍睹而SMOTE过采样后训练的模型少数类召回率会明显抬头整体F1也更好看。AUC通常也会有提升但提升幅度取决于类别重叠程度和特征质量不要期待每次都能从0.8跳到0.95。此外若你在做网格搜索选参过采样步骤应该放在每条候选参数内部验证流程的训练折叠里。这样又回到我一直强调的原则评估流程禁止在过采样后的数据上进行全量验证。3.4 评估模型时该盯住哪几个指标很多人在类不平衡任务上栽跟头是因为最后打印一份accuracy看着98%的准确率就开开心心收工了。这是错误示范。下面这张表是我多年里认为类不平衡任务最有参考价值的指标务必记住指标计算方式为什么在类不平衡任务里重要召回率RecallTP / (TP FN)直接衡量真正的少数类抓回来多少是业务最关心的底层能力精确率PrecisionTP / (TP FP)衡量预测为少数类的人里可靠的比例防止误报太严重F1分数精确率和召回率的调和平均当精确率和召回率需要平衡时一个综合判断依据PR曲线下面积PR-AUC不同阈值下精确率对召回率积分少数类占比极低时比ROC曲线更能反映模型真实质量ROC-AUC不同阈值下召回率对错误率积分兼容性最好但正负样本比例严重失衡时可能过度乐观用实际项目中的对比来看更容易懂。假设10000个在职员工里真离职有200人。一个模型预测了150人离职其中80人真的离职漏掉了120人。那么它的召回率是40%精确率是53%。另一个模型预测了500人离职其中150人真离职漏掉50人。召回率是75%精确率只有30%。哪个模型对业务更友好完全取决于你是在做筛查还是做确认。做员工离职预警人们宁可多盯几批名单也不愿漏掉真正想走的关键人才此时召回率优先做反欺诈误伤一个正常用户可能比漏掉一笔欺诈损失更大此时精确率优先。SMOTE本身不能帮你选这个优先级但通过调整过采样比例和分类阈值你确实可以把模型向更想抓少数类或更想少误报的方向推动。4. 参数、变体与实战选择SMOTE家族怎么挑怎么调4.1 k_neighbors和sampling_strategy两个上手就要改的参数SMOTE默认参数可以直接跑通但只依赖默认值远不够。两个最核心的参数值得专门解释。k_neighbors找几个近邻来参与插值默认5。调小到1或2会使生成样本高度贴近原始样本局部模式被过度强化噪声不容易稀释调大到10或15个别相距较远的样本会被强行桥接生成样本可能跨越到多数类区域。我的经验是先观察少数类样本的聚集度。用KMeans先粗看一下簇数如果样本就在一两个簇内k可以取5到7如果样本很分散k保持3到5更安全。sampling_strategy控制过采样后少数类达到什么比例默认值是auto也就是让少数类数量自动升到和多数类一致。这一最直觉的选择实际操作中未必最优。试一试就明白了把离职样本从150条升到8500条生成的合成数据量是原先几十倍。这些样本全部落在少数类已有的分布范围内极端情况下会把少数类区域填充得过于饱和分类器几乎无差别地对整片区域给出离职判断误报率大幅上升。更稳的做法是把采样策略设成中间态sampling_strategy0.5表示让少数类达到多数类一半数量sampling_strategy0.75表示达到四分之三。处理高度不平衡的数据我通常带着这个参数做一轮网格搜索from sklearn.model_selection import GridSearchCV from imblearn.pipeline import Pipeline pipe Pipeline([ (smote, SMOTE(random_state42)), (clf, RandomForestClassifier(random_state42)) ]) param_grid { smote__sampling_strategy: [0.3, 0.5, 0.75, 1.0], smote__k_neighbors: [3, 5, 7], clf__n_estimators: [100, 200] } search GridSearchCV(pipe, param_grid, scoringf1, cv5) search.fit(X_train, y_train) print(search.best_params_)注意这里我把SMOTE放进了imblearn.pipeline.Pipeline里。imblearn.pipeline是从imblearn库导入的专用Pipeline与sklearn.pipeline.Pipeline的区别在于它能保证在交叉验证的每一折内部先做SMOTE再训练模型不会把全量数据的过采样结果透传给验证集。这是官方推荐的标准写法也是避免第五章节数据泄漏坑的利器。4.2 Borderline-SMOTE、ADASYN、SMOTEENN变体各自解决什么问题SMOTE从2002年提出后衍生出了很多变体每个都在解决原版的某个短板。我用一张表格把最常用的几个说清楚变体改进思路适用场景Borderline-SMOTE只对决策边界附近的少数类样本做插值内部样本不生成少数类和多数类边界分明但边界样本数量太少不足以定型边界ADASYN根据少数类样本的学习难度自适应分配生成数量难学的样本生成更多少数类内部难度差异大存在部分极容易被混淆的样本SVM-SMOTE用SVM结果定位边界支持向量在支持向量附近做插值高维数据下想借助SVM边界拟合能力来引导生成位置SMOTEENNSMOTE过采样后再用编辑近邻法清洗噪声样本类别重叠较高、噪声较多的脏数据SMOTETomekSMOTE过采样后用Tomek links删除边界上的多数类与过近样本希望同时清理两类边界样本保持边界干净这里重点提两个场景判断当少数类样本大多数挤在一个安全区域内边界区只有零星几点原版SMOTE对边界区域的关照不足。此时用Borderline-SMOTE会更精准它先判断哪些少数类样本位于危险区近邻中多数类占比较高只对这些样本做插值生成的样本更集中在真正影响决策边界的位置。当数据里噪声多、两类样本犬牙交错时任何过采样都会放大原有噪声。SMOTEENN的思路比较适合脏数据先做SMOTE补足数量再用近邻规则把类别不一致的样本清洗掉相当于先补漏洞再清理垃圾。我自己的经验是第一版方案永远先用原版SMOTE把流程跑通、评估框架立起来如果发现边界效果不佳或噪声明显再切换到对应的变体横向对比。不要一上来就上一堆复杂变体那样你很难定位到底是数据问题、采样问题还是模型问题。4.3 一个参考的变体应用操作实例接第三章的离职预测数据直接演示一下切换边界型变体有多简单from imblearn.over_sampling import BorderlineSMOTE, ADASYN # 以BorderlineSMOTE为例 bsmote BorderlineSMOTE(random_state42, kindborderline-1) X_res_bs, y_res_bs bsmote.fit_resample(X_train, y_train) # 以ADASYN为例 adasyn ADASYN(random_state42) X_res_ada, y_res_ada adasyn.fit_resample(X_train, y_train)kindborderline-1是经典版本还有kindborderline-2后者做插值时不只是找少数类近邻也会结合多数类近邻的信息生成样本时更靠近多数类区域的真实结构。实际哪个更好脱离数据场景讨论都是空谈跑一遍对比AUC和PR-AUC就见分晓。5. 先切分再过采样SMOTE最容易踩的五个坑5.1 坑一全部数据过采样后再切分测试结果虚高的数据泄漏这是我在新人代码评审里见过次数最多的问题。处理流程写成读入数据SMOTE调整全量样本再做train_test_split。单看代码逻辑没问题但后果非常隐蔽合成样本在切分后同时落入训练集和测试集尤其是那些插值生成的样本和训练集中某些原始样本的距离极近测试集里几乎等于出现了训练集的近亲评估出的准确率和AUC自然大幅虚高。正确处理是先train_test_split或KFold切分再在训练集上执行SMOTE测试集保持原始分布。如果用了交叉验证就用我前面写的imblearn.pipeline.Pipeline让SMOTE在每一折的训练折叠内部执行。这个坑的危害在于它不是直接报错而是悄悄给你一个漂亮但不真实的分数上线后真实效果立刻打回原形。5.2 坑二不看分布直接SMOTE类别重叠反而更严重SMOTE有个默认前提少数类样本在特征空间内是相对聚集的。但如果少数类样本本身分散在不同区域少数类和多数类的边界区域高度重叠SMOTE插值生成的新样本就可能落在多数类的地盘上把原本已经模糊的边界搅得更浑。我通常会先做一个快速检查看看少数类样本的最近邻构成。简单的方式是使用sklearn.neighbors.NearestNeighbors计算少数类样本周围K个邻居中多数类占比超过一半的样本有多少个。比例很高说明类别重叠严重此时优先考虑数据处理升级方案先用NearMiss或EditedNearestNeighbours清理噪声再决定是否上SMOTE。也可以直接选SMOTEENN这类带清洗步骤的组合方法。5.3 坑三特征稀疏或高维场景插值等于插噪声SMOTE的本质是特征空间中的欧氏距离连线。如果特征经过TF-IDF向量化或One-Hot编码特征向量里绝大部分维度是0两个样本间的欧氏距离反映出来的相似性很不可靠。在这样的空间里插值生成的新样本往往落在一个绝大多数维度为0、少数维度为小数的位置这样的特征向量既不像真实样本也不具备类别区分能力。同理特征维度几千上万时少数类样本在高维空间里普遍极度稀疏彼此间的距离奇大近邻关系已经失去统计意义。我对这类场景的态度是要么先对高维特征做筛选或降维PCA、截断SVD让SMOTE在合理的低维空间内发挥作用要么干脆换无坐标空间的过采样思路比如用SMOTE-NC处理混合分类特征用生成模型生成新样本。继续在原空间硬做不过是自我安慰。5.4 坑四过采样比例直接拉到1比1高误报随之而来接第四章提到的sampling_strategy这里讲一个真实的对比。离职预测案例里把SMOTE比例设为1.0少数类数量和多数类完全相等生成的离职样本高达7000条。我实测时发现逻辑回归的召回率确实上去了但误报率也肉眼可见地变高。原因很简单合成样本过多时少数类分布区域被过度填充分类器对整片区域过度自信决策边界向多数类方向推进太多。把比例降到0.5到0.75后模型表现得明显更克制精确率回升整体F1分数往往更好。项目上我现在默认从0.5或0.75起步再结合f1或pr_auc作为网格搜索的评分函数去挑最优比例。永远不要默认1比1最好合成数据不是越多越好数量是服务质量的不是服务数量的。5.5 坑五评估时只提AUC不提PR-AUC结果被过度乐观误导ROC-AUC在很多不平衡比例下会显得异常好看哪怕少数类识别率并不高。这不是AUC算错了而是ROC曲线权衡的错误接受率受到庞大的多数类基数牵制——通常绝大多数真实负例都能被正确判为负例导致总体假正率很低。当负例数量是正例的上百倍时即便模型把一小部分负例误判成正例假正率数字也会被稀释得很小ROC曲线看起来相当优美。而PR-AUC呢它的核心在于精确率直接和预测为正的数里正确的比例挂钩正例基数小任何误报都会立刻拉低数值曲线比ROC敏感得多。所以类不平衡任务里我习惯把它作为主评审指标。注意SMOTE过采样后训练集的比例已经不真实了你以为的决策阈值也是基于均衡状态算出的因此评估模型性能时如果有可能优先在保持真实分布的测试集上运行precision_recall_curve和roc_auc_score然后给出指标组合对比而不是单发一个AUC数字。最后再分享一点个人体会我在持续做类不平衡任务的过程中越来越认同一个观点SMOTE不是万能药但它是一把能快速打开僵局的好钥匙。它的价值在于把数据太少、模型根本看不见少数类的这个起点性问题解决掉让后续调参、模型选型、业务目标对齐这些环节有了真正可操作的基础。我现在回头复盘最想留给读者的一句话是用SMOTE的同时一定要重新定义你的成功标准。如果你的评估指标里还只有accuracy那不管SMOTE生成多少合成样本你都在戴着一副模糊的眼镜看世界。先把指标换成召回率、精确率、F1和PR-AUC再把SMOTE用过采样比例和正确的切分顺序配合到位这个组合拳打下来多数类不平衡问题都能往前推进一大截。最后送一个小技巧跑过采样之前把少数类样本用t-SNE或PCA投影到二维肉眼看一眼它们和多数类的位置关系。这一步花不了一分钟却能帮你避开我在第五章里讲的绝大多数故障。数据分布是真的还是假的一眼望过去直觉比任何参数都诚实。
返回列表