跳到主要内容

SMOTE 算法

一、直观理解:在少数类邻居之间“连线造点”​

类别不平衡时,模型会偷懒:全猜多数类也能拿到高准确率。朴素做法是复制少数类(随机过采样),但容易过拟合重复点。

SMOTE(Synthetic Minority Over-sampling Technique)在特征空间里,对每个少数类样本找其少数类近邻,在连线上随机插值,生成新的合成样本:

xnew=xi+λ(xnn−xi),λ∼U(0,1)x_{\text{new}}=x_i+\lambda(x_{nn}-x_i),\quad \lambda\sim U(0,1)

像在少数类居民区的道路上新建房屋,而不是把同一栋楼复制粘贴。这样决策边界会向多数类方向适度推移,提升对少数类的召回。

SMOTE 只是训练阶段的重采样;测试集必须保持真实分布,绝不能先造点再划分。

二、适用场景与优缺点​

类型说明
适用场景少数类样本过少导致召回低;特征为连续数值;需要加强树模型/线性模型对少数类的学习;与代价敏感学习对照使用。
核心优点缓解简单复制的过拟合;实现直观;有 Borderline-SMOTE、ADASYN 等改进;常提升少数类 F1。
局限与缺点在重叠区域造点可能加重噪声;高维距离不可靠;类别特征需特殊处理;可能改变校准;不是因果增广。

使用前:确认评价用 PR-AUC/F1/召回而非准确率;只在训练折内 SMOTE;连续特征已处理缺失;考虑是否改用类权重更简单。

三、核心原理​

1. 基本流程​

对每个少数类样本 xix_i:

  1. 在少数类集合中找 KK 近邻;
  2. 按过采样倍率,随机选近邻 xnnx_{nn};
  3. 线性插值生成 xnewx_{\text{new}};
  4. 将合成样本并入训练集后拟合分类器。

2. 常见变体​

变体思想
Borderline-SMOTE只对边界附近少数类造点
ADASYN按局部难度自适应造点数量
SMOTE-NC处理混合类别特征
SMOTE + Tomek造点后再清边界噪声

3. 与其他策略关系​

  • 欠采样:删多数类,简单但丢信息;
  • 类权重/Cost:不改数据,改损失;
  • 阈值移动:先按原分布训练再调阈值;
  • 组合:SMOTE 只在训练折,验证用真实比例。

四、关键决策​

决策点建议
目标比例不必 1:1,可升到 1:2 或按验证 F1 选
KK5 常用;少数类极少时减小 KK
使用位置仅训练集;CV 时每折内独立 SMOTE
替代方案先试 Cost/Prior/阈值,再考虑 SMOTE
高维先降维或特征选择
评价宏 F1、少数类召回、PR 曲线

五、MATLAB 示例​

MATLAB 没有永远稳定的同名 smote 函数于所有版本,赛题中可手写插值,或用 randsample 结合近邻。下面给出完整可运行的手写 SMOTE + 分类示例。

function [X2, y2] = smote(X, y, minorityLabel, targetCount, K)
% 对 minorityLabel 过采样至 targetCount
Xmin = X(y == minorityLabel, :);
nMin = size(Xmin, 1);
if nMin < 2
error("少数类样本不足以 SMOTE");
end
K = min(K, nMin - 1);
need = max(0, targetCount - nMin);
syn = zeros(need, size(X,2));

% 少数类内部两两距离
D = pdist2(Xmin, Xmin);
for i = 1:nMin
D(i,i) = inf;
end

for t = 1:need
i = randi(nMin);
[~, ord] = sort(D(i, :), "ascend");
nnIdx = ord(1:K);
j = nnIdx(randi(K));
lambda = rand;
syn(t, :) = Xmin(i, :) + lambda * (Xmin(j, :) - Xmin(i, :));
end

X2 = [X; syn];
y2 = [y; repmat(minorityLabel, need, 1)];
end

% ---- 演示 ----
rng(0);
Xmaj = randn(40, 2) + [0 0];
Xmin = randn(6, 2) + [2.0 2.0];
X = [Xmaj; Xmin];
y = [zeros(40,1); ones(6,1)];

% 注意:真实流程应先划分 train/test,再仅对 train SMOTE
cvp = cvpartition(y, "HoldOut", 0.3);
Xtr = X(training(cvp), :); ytr = y(training(cvp));
Xte = X(test(cvp), :); yte = y(test(cvp));

nMaj = sum(ytr == 0);
nMin = sum(ytr == 1);
fprintf("SMOTE 前:多数 %d, 少数 %d\n", nMaj, nMin);

[Xtr2, ytr2] = smote(Xtr, ytr, 1, nMaj, 3); % 少数类补到与多数相同
fprintf("SMOTE 后:多数 %d, 少数 %d\n", sum(ytr2==0), sum(ytr2==1));

% 对比:不处理 vs SMOTE
mdl0 = fitctree(Xtr, ytr, "MinLeafSize", 2);
mdl1 = fitctree(Xtr2, ytr2, "MinLeafSize", 2);
y0 = predict(mdl0, Xte);
y1 = predict(mdl1, Xte);

disp("原始训练集混淆矩阵:"); disp(confusionmat(yte, y0));
disp("SMOTE 后混淆矩阵:"); disp(confusionmat(yte, y1));

% 少数类召回
rec = @(yt,yp) sum(yt==1 & yp==1) / max(1,sum(yt==1));
fprintf("少数类召回:原始=%.2f, SMOTE=%.2f\n", rec(yte,y0), rec(yte,y1));

也可用 fitcensemble(...,"Method","RUSBoost") 等从算法侧处理不平衡,作为 SMOTE 的对照。

六、如何解释结果​

  1. 前后类分布:造了多少点、目标比例;
  2. 指标变化:准确率可能下降,但少数类召回/F1 上升才是目标;
  3. 可视化:二维时可画合成点是否落入合理区域;
  4. 边界是否更噪:若精度全面下降,可能 K 过大或重叠严重;
  5. 报告:仅训练折使用、K、倍率、与 Cost-sensitive 对照。

七、常见误区​

1. 先 SMOTE 整个数据集再 train/test split​

严重泄漏:测试集含合成点或同源插值信息。

2. 用准确率证明 SMOTE “有效”​

不平衡题准确率会骗人。

3. 在类别高度重叠处猛造点​

等于在噪声区增稠,损害边界。

4. 对 one-hot 类别特征直接欧氏插值​

可能产生无意义的小数类别编码,应使用 SMOTE-NC 或分开处理。

5. 忽略“阈值/代价”更简单的解​

有时只调阈值就够,不必上采样。

八、小结​

SMOTE 在少数类近邻连线上合成新样本,缓解不平衡学习中的信息稀释与简单复制过拟合,但必须严格限制在训练协议之内。

它是手段不是目的:最终要用少数类召回、F1、PR-AUC 与业务代价说话,并与类权重、欠采样、RUSBoost 等方案并列比较。