#导入相关的包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import mglearn
import warnings
warnings.filterwarnings('ignore')
K—NN算法
输入:
- 训练数据集$$T=\{(x_1,y_1),(x_2,y_2),..,(x_n,y_n)\}$$
- 实例特征向量x。
输出:实例x所属的类别y。
- 根据给定的距离度量,找出距离x最近的k个点。
- 根据预设的分类决策规则(如多数表决),决定x的类别y。
可以看出,K近邻算法的三要素分别为:k值、距离度量方法、分类决策规则。k近邻没有显式的学习过程。
mglearn.plots.plot_knn_classification(n_neighbors=3)
上图给出了K-NN算法的示例,五角星测试集数据点根据离各自最近的三个训练集点进行投票而分类。
K近邻模型
距离度量
- $L_p距离$:$$L_p(x_i,x_j)=(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^p)^{\frac 1p}$$
- 欧式距离:$p=2$$$L_p(x_i,x_j)=(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^2)^{\frac 12}$$
- 曼哈顿距离:$p=1$$$L_p(x_i,x_j)=\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|$$
k值的选择
K值的选择会对K近邻法的结果产生重大影响,如果选择较小的K值,就相当于用较小的邻域中的训练实例进行预测,“学习"的近似误差(approximation error)会减小,只有与输入实例较近的(相似的)训练实例才会对预测结果起作用。但缺点是“学习"的估计误差(estimation error)会增大,预测结果会对近邻的实例点非常敏感。如果邻近的实例点恰巧是噪声,预剥就会出错。换句话说,K值的减小就意味着整体模型变得复杂,容易发生过拟合。
如果选择较大的K值,就相当于用较大邻域中的训练实例进行预测。其优点是可以减少学习的估计误差,但缺点是学习的近似误差会增大。这时与输入实例较远的(不相似的)训练实例也会对预测起作用,使预测发生错误。K值的增大就意味着整体的模型变得简单,如果k=N,那么无论输入实例是什么,都将简单地预判它属于在训练实例中最多的类,这时,模型过千简单,完全忽略训练实例中的大量有用信息,是不可取的。在应用中,K值一般取一个比较小的数值,通常采用交叉验证法来选取最优的K值。
分类决策规则
通常采用多数表决法。
KNN的sklearn使用
K-NN分类使用距离目标点最近的K个点的类别来进行“投票”,得票数多的类别将被标记为预测类别。
- KNN算法的线性扫描算法:
对于每一个在测试集中的数据点:
- 计算目标的数据点(需要分类的数据点)与该数据点的距离
- 将距离排序:从小到大
- 选取前K个最短距离
- 选取这K个中最多的分类类别
- 返回该类别来作为目标数据点的预测值
由于线性扫描算法需要计算每一个点到待预测点的距离,复杂度较大,还有很多其他的数据结构来存储数据,以减少计算次数,提高搜索效率,如kd树等。
下面使用sklearn应用K近邻算法:
#导入数据集
from sklearn.model_selection import train_test_split
X,y = mglearn.datasets.make_forge()
print('自变量前五行:\n{}'.format(X[:5]))
print('因变量:{}'.format(y))
自变量前五行:
[[ 9.96346605 4.59676542]
[11.0329545 -0.16816717]
[11.54155807 5.21116083]
[ 8.69289001 1.54322016]
[ 8.1062269 4.28695977]]
因变量:[1 0 1 0 0 1 1 0 1 1 1 1 0 0 1 1 1 0 0 1 0 0 0 0 1 0]
from sklearn.neighbors import KNeighborsClassifier
X_train,X_test,y_train,y_test = train_test_split(X,y,stratify=y,random_state=42)
KNC = KNeighborsClassifier(n_neighbors=5).fit(X_train,y_train)
KNC.predict(X_test),y_test
(array([1, 0, 1, 0, 0, 0, 0]), array([1, 0, 1, 1, 0, 0, 0]))
print('训练集精度:{}'.format(KNC.score(X_train,y_train)))
print('测试集精度:{}'.format(KNC.score(X_test,y_test)))
训练集精度:0.9473684210526315
测试集精度:0.8571428571428571
在这里,train_test_split函数将输入的X,y划分为四部分,X_train,y_train,X_test,y_test,参数stratify=y表示在测试集与训练集划分时,各个部分的数据类型与y中两种类别的数据比例相同,防止分配不均影响模型性能。random_state参数限定了每次运行划分函数都获得同样的结果。因此,影响最终模型精度的因素有:
1)每次数据划分时,划分到不同组别的数据不同。
2)模型参数,即选择的最近邻数量不同。
第一项在训练模型时使用交叉验证的方式尽量降低随机性对模型的影响,下面将通过调整参数n_neigbors调整模型精度。
#本例使用乳腺癌数据演示KNN模型调参
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
X_train,X_test,y_train,y_test = train_test_split(cancer.data,cancer.target,stratify = cancer.target,random_state = 66)
training_accuracy=[]
test_accuracy = []
for i in range(1,11):
KNC = KNeighborsClassifier(n_neighbors=i).fit(X_train,y_train)
training_accuracy.append(KNC.score(X_train,y_train))
test_accuracy.append(KNC.score(X_test,y_test))
plt.plot(range(1,11),training_accuracy,label = 'training_accuracy')
plt.plot(range(1,11),test_accuracy,label='test_accuracy')
plt.xlabel('n_neighbors')
plt.ylabel('Accuracy')
plt.legend()
从上图可以看出,当近邻数为6时,模型具有最大泛化精度。
小结:
K值的选择
选择较小的k值,训练误差会减小,泛化误差会增大,换句话说,K值的减小就意味着整体模型变得复杂,容易发生过拟合;
选择较大的k值,可以减少泛化误差,训练误差会增大。K值的增大就意味着整体的模型变得简单,容易欠拟合。
一个极端是k等于样本数m,则完全没有分类,此时无论输入实例是什么,都只是简单的预测它属于在训练实例中最多的类,模型过于简单。
距离的度量
欧式距离$$D(x,y)=\sqrt {(x_1−y_1)^2+(x_2−y_2)2+...+(x_n−y_n)^2}=\sqrt{\displaystyle\sum_{i=1}^n(x_i−y_i)^2}$$
曼哈顿距离$$D(x,y)=|x_1−y_1|+|x_2−y_2|+...+|x_n−y_n|=\displaystyle\sum_{i=1}^n|x_i−y_i|$$
闵可夫斯基距离(Minkowski Distance)$$D(x,y)=\sqrt[p]{(|x_1−y_1|)^p+(|x_2−y_2|)^p+...+(|x_n−y_n|)^p}=\sqrt[p]{\displaystyle\sum_{i=1}^n|x_i−y_i|^p}$$
欧式距离和曼哈顿距离分别是闵可夫斯基距离中p为2、1时的特殊距离。
- 优缺点及参数
优点:
- 理论成熟,思想简单,既可以用来做分类也可以用来做回归
- 可用于非线性分类
- 训练时间复杂度比支持向量机之类的算法低,仅为O(n)
- 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
- 由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
- 该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分
缺点:
- 计算量大,尤其是特征数非常多的时候
- 样本不平衡的时候,对稀有类别的预测准确率低
- KD树,球树之类的模型建立需要大量的内存
- 使用懒散学习方法,基本上不学习,导致预测时速度比起逻辑回归之类的算法慢
- 相比决策树模型,KNN模型可解释性不强
主要参数:KNeiborsClassifier(n_neighbors),距离度量方法。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。