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.Pipeline 把 SMOTE 放在模型前,这样在交叉验证里只对训练折过采样,避免数据泄漏。
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% 则倾向二分类”的决策规则就会用到)。
常见坑 & 规避
- 数据泄漏:必须用 imblearn.Pipeline 或在交叉验证每个训练折里单独做 SMOTE。
- 稀疏矩阵不支持:SMOTE 需要稠密;用 SMOTE-NC 或让 OHE 输出稠密(数据不大时)。
- k_neighbors 太大:小类样本太少会报错或生成异常点;把
k_neighbors
调小。 - 重叠/噪声:考虑 Borderline-SMOTE 或 SMOTEENN(SMOTE 后再清洗)。
- 评估口径统一:开启和关闭 SMOTE 时,相同训练/测试切分、相同模型与特征,只切换“是否 SMOTE”,才是“同台对比”。