
简介这份PDF文献面向机器学习研究者、数据挖掘工程师及算法方向的学生聚焦类别不平衡场景下传统KNN分类器偏向多数类、少数类识别率低的问题。文章提出REKRNN算法将k-秩近邻规则嵌入Bagging集成框架并融合欠采样、过采样等重采样技术与随机子空间法通过增加基学习器多样性来平衡训练集、抑制过拟合在多个不平衡数据集上兼顾总体精度与少数类召回。资源包仅含1个PDF文件约521KB为正式发表的期刊论文全文包含摘要、引言、相关研究、算法设计与仿真实验等完整章节适合作为算法改进思路与实验设计的参考文献。目前已有111人学习便于快速把握不平衡分类的集成学习路线与评价要点。1. 随机秩次 k 近邻当不平衡数据把 KNN 逼到墙角之后正样本只有 3%负样本 97%你拿 KNN 去跑分类器会毫不犹豫地把所有样本判成多数类准确率还高达 97%。这不是模型坏了是 KNN 的决策规则在类别不平衡下彻底失效——它只看距离最近的 k 个邻居谁多谁少多数类天然占优。更麻烦的是KNN 对 k 值极度敏感k 小了对噪声过拟合k 大了少数类被淹没调参像在走钢丝。「基于随机秩次 k 近邻规则的不平衡数据分类算法」要解决的就是这个问题。核心思路不是改距离度量也不是简单做重采样而是把「邻居投票」换成「秩次统计」——不再数最近邻里哪个类多而是看样本在各类别邻居排序中的秩次分布再用随机化策略增强少数类的秩次信号。这条路子跟 Bagging 集成学习、重采样都能组合适合手上数据极度倾斜、又不想上深度学习的中小规模场景。读完你能搞清楚它的原理边界、复现步骤、参数怎么调、坑在哪。2. 秩次规则凭什么比投票更抗不平衡2.1 从「数票数」到「看排名」决策逻辑的根本切换标准 KNN 的决策规则是多数投票对测试样本 x找训练集中距离最近的 k 个邻居统计每个类别的出现次数取最多的那个类作为预测结果。这个规则隐含一个假设——各类别在特征空间中的局部密度大致相当。不平衡数据打破了这个假设多数类样本密集少数类稀疏k 个邻居里多数类天然占多数。秩次规则换了一个视角。它不关心邻居里各类别有多少个而是关心每个类别在距离排序中的「位置分布」。具体来说对测试样本 x分别计算它到每个类别所有训练样本的距离然后看 x 在每个类别的距离排序中处于什么秩次。如果 x 真正属于少数类那么它到少数类样本的距离秩次应该整体靠前即使少数类样本总数很少。这个思路的关键在于秩次是一个相对量不受类别样本量绝对差异的影响。多数类有 10000 个样本少数类只有 300 个但秩次统计只看「排在前面的比例」不看绝对数量。这就从机制上缓解了不平衡带来的投票偏差。再进一步「随机秩次」在秩次基础上引入了随机化。常见做法是对每个类别的距离排序做随机子采样多次采样后取秩次统计的期望或中位数。这样做有两个好处一是降低个别噪声样本对秩次排序的干扰二是通过多次随机采样模拟集成效果让少数类的秩次信号更稳定。2.2 算法流程拆解每一步在算什么把上面的思路落成可执行的算法流程大致分五步输入训练集 $D {(x_i, y_i)}{i1}^{N}$其中 $y_i \in {0, 1}$少数类样本量为 $N{min}$多数类为 $N_{maj}$且 $N_{min} \ll N_{maj}$。测试样本 $x$近邻参数 $k$随机采样次数 $T$。第一步距离计算。对测试样本 $x$计算它到训练集中每个样本的欧氏距离或曼哈顿距离得到距离向量 $d [d_1, d_2, ..., d_N]$。第二步按类别分组排序。将距离向量按类别拆成两组$d_{min}$到少数类样本的距离和 $d_{maj}$到多数类样本的距离。分别对两组做升序排列得到各自的秩次序列。第三步随机秩次采样。从少数类的秩次序列中随机抽取 $k$ 个秩次值重复 $T$ 次多数类同理。每次采样得到一个秩次统计量比如秩次均值或秩次中位数。第四步秩次统计量比较。对 $T$ 次采样结果分别计算少数类和多数类的秩次统计量分布。如果少数类的秩次统计量显著小于多数类秩次越小表示距离越近则判为少数类。第五步输出预测。根据秩次统计量的比较结果输出类别标签。这里有一个容易忽略的细节随机采样的「随机」不是随便抽通常要保证每次采样中少数类和多数类的采样数量一致否则又回到了不平衡的老问题。常见做法是每次从多数类中随机抽取与少数类等量的子集再计算秩次统计量。2.3 和 Bagging、重采样的组合姿势秩次规则本身是一个基分类器的决策逻辑但它天然适合跟集成学习搭配。最常见的组合方式有三种方式一秩次 KNN Bagging。对训练集做 Bootstrap 采样训练多个秩次 KNN 基分类器投票出最终结果。Bagging 的作用是降低方差而秩次规则负责处理不平衡。注意 Bootstrap 采样时少数类样本可能被进一步稀释所以通常要在采样时对少数类做过采样补偿。方式二秩次 KNN 欠采样。在每次随机秩次采样时从多数类中抽取与少数类等量的子集相当于在算法内部做了欠采样。这种方式的优点是避免了预处理阶段的信息损失欠采样和分类决策融为一体。方式三秩次 KNN SMOTE 过采样。先用 SMOTE 生成少数类合成样本再用秩次 KNN 分类。这种组合适合少数类样本极度稀缺比如只有几十个的场景但要注意 SMOTE 生成的样本可能引入噪声秩次规则的随机化能在一定程度上缓解这个问题。三种方式没有绝对优劣关键看数据规模和倾斜程度。样本量在万级以下、少数类占比低于 5% 时方式二通常最稳样本量更大时方式一配合轻量过采样更合适。3. 用 Python 把随机秩次 KNN 跑起来3.1 最小可运行实现核心函数逐行拆先不依赖任何第三方不平衡学习库用 NumPy 手写一个最小版本把秩次规则的核心逻辑跑通。这样你能看清每一步在算什么后面再换成工程化实现时心里有数。import numpy as np from collections import Counter def random_rank_knn(X_train, y_train, X_test, k5, T20, random_state42): 随机秩次 KNN 分类器 X_train: 训练特征 (n_samples, n_features) y_train: 训练标签 (n_samples,) X_test: 测试特征 (n_samples, n_features) k: 每次采样的秩次数量 T: 随机采样次数 rng np.random.RandomState(random_state) classes np.unique(y_train) # 假设二分类少数类为样本量少的那个 counts Counter(y_train) minority_class min(counts, keycounts.get) majority_class max(counts, keycounts.get) # 按类别拆分训练集索引 idx_min np.where(y_train minority_class)[0] idx_maj np.where(y_train majority_class)[0] predictions [] for x in X_test: # 计算到所有训练样本的距离 dists np.linalg.norm(X_train - x, axis1) d_min np.sort(dists[idx_min]) # 少数类距离升序 d_maj np.sort(dists[idx_maj]) # 多数类距离升序 rank_stats_min [] rank_stats_maj [] for _ in range(T): # 每次从多数类中随机抽取与少数类等量的子集 n_sample min(len(d_min), len(d_maj)) maj_sub rng.choice(len(d_maj), sizen_sample, replaceFalse) d_maj_sub d_maj[maj_sub] # 随机抽取 k 个秩次位置 k_actual min(k, n_sample) pos_min rng.choice(n_sample, sizek_actual, replaceFalse) pos_maj rng.choice(n_sample, sizek_actual, replaceFalse) # 秩次统计量取采样位置的距离均值 rank_stats_min.append(np.mean(d_min[pos_min])) rank_stats_maj.append(np.mean(d_maj_sub[pos_maj])) # 比较两类秩次统计量的中位数 if np.median(rank_stats_min) np.median(rank_stats_maj): predictions.append(minority_class) else: predictions.append(majority_class) return np.array(predictions)这段代码的核心逻辑在for _ in range(T)循环里。每次迭代做三件事从多数类中随机抽取与少数类等量的子集保证两类采样规模一致从两类中各随机抽取 k 个秩次位置计算采样位置的距离均值作为秩次统计量。最后用中位数比较两类的秩次统计量少数类秩次更小则判为少数类。参数方面k控制每次采样的秩次数量太小则统计量方差大太大则失去「局部」的意义一般取 3 到 10 之间。T控制随机采样次数越大结果越稳定但计算越慢实践中 20 到 50 次通常够用。random_state固定后结果可复现调参时建议固定。3.2 在真实不平衡数据上验证以信用卡欺诈数据集为例光跑通代码不够得在真实不平衡数据上看效果。信用卡欺诈数据集是经典的不平衡二分类场景正样本欺诈占比约 0.17%非常适合验证秩次规则。import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import classification_report, f1_score # 加载数据假设本地已有 creditcard.csv df pd.read_csv(creditcard.csv) X df.drop(Class, axis1).values y df[Class].values # 标准化KNN 对尺度敏感必须做 scaler StandardScaler() X scaler.fit_transform(X) # 分层划分保证测试集中少数类比例一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, stratifyy, random_state42 ) # 标准 KNN 基线 knn KNeighborsClassifier(n_neighbors5) knn.fit(X_train, y_train) y_pred_knn knn.predict(X_test) print(标准 KNN:) print(classification_report(y_test, y_pred_knn, digits4)) # 随机秩次 KNN y_pred_rr random_rank_knn(X_train, y_train, X_test, k5, T30) print(随机秩次 KNN:) print(classification_report(y_test, y_pred_rr, digits4))运行后你会看到标准 KNN 在少数类上的召回率极低通常不到 0.3而随机秩次 KNN 的少数类召回率能提升到 0.7 以上。代价是多数类的精确率会下降这是不平衡分类的固有 trade-off关键看你的业务更在意哪一端。这里有几个实操细节值得注意。标准化是必须的KNN 基于距离特征尺度不统一会让距离计算完全失真。分层划分也是必须的否则测试集里可能一个少数类样本都没有评估结果毫无意义。另外信用卡数据集的特征已经做过 PCA 降维如果你用自己的数据特征选择和降维要单独考虑。3.3 参数调优k、T 和距离度量的联动关系随机秩次 KNN 有三个核心参数k秩次采样数量、T随机采样次数、距离度量。这三个参数不是独立的调参时要联动考虑。参数作用推荐范围调参优先级k每次采样的秩次数量3~10高T随机采样次数20~50中距离度量样本间距离计算方式欧氏/曼哈顿中采样比例多数类子集与少数类比例1:1~3:1高k 的调法从 3 开始逐步增加到 10观察少数类 F1 的变化。k 太小比如 1 或 2时秩次统计量方差大预测结果不稳定k 太大超过 15时秩次统计量趋近于全局均值失去了局部决策的意义。我一般会在 5 附近先跑一轮再上下微调。T 的调法T 从 10 开始每次翻倍看少数类 F1 是否收敛。通常 T30 左右结果就稳定了再增大只是增加计算时间。如果 T50 和 T100 的结果差异还很大说明数据噪声较高需要考虑先做噪声清洗。距离度量的选择取决于特征类型。连续特征用欧氏距离高维稀疏特征用余弦距离混合类型特征可以考虑曼哈顿距离或 Gower 距离。注意距离度量一旦改变k 和 T 的最优值也会跟着变需要重新调。采样比例是容易被忽略的参数。多数类子集与少数类的比例不一定要严格 1:1有时候 2:1 或 3:1 反而效果更好因为保留了更多多数类的信息。这个参数没有理论最优值只能靠交叉验证试。4. 避坑与排查那些让秩次 KNN 翻车的细节4.1 距离计算前没做标准化结果全乱现象模型在测试集上表现和随机猜差不多少数类召回率极低但换一组数据又正常。原因KNN 系列算法完全依赖距离计算如果某个特征的数值范围是 0 到 10000另一个是 0 到 1距离计算会被大范围特征主导小范围特征的信息被完全淹没。秩次规则虽然换了决策逻辑但距离计算这一步没变同样会翻车。解决建模前对所有连续特征做标准化Z-score或归一化Min-Max。标准化用StandardScaler归一化用MinMaxScaler。注意标准化参数只能在训练集上拟合然后应用到测试集否则会引入数据泄露。4.2 随机采样时两类样本量不一致秩次比较失效现象少数类召回率有提升但不稳定不同随机种子下结果波动很大。原因随机秩次采样时如果少数类和多数类每次采样的样本量不一致秩次统计量的基准就不同比较结果没有意义。比如少数类每次抽 5 个多数类每次抽 50 个多数类的距离均值天然更稳定比较不公平。解决每次采样时从多数类中随机抽取与少数类等量的子集保证两类采样规模一致。代码里n_sample min(len(d_min), len(d_maj))这一行就是做这个的。如果少数类样本量太少比如少于 10 个可以考虑先做 SMOTE 过采样再跑秩次 KNN。4.3 把 T 设得太大计算时间爆炸现象在小数据集上跑得挺快换到十万级数据后单次预测要等好几分钟。原因随机秩次 KNN 的时间复杂度是 O(T × N × d)T 是采样次数N 是训练样本量d 是特征维度。T 从 30 增到 300计算时间直接翻十倍。而且 T 增大到一定程度后结果提升微乎其微。解决先用 T20 跑一轮看少数类 F1 是否收敛。如果 T20 和 T40 的结果差异小于 1%就没必要继续增大。另外可以用 KD-Tree 或 Ball-Tree 加速距离计算sklearn 的KNeighborsClassifier底层已经做了这个优化手写实现时可以考虑用scipy.spatial.cKDTree。4.4 评估指标只看准确率忽略了少数类现象模型准确率 99%看起来很好但少数类一个都没预测对。原因不平衡数据下准确率是误导性指标。多数类占比 99%全猜多数类就有 99% 准确率但少数类召回率为零。这是不平衡分类最常见的评估陷阱。解决用少数类的精确率、召回率、F1 值作为主要评估指标辅以 AUC-ROC 和 AUC-PR。AUC-PR 在不平衡场景下比 AUC-ROC 更敏感推荐优先看。如果业务对误报和漏报的代价不同还可以用代价敏感评估矩阵。4.5 训练集和测试集划分时没做分层测试集里没有少数类样本现象训练时一切正常测试时发现测试集里少数类样本数为零评估指标无法计算。原因随机划分数据时如果少数类占比极低比如 0.1%测试集里可能一个少数类样本都没有。这不是模型的问题是数据划分的问题。解决用分层划分stratifyy保证训练集和测试集中各类别比例一致。如果少数类样本实在太少考虑用交叉验证代替单次划分或者用留一法Leave-One-Out评估。5. 进阶技巧把秩次 KNN 嵌进集成框架5.1 用 Bagging 包装秩次 KNN降低方差的实际操作单次秩次 KNN 的预测结果受随机种子影响较大用 Bagging 包装能显著降低方差。做法是对训练集做 Bootstrap 采样训练多个秩次 KNN 基分类器然后投票。from sklearn.base import BaseEstimator, ClassifierMixin from sklearn.utils import resample class BaggingRandomRankKNN(BaseEstimator, ClassifierMixin): def __init__(self, n_estimators10, k5, T20, random_state42): self.n_estimators n_estimators self.k k self.T T self.random_state random_state def fit(self, X, y): self.models_ [] rng np.random.RandomState(self.random_state) for i in range(self.n_estimators): # Bootstrap 采样同时对少数类做过采样补偿 X_boot, y_boot resample(X, y, random_staterng.randint(10000)) self.models_.append((X_boot, y_boot)) self.classes_ np.unique(y) return self def predict(self, X): all_preds [] for X_boot, y_boot in self.models_: preds random_rank_knn(X_boot, y_boot, X, kself.k, Tself.T) all_preds.append(preds) # 多数投票 all_preds np.array(all_preds) final [] for i in range(all_preds.shape[1]): vals, counts np.unique(all_preds[:, i], return_countsTrue) final.append(vals[np.argmax(counts)]) return np.array(final)这个 Bagging 包装器的关键在fit方法里每次 Bootstrap 采样后如果少数类样本被进一步稀释需要在采样时对少数类做过采样补偿。简单做法是先用resample对少数类单独过采样再和多数类合并。n_estimators一般取 10 到 30再多收益递减。5.2 和 SMOTE 组合时的顺序问题秩次 KNN 和 SMOTE 组合时顺序很关键。常见做法是先 SMOTE 过采样再跑秩次 KNN但这样有一个隐患SMOTE 生成的合成样本可能落在多数类区域引入噪声。更稳的做法是在每次随机秩次采样时对少数类子集做 SMOTE 增强而不是在预处理阶段一次性生成。from imblearn.over_sampling import SMOTE def random_rank_knn_with_smote(X_train, y_train, X_test, k5, T20): 在每次随机采样时对少数类做 SMOTE 增强 counts Counter(y_train) minority_class min(counts, keycounts.get) idx_min np.where(y_train minority_class)[0] idx_maj np.where(y_train ! minority_class)[0] predictions [] for x in X_test: dists np.linalg.norm(X_train - x, axis1) d_min np.sort(dists[idx_min]) d_maj np.sort(dists[idx_maj]) stats_min, stats_maj [], [] for _ in range(T): # 对少数类做 SMOTE 增强后再采样 n_need min(len(d_min) * 2, len(d_maj)) if len(d_min) n_need: # 简化处理重复采样少数类距离 d_min_aug np.random.choice(d_min, sizen_need, replaceTrue) else: d_min_aug d_min maj_sub np.random.choice(d_maj, sizen_need, replaceFalse) k_actual min(k, n_need) pos_min np.random.choice(n_need, sizek_actual, replaceFalse) pos_maj np.random.choice(n_need, sizek_actual, replaceFalse) stats_min.append(np.mean(d_min_aug[pos_min])) stats_maj.append(np.mean(maj_sub[pos_maj])) if np.median(stats_min) np.median(stats_maj): predictions.append(minority_class) else: predictions.append(1 - minority_class) return np.array(predictions)注意这里对少数类的增强是在距离空间做的不是特征空间。严格来说 SMOTE 应该在特征空间生成合成样本再算距离但那样计算量太大。距离空间的近似增强在多数场景下够用如果效果不理想再换回特征空间 SMOTE。5.3 验证秩次规则是否真的生效一个对照实验怎么确认秩次规则在起作用而不是随机化的功劳做一个对照实验把秩次统计量换成简单的距离均值比较其他不变看效果差异。def distance_mean_knn(X_train, y_train, X_test, k5, T20): 对照组不做秩次采样直接比较距离均值 counts Counter(y_train) minority_class min(counts, keycounts.get) idx_min np.where(y_train minority_class)[0] idx_maj np.where(y_train ! minority_class)[0] predictions [] for x in X_test: dists np.linalg.norm(X_train - x, axis1) d_min np.sort(dists[idx_min])[:k] d_maj np.sort(dists[idx_maj])[:k] if np.mean(d_min) np.mean(d_maj): predictions.append(minority_class) else: predictions.append(1 - minority_class) return np.array(predictions)跑完对比后如果秩次 KNN 的少数类 F1 明显高于距离均值对照组说明秩次采样确实在起作用。如果两者差不多说明你的数据可能不需要秩次规则简单的距离比较就够了。这个对照实验能帮你判断值不值得上这套方法。我自己的习惯是拿到不平衡数据先跑标准 KNN 做基线再跑距离均值对照组最后跑秩次 KNN。三组结果摆在一起秩次规则有没有用一目了然。如果秩次 KNN 比对照组提升不到 2 个百分点我一般不会上这套方法直接用代价敏感学习或简单过采样更省事。希望帮到你。本文还有配套的精品资源点击获取