数据平衡后KNN模型无法识别全部类别问题求助
先来说说你遇到的两个核心问题,以及重采样后训练前必须做的关键步骤:
一、为什么原始KNN模型准确率1.0,但新数据无法识别最小类别?
你的原始数据集极度不平衡,而准确率是一个对不平衡数据极不友好的指标——模型只要一直预测占比75%的类别,就能轻松拿到75%的准确率,但你这里拿到了1.0,大概率是两种情况:
- 特征存在标签泄露:比如某个特征直接和标签强相关(甚至就是标签的变形),导致KNN能完美区分训练/测试集中的所有类别,但这种完美只存在于你的现有数据中,新数据没有这种泄露,所以模型失效。
- 训练集与测试集特征分布高度重合:虽然用了
StratifiedShuffleSplit,但如果数据本身的特征簇完全分离,KNN确实能做到全对,但新数据的特征分布和训练集差异大,导致模型无法泛化到小类别。
结合你说的“新数据总是把最小类别误分类为第二小的类别”,更倾向于模型在小类别上根本没学到有效特征——毕竟原始数据中小类别样本太少,KNN很难捕捉到它们的特征模式。
二、为什么SMOTEENN处理后,模型只识别出2个类别?
看你给出的分类报告,测试集的support只有类别0和1,说明你的测试集中根本没有其他两个类别!这大概率是拆分数据集时的低级错误:
- 你处理完数据得到
X_res和y_res后,是不是还在用之前针对原始X、y生成的train_index和test_index?这完全错误,因为X_res是重采样后的新数组,索引和原始数据不匹配,抽取的样本自然会混乱,甚至只包含部分类别。 - 另外,你可能没有用分层抽样拆分重采样后的数据:即使数据平衡了,随机拆分也可能出现极端情况(虽然概率低),但分层抽样能强制保证训练集和测试集的类别分布与整体一致。
三、重采样后训练KNN前必须做的步骤
针对你的情况,这些步骤缺一不可:
1. 用分层抽样重新拆分数据集
重采样后的数据是平衡的,但必须用分层抽样确保训练/测试集都包含所有类别。替换你的拆分代码:
# 用train_test_split的stratify参数更简洁 from sklearn.model_selection import train_test_split x_train, x_test, y_train, y_test = train_test_split(X_res, y_res, test_size=0.2, stratify=y_res, random_state=42)
或者继续用StratifiedShuffleSplit,但要基于X_res和y_res生成新索引:
sss = StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=42) train_index, test_index = next(sss.split(X_res, y_res)) # 这里必须用重采样后的X_res和y_res x_train, y_train = X_res[train_index], y_res[train_index] x_test, y_test = X_res[test_index], y_res[test_index]
拆分后一定要验证测试集的类别分布:
from collections import Counter print("测试集类别分布:", Counter(y_test))
正常情况下应该是每个类别各1500个样本(7500*0.2=1500)。
2. 对特征进行标准化/归一化
KNN是基于距离的模型,特征的尺度差异会严重影响距离计算——比如某个特征取值范围是0-1000,另一个是0-1,KNN会几乎只依赖前者。重采样后,SMOTE生成的样本基于特征空间,归一化后生成的样本更合理,模型效果也会更稳定:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() x_train = scaler.fit_transform(x_train) x_test = scaler.transform(x_test) # 测试集必须用训练集的scaler拟合,避免数据泄露
如果你用欧氏距离,也可以用MinMaxScaler将特征归一化到0-1区间。
3. 调整KNN的参数
平衡数据后,原来的k=5可能不是最优选择:
- 尝试更大的k值(比如k=10、15),减少噪声样本的影响;
- 使用加权KNN:
KNeighborsClassifier(weights='distance'),让距离更近的样本拥有更高权重,帮助模型更好捕捉小类别的特征; - 尝试不同的距离度量,比如曼哈顿距离(
metric='manhattan'),对异常值更鲁棒。
4. 验证重采样后的数据有效性
虽然你看到SMOTEENN处理后四个类别各7500,但可以检查生成的样本是否合理:比如查看小类别生成的样本特征是否和原始小类别样本分布一致,避免SMOTE生成无意义的噪声样本。
最后
按上述步骤调整后,你的模型应该能识别所有四个类别了。另外,评价模型时不要只看准确率,重点关注小类别的recall和f1-score——这才是不平衡数据下模型性能的关键指标。
内容的提问来源于stack exchange,提问作者demeflac

