k近邻法(K-neareset neighbor, KNN)是一种基本分类与回归的算法,对给定的训练实例点和输入实例点,首先确定输入实例点的k个最近邻训练实例点,然后利用这k个训练实例点的类的多数来预测输入实例点的类(少数服从多数的思想)。
一个形象的例子可见一文搞懂k近邻(k-NN)算法(一)
1. k近邻算法
输入:训练数据集
输出:实例
所属的类
- 根据给定的距离度量,在训练集 中找出与 最邻近的 个点,涵盖这 个点的 的邻域记作 ;
- 在
中根据分类决策规则(如多数表决)决定
的类别
:
为指示函数,即当 时 为1,否则 为0。
当 时,称为最近邻算法。
2. k近邻模型三要素
2.1 距离度量
特征空间中两个实例点的距离是两个实例点相似程度的反映,k近邻模型一般使用的距离是欧式距离,即 。
- 一般的
距离(
):设特征空间
是
维实数向量空间
,
,
,
,
的
距离定义为
- 曼哈顿距离: 时
- 欧式距离: 时
2.2 k值
k值的选择会对k近邻法的结果产生重大影响。
- k值小时,相当于用较小的邻域中的训练实例进行预测,k近邻模型更复杂,容易发生过拟合,因为很容易学习到一些噪声而忽略了数据真实的分布;
- k值大时,相当于用较大邻域中的训练实例进行预测,k近邻模型更简单,如果k值太大则完全忽略训练实例汇总的大量有用的信息;
- 在应用中k值一般取一个比较小的数值,通常由交叉验证选择最优的k值。
2.3 分类决策规则
多数表决:由输入实例的 个邻近的训练实例中的多数类决定输入实例的类。
用误分类的概率判断模型好坏,误分类率最低最好,误分类率:
要使误分类率最小即经验风险最小,就要使
最大,所以多数表决规则等价于经验风险最小化。
3. kd树
kd树是二叉树,表示对 维空间的一个划分。构造kd树相当于不断地用垂直于坐标轴的超平面将 维空间切分,构成一系列的 维超矩形区域。
3.1 构造kd树
输入:
维空间数据集
输出:kd树
- 选择以 为坐标轴,以所有实例的 坐标的中位数为切分点,将通过切分点并与坐标轴 垂直的超平面为切分平面,将根结点对应的超矩形区域切分为两个子区域,由此生成左右两个子结点;
- 对于子区域,继续选取 为切分坐标轴, ,将子区域继续切分(这里轮流沿着每一个维度进行分割, 是因为一共有 个维度,在沿着最后一个维度切割之后再重新回到第一个维度);
- 直到切分后的子区域没有实例点存在时停止,从而形成kd树的区域划分(终止时的结点为叶结点)。
3.2 搜索kd树
输入:已构造的kd树;目标点
输出:
的最近邻
- 在kd树中找出包含目标点 的叶结点:从根结点出发,递归地向下访问kd树。目标点 当前维的坐标小于切分点的坐标,移动到左子结点,否则移动到右子结点。直到子结点为叶结点为止。
- 以此叶结点为“当前最近点”。
- 递归地向上回退,在每个结点进行以下操作:
(a) 如果该结点保存的实例点比当前最近点距离目标点更近,则以该实例点为“当前最近点”;
(b) 当前最近点一定存在于该结点一个子结点对应的区域。检查该子结点的父结点的另一子结点对应的区域是否有更近的点。即检查另一子结点对应的区域是否与以目标点为球心,以目标点与“当前最近点”间的距离为半径的超球体相交。若相交,则在对应区域存在距离目标更近的点,移动到另一子结点继续递归搜索;否则向上回退。 - 回退到根结点时,搜索结束,最后的“当前最近点”即为 的最近邻点。
平均计算复杂度为 (实例点随机分布,N:训练实例数),kd树更适用于训练实例数远大于空间维度时的 近邻搜索。当两者接近时,它的效率会迅速下降,几乎接近线性扫描。
3.3 例子
【量化课堂】kd 树算法之详细篇,这个例子可以帮助理解构造kd树和搜索kd树的算法过程。
4. KNN优缺点
4.1 优点
- 理论成熟,思想简单,既可用来做分类也可做回归
- 可用于非线性分类
- 训练时间复杂度比支持向量机之类的算法低,仅为
- 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
- 由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分类样本集来说,KNN方法较其他方法更为合适
- 比较适用于样本容量比较大的类域的自动分类
4.2 缺点
- 计算量大,尤其是特征数非常多时
- 样本不平衡时,对稀有类别的预测准确率低
- KD树、球树之类的模型建立需要大量的内存
- 使用懒散学习方法,基本上不学习,导致预测时速度比起逻辑回归之类的算法慢
- 相对决策树模型,KNN模型可解释性不强
5. sklearn应用
class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None, n_jobs=1, **kwargs)[source]
具体见sklearn文档:sklearn.neighbors.KNeighborsClassifier
# 使用KNN分类需要对数据进行归一化,因为要计算距离
from sklearn.preprocessing import MinMaxScaler
mm = MinMaxScaler()
mm.fit(X_train)
X_train_mm = sc.transform(X_train)
X_test_mm = sc.transform(X_test)
# KNN
from sklearn.neighbors import KNeighborsClassifier
# n_neigbors:k
# metric&p:距离度量,默认为欧式距离(minkowski&2)
knn = KNeighborsClassifier(n_neighbors=5, p=2, metric='minkowski')
knn.fit(X_train_mm, y_train)
详细代码见github
6. reference
- 李航《统计学习方法》
- Python Machine Learning Second Edition
- 一文搞懂k近邻(k-NN)算法(一)
- 【量化课堂】一只兔子帮你理解 kNN
- 【量化课堂】kd 树算法之思路篇
- 【量化课堂】kd 树算法之详细篇
- K近邻法(KNN)原理小结
本文同步发布在我的github.io