外观
在不平衡数据集上使用 kNN
Using kNNs on Imbalanced Datasets
在使用 kNN(k 近邻)时,有一点总让我格外谨慎和怀疑——它对参数极度敏感。为了更好地理解,请看下面这个虚拟的二维数据集。红色数据点是一个测试样本,我们打算用 kNN 为它生成预测。

假设我们把值设为 k=7。红色样本的预测分两步生成:
- 首先,统计红色数据点的 7 个最近邻。
- 然后,把它分配到这 7 个最近邻中出现次数最多的类别。如下图所示:

问题在于,第 2 步完全建立在“类别贡献”这一概念之上——也就是把对 k 个最近邻贡献最大的类别分配给该数据点。但这种做法有时会惨败,尤其是当某个类别只有很少样本时。

例如,如这张图所示,当 k=7 时,红色数据点永远不可能被分配到黄色类别,无论它离那个簇有多近。
虽然在上面的演示中我们可以很直观地调整超参数 k,但在高维数据集中这种方法并不可行。有两种方式可以应对这个问题。

距离加权(distance-weighted)kNN 是传统 kNN 的一个更稳健的替代方案。顾名思义,它在第 2 步中会考虑与最近邻的距离。结果是,某个邻居越近,它对最终预测的影响就越大。从这张图中可以明显看出它的有效性。
- 在第一幅图中,传统 kNN(k=7)永远无法预测出蓝色类别。
- 在第二幅图中,距离加权 kNN 的预测更加稳健。据我观察,距离加权 kNN 通常比传统 kNN 表现更好。然而这一点可能被忽略,因为 sklearn 的 kNN 实现默认采用的是“uniform”加权方式。

再回想一下上面的演示:

这里有人可能会说,我们必须避免把超参数 k 设为大于数据集中某类别最小样本数的任何值。在某种程度上我同意这一点。但让我告诉你这样做的缺点。在极不平衡的数据集中,把 k 设得过低可能带来很大问题。

给你更多视角:我个人曾在训练集中某个类别只有一两个样本的数据集上使用过 kNN。我
发现把 k 设得很低(比如 1 或 2)会导致性能欠佳,因为模型并没有像使用较大 k 值时那样全面地评估最近邻的模式。换句话说,设置一个相对较大的 k 值通常比使用较低的值能给出更有依据的预测。但我们上面刚刚讨论过,如果把 k 设得很大,多数类别就会主导分类结果:

为了解决这个问题,我发现动态更新超参数 k 要有效得多。更具体地说,这种方法分三步。对每一个测试样本:1. 像通常那样从一个标准的 k 值开始,找到 k 个最近邻。2. 接下来,按如下方式更新 k 的值:a. 对于出现在 k 个最近邻中的所有唯一类别,统计它们各自拥有的训练样本总数。

b. 将 k 的值更新为:

- 现在仅对前 k' 个邻居执行多数投票。这在直觉上也说得通:
- 如果某个少数类别出现在前 k 个最近邻中,我们就必须减小 k 的值,以免多数类别占据主导。
- 如果某个少数类别没有出现在前 k 个最近邻中,我们很可能就不会更新 k 的值,而是继续进行全面的分类。唯一的不足是,你在任何开源实现中都找不到这种方法。事实上,在我的项目里我也得自己写自定义实现,所以请把这一点考虑在内。