前言
k近邻法 (k-nearest neighbor, k-NN) 是一种基本分类与回归方法,是数据挖掘技术中原理最简单的算法之一,核心功能是解决有监督的分类问题。
KNN能够快速高效地解决建立在特殊数据集上的预测分类问题,但其不产生模型。
k近邻法的输入为实例的特征向量,对应与特征空间的点;输出为实例的类别,可以取多类。
k近邻法三个基本要素:k 值的选择、距离度量及分类决策规则。
算法过程
1 计算训练样本和测试样本中每个样本点的距离;
2 对上面所有的距离值进行排序;
3 选前k个最小距离的样本;
4 根据这k个样本的标签进行投票,得到最后的分类类别。
距离度量
特征空间中两个实例点的距离是两个实例点相似程度的反映。
在距离类模型,例如KNN中,有多种常见的距离衡量方法。如欧几里得距离、曼哈顿距离、闵科夫斯基距离、切比雪夫距离及余弦距离。其中欧几里得距离为最常见。
- 欧几里得距离(Euclidean Distance)
在欧几里得空间中,两点之间或多点之间的距离表示又称欧几里得度量。
- 曼哈顿距离(Manhattan Distance)
曼哈顿距离,正式意义为城市区块距离,也被称作街道距离,该距离在欧几里得空间的固定直角坐标所形成的线段产生的投影的距离总和。其计算方法相当于是欧式距离的1次方表示形式,其基本计算公式如下:
- 闵科夫斯基距离(Minkowski Distance)
闵氏距离不是一种距离,而是一组距离的定义,是对多个距离度量公式的概括性的表述。无论是欧式距离还是曼哈顿距离,都可视为闵可夫斯基距离的一种特例。
其中p是一个变参数:
- 当p=1时,就是曼哈顿距离;
- 当p=2时,就是欧氏距离;
- 当p→∞时,就是切比雪夫距离。
因此,根据变参数的不同,闵氏距离可以表示某一类 / 种的距离。
闵氏距离,包括曼哈顿距离、欧氏距离和切比雪夫距离都存在明显的缺点。
e.g. 二维样本(身高[单位:cm],体重[单位:kg]), 现有三个样本:a(180,50),b(190,50),c(180,60)。那么a与b的闵氏距离(无论是曼哈顿距离、欧氏距离或切比雪夫距离)等于a与c的闵氏距离。但实际上身高的10cm并不能和体重的10kg划等号。
闵氏距离的缺点:
(1)将各个分量的量纲(scale),也就是"单位"相同的看待了;
(2)未考虑各个分量的分布(期望,方差等)可能是不同的。
- 切比雪夫距离 (Chebyshev Distance)
国际象棋中,国王可以直行、横行、斜行,所以国王走一步可以移动到相邻8个方格中的任意一个。国王从格子(xa,ya)走到格子(xb,yb)最少需要多少步?这个距离就叫切比雪夫距离。
- 余弦距离(Cosine Distance)
余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个样本差异的大小。余弦值越接近1, 说明两个向量夹角越接近0度,表明两个向量越相似。几何中,夹角余弦可用来衡量两个向量方向的差异;机器学习中,借用这一概念来衡量样本向量之间的差异。
K值选择
k 值的选择会对KNN 算法的结果产生重大影响。
- k 值的减小就意味着整体模型变得复杂,学习器容易受到由于训练数据中的噪声而产生的过分拟合的影响。
- k 值的的增大就意味着整体的模型变得简单。如果k太大,最近邻分类器可能会将测试样例分类错误,因为k个最近邻中可能包含了距离较远的,并非同类的数据点。
在应用中,k 值一般选取一个较小的数值,通常采用交叉验证来选取最优的k 值。
分类决策规则
根据 “少数服从多数,一 点算一票” 的原则进行判断,数量最多标签类别就是x的标签类别。其中涉及到的原理是"越相近越相似",这也是KNN的基本假设。
算法不足
KNN算法作为一种较简单的算法,存在不足之处。
- 没有明显的训练过程,它是 "懒惰学习"的典型代表,它在训练阶段所做的仅仅是将样本保存起来,如果训练集很大,必须使用大量的存储空间,训练时间开销为零。
- KNN必须对每一个测试点来计算到每一个训练数据点的距离, 并且这些距离点涉及到所有的特征,当数据的维度很大,数据量也很大的时候,KNN的计算会成为诅咒。
kd树
由于上述的不足,为了提高KNN搜索的速度,可以利用特殊的数据存储形式来减少计算距离的次数。kd树就是一种以二叉树的形式存储数据的方法。
kd树就是对k维空间的一个划分。构造kd树相当于不断用垂直于坐标轴的超平面将k维空间切分,构成一系列k维超矩阵区域。kd树的每一个节点对应一个超矩阵区域。
代码实现
# 从sklearn.neighbors里导入 KNN分类器的类
from sklearn.neighbors import KNeighborsClassifier
# 通过类实例化一个knn分类器对象
# 类中的具体参数
# KNeighborsClassifier(n_neighbors=5,weights='uniform',algorithm='auto',leaf_size=30,p=2, metric='minkowski',metric_params=None,n_jobs=None,**kwargs,)
knn_clf = KNeighborsClassifier(n_neighbors=k)
# 通过对象调fit()方法, 传入训练集, 训练模型
knn_clf.fit(X_train, y_train)X
# 训练好的模型, 通过其他接口, 传入测试集查看模型效果
knn_clf.score(X_test, y_test)
# 预测结果
knn_clf.predict(X_test)
knn_clf.predict_proba(X_test)
实现无监督最近邻KNN学习。它充当了三种不同的最近邻(nearest neighbors)算法的统一接口:BallTree、KDTree和基于sklear. metrics.pairwise例程的暴力算法。
邻居搜索算法的选择是通过关键字’algorithm’来控制的,它必须是 [‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’] 之一。 当默认值’auto’时,算法尝试从训练数据中确定最佳方法。
案例实战
1、鸢尾花
数据:鸢尾花数据集
https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_iris.html
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from matplotlib.colors import ListedColormap
#导入iris数据
from sklearn.datasets import load_iris
iris = load_iris()
X=iris.data[:,:2] #只取前两列
y=iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y,random_state=42) #划分数据,random_state固定划分方式
#导入模型
from sklearn.neighbors import KNeighborsClassifier
#训练模型
n_neighbors = 5
knn = KNeighborsClassifier(n_neighbors=n_neighbors)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
#查看各项得分
print("y_pred",y_pred)
print("y_test",y_test)
print("score on train set", knn.score(X_train, y_train))
print("score on test set", knn.score(X_test, y_test))
print("accuracy score", accuracy_score(y_test