【机器学习】SMOTE(Synthetic Minority Over-sampling Technique),合成少数类过采样是什么?

发布于:2025-08-20 ⋅ 阅读:(17) ⋅ 点赞:(0)

SMOTE 是什么?(中英)

SMOTE(Synthetic Minority Over-sampling Technique,合成少数类过采样)

  • 做什么:类别不平衡时,给“样本太少的类”(少数类)人工造一些“像真的”新样本,而不是简单复制已有样本。
  • 怎么造:对每个少数类样本,找它的 k 个少数类近邻;随机挑一个近邻,在两者之间连一条线,再在线段上随机取一点作为新样本(新样本 = 原样本 + 随机系数 ×(近邻 − 原样本))。
  • 为什么有用:能提高少数类的召回率(Recall),从而常常提升 macro-F1(因为 macro-F1 平均对待每个类)。
  • **可能副作用:**类边界本来就重叠/噪声多时,误报率 FPR 可能上升;若操作不当会放大量噪声。

What it is (EN brief): SMOTE synthesizes new minority samples by interpolating between a sample and its minority neighbors, helping the classifier see more examples of rare attacks and improving per-class recall and macro-F1.


在你的课题里怎么用?

  • 二分类(Normal vs Attack):如果“Attack”占比很小,SMOTE 对召回攻击很有帮助,macro-F1 往往↑;但FPR也可能随之↑,需要权衡你 RQ2 的“运行代价”(FPR、时延)。
  • 多分类(DoS、DDoS、Recon、…):可以把每个小类都当“少数类”去补齐。imblearn 会对非多数类分别过采样,有助于找出“最易混淆的家族”不再被大类淹没。

什么时候用 / 不用?

适用:

  • 类别极不平衡;你在意 macro-F1、per-class recall
  • 特征大多是连续数值或可合理数值化(如经 OHE 编码后的特征,数据量不算过大)。

谨慎/不建议:

  • 全是高维稀疏 OHE,数据很大(SMOTE 不支持稀疏矩阵,需变稠密会爆内存);
  • 时间序列/流式数据(会破坏时间结构);
  • 少数类本身噪声很多(可能放大噪声)。

变体(知道下就好)

  • SMOTE-NC:Mixed 数值 + 类别特征(把类别特征复制、对数值特征插值)。
  • Borderline-SMOTE / SVM-SMOTE:更关注类边界,常更稳。
  • SMOTEENN / SMOTETomek:SMOTE 后再清洗重叠/噪声样本。
  • ADASYN:更偏向在难学区域生成样本。

代码怎么写(与你的流水线兼容)

关键点:用 imblearn.PipelineSMOTE 放在模型前,这样在交叉验证里只对训练折过采样,避免数据泄漏。

A. 全数值或可承受稠密特征(简单好用)

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline  # 注意是 imblearn.pipeline
from sklearn.metrics import classification_report

# 1) 预处理:让 OHE 输出稠密(sparse_output=False),便于 SMOTE 使用
pre = ColumnTransformer([
    ("cat",  Pipeline([
        ("imp", SimpleImputer(strategy="most_frequent")),
        ("ohe", OneHotEncoder(handle_unknown="ignore", sparse_output=False))
    ]), cat_cols),  # 你的类别列
    ("num",  Pipeline([
        ("imp", SimpleImputer(strategy="median")),
        ("std", StandardScaler(with_mean=True))
    ]), num_cols)   # 你的数值列
], remainder="drop")

# 2) 采样器 + 模型
smote = SMOTE(sampling_strategy="not majority", k_neighbors=5, random_state=42)
clf   = LogisticRegression(max_iter=2000, class_weight=None, solver="saga", random_state=42)

pipe = Pipeline(steps=[
    ("pre",   pre),
    ("smote", smote),    # 过采样仅在 fit 的训练折上发生
    ("clf",   clf)
])

pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print(classification_report(y_test, y_pred, digits=4))

参数提示:

  • sampling_strategy="not majority":对非多数类都过采样;也可用 {"Worms": 2000, "Backdoor": 2000} 这种 dict 精准控制。
  • k_neighbors=5:少数类样本数要大于这个值;过小类(<6)时把它改小,比如 2–3。

B. 混合数值+类别且样本大(优先 SMOTE-NC

SMOTE-NC 直接在原始列上工作(指定哪些是类别列索引),再交给预处理:

from imblearn.over_sampling import SMOTENC
from imblearn.pipeline import Pipeline as ImbPipeline

cat_idx = [X.columns.get_loc(c) for c in cat_cols]  # 类别列的索引
sampler = SMOTENC(categorical_features=cat_idx, sampling_strategy="not majority", k_neighbors=5, random_state=42)

pipe = ImbPipeline(steps=[
    ("sampler", sampler),  # 先在原始空间过采样(类别列复制,数值列插值)
    ("pre",     pre),      # 再做 OHE / 标准化
    ("clf",     clf)
])

这种方式不需要把 OHE 变稠密,更省内存。


与你的 RQ 如何对应?

  • RQ1(有效性):启用 SMOTE 往往 macro-F1↑、per-class recall↑;和“只用 class_weight”相比,SMOTE 更积极,提升通常更明显,但要监控整体 FPR多数类 Precision
  • RQ2(运行代价):SMOTE 只影响训练阶段推理时延不变;但如果导致边界泛化到多数类,FPR可能上升——这正是你需要报告的折中(“macro-F1 提升 < 0.02 但代价↑ > 30% 则倾向二分类”的决策规则就会用到)。

常见坑 & 规避

  1. 数据泄漏:必须用 imblearn.Pipeline 或在交叉验证每个训练折里单独做 SMOTE。
  2. 稀疏矩阵不支持:SMOTE 需要稠密;用 SMOTE-NC 或让 OHE 输出稠密(数据不大时)。
  3. k_neighbors 太大:小类样本太少会报错或生成异常点;把 k_neighbors 调小。
  4. 重叠/噪声:考虑 Borderline-SMOTESMOTEENN(SMOTE 后再清洗)。
  5. 评估口径统一:开启和关闭 SMOTE 时,相同训练/测试切分相同模型与特征,只切换“是否 SMOTE”,才是“同台对比”。

网站公告

今日签到

点亮在社区的每一天
去签到