共计 3498 个字符,预计需要花费 9 分钟才能阅读完成。
K 近邻
K-NN 算法可以说是最简单的机器学习算法,他使用距离预测点最近的点的均值来进行预测。k 近邻算法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类。k 近邻算法假设给定一个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其 k 个最近邻的训练实例的类别,通过多数表决等方式进行预测。因此,k 近邻算法不具有显式的学习过程。
k 近邻算法实际上利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”。 k 值的选择、距离度量以及分类决策规则 是 k 近邻算法的三个基本要素。
K—NN 分类
K-NN 分类使用距离目标点最近的 K 个点的类别来进行“投票”,得票数多的类别将被标记为预测类别。
KNN 算法的伪代码:
对于每一个在数据集中的数据点:
- 计算目标的数据点(需要分类的数据点)与该数据点的距离
- 将距离排序:从小到大
- 选取前 K 个最短距离
- 选取这 K 个中最多的分类类别
- 返回该类别来作为目标数据点的预测值
# 导入机器学习的包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import mglearn
import warnings
warnings.filterwarnings('ignore')
mglearn.plots.plot_knn_classification(n_neighbors=3)
上图给出了 K -NN 算法的示例,五角星测试集数据点根据离各自最近的三个训练集点进行投票而分类, 下面我们将使用 forge 数据进行 K -NN 分类。
# 导入数据集
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 时,模型具有最大泛化精度。
KNN 回归
KNN 回归是使用最近 K 个点的因变量均值作为预测值,通常回归效果不佳, 实现与 KNN 分类相同。
小结:
KNN 算法的三要素:K 值的选取、距离度量方式、分类决策规则
分类决策规则通常使用少数服从多数的方法,在此不做说明。
K 值的选择
之前有提到,可以通过交叉验证的方法选择 K 合适的 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), 距离度量方法。