作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客
本文网址:[机器学习与scikit-learn-18]:算法-K近邻算法KNN的原理与代码实例_文火冰糖(王文兵)的博客-CSDN博客
目录
第1章 K近邻算法概述
1.1 什么是K近邻
K近邻就是离某个n维向量点的距离最近的K个邻居。
K近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。
该方法的思路是:在特征空间中,如果一个样本附近的k个最近(即特征空间中最邻近)样本的大多数属于某一个类别,则该样本也属于这个类别。即用周围的环境的类型来判断该向量点的分类类型。
用官方的话来说,所谓K近邻算法,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居), 这K个实例的多数属于某个类,就把该输入实例分类到这个类中。
1.2 K近邻的本质
分类模型的基本假设是:
相同类型的向量点在空间中的位置分布是相邻的,不同向量点之间有清晰的边界,我们可以通过某种数学模型,可以把这些不同类型的点可以区分开。这个数学模型可以是线性的直线、折现,也可以是非线性的二次或高次曲线,也可以是圆。
K近邻的本质是:以待判决的位置类别的点为中心画圆,该圆能够包含K个已知标签样本点,已知样本点数量最大的类别来作为未知点的类别。
1.3 K近邻的优缺点
优点:简单,只要计算出所有已知点与待判断点的距离,然后进行排序,选择距离最小的K个点,然后对K个已知类别的点进行统计即可。
缺点:判断的准确性与待测向量点的位置强相关,在多个分类的边界处,出错的概率很大,而在分类点的中心位置处,准确率比较高。如下图中红色圈中的点。
1.4 算法的基本过程
如图所示,有两类不同的样本数据,分别用蓝色的小正方形和红色的小三角形表示,而图正中间的那个绿色的圆所标示的数据则是待分类的数据。也就是说,现在, 我们不知道中间那个绿色的数据是从属于哪一类(蓝色小正方形or红色小三角形)。
下面,我们就要解决这个问题:给这个绿色的圆分类。
我们常说,物以类聚,人以群分,判别一个人是一个什么样品质特征的人,常常可以从他/她身边的朋友入手,所谓观其友,而识其人。我们不是要判别图中那个绿色的圆是属于哪一类数据么,好说,从它的身边的邻居和朋友下手。
但一次性看多少个邻居呢?这是需要预先设置的。从图中,你还能看到:
(1)如果K=3
- 计算所有点与绿色圆点的距离,并排序
- 选择或统计绿色圆点的最近的3个邻居,统计总数K=3
- 统计:红色小三角形的个数=2
- 统计:蓝色小正方形的个数=1
- 少数服从多数:红色较多,判决绿色圆点属于红色一类
- 计算可能性:2/3 = 66%为红色的可能性
(2)如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
- 计算所有点与绿色圆点的距离,并排序
- 选择或统计绿色圆点的最近的5个邻居,统计总数K=5
- 统计:红色小三角形的个数=2
- 统计:蓝色小正方形的个数=3
- 少数服从多数:蓝色较多,判决绿色圆点属于蓝色一类
- 计算可能性:3/5 = 60%为蓝色的可能性
可以看出,K值不同,判断的结果不同.
在实际应用中,通常采用交叉验证的方法从连续n个不同的K值的预测进行综合比较,选择最优的 K 值。随着训练实例数目趋向于无穷和 K=1 时,误差率不会超过贝叶斯误差率的2倍,如果K也趋向于无穷,则误差率趋向于贝叶斯误差率。即误差在[贝叶斯误差,二倍贝叶斯误差]之间。
因此,其性能不如贝叶斯算法。
第2章 scikit-learn对K近邻算法的支持
2.1 scikit-learnK近邻库概述
近邻法是一个算法大类,而不是单个算法。
在scikit-learn 中,与近邻法这一大类相关的类库都在sklearn.neighbors包之中。
- KNN分类树的类是KNeighborsClassifier,仅仅限制个数
- KNN回归树的类是KNeighborsRegressor,仅仅限制个数
- 限定半径最近邻分类树的类RadiusNeighborsClassifier,仅仅限定半径
- 限定半径最近邻回归树的类RadiusNeighborsRegressor, 仅仅限定半径
- 以及最近质心分类算法NearestCentroid。
在这些算法中,KNN分类和回归的类参数完全一样。
限定半径最近邻法分类和回归的类的主要参数也和KNN基本一样。
比较特别是的最近质心分类算法,由于它是直接选择最近质心来分类,所以仅有两个参数,距离度量和特征选择距离阈值,比较简单。
另外几个在sklearn.neighbors包中但不是做分类回归预测的类也值得关注。kneighbors_graph类返回用KNN时和每个样本最近的K个训练集样本的位置。radius_neighbors_graph返回用限定半径最近邻法时和每个样本在限定半径内的训练集样本的位置。
NearestNeighbors是个大杂烩,它即可以返回用KNN时和每个样本最近的K个训练集样本的位置,也可以返回用限定半径最近邻法时和每个样本最近的训练集样本的位置,常常用在聚类模型中。
2.2 K近邻参数详解
下面这个表格非常方便的解读了不同类型的类,他们的参数的相同点和不同点。
参数 | KNeighbors Classifier | KNeighbors Regressor | RadiusNeighbors Classifier | RadiusNeighbors Regressor |
KNN中的K值n_neighbors | 最邻近点的个数K K值的选择与样本分布有关 一般选择一个较小的K值,可以通过交叉验证来选择一个比较优的K值,默认值是5。如果数据是三维一下的,如果数据是三维或者三维以下的,可以通过可视化观察来调参。 | 不适用于限定半径最近邻法 | ||
限定半径最近邻法中的半radius | 不适用于KNN | 半径的值 半径的选择与样本分布有关,可以通过交叉验证来选择一个较小的半径,尽量保证每类训练样本其他类别样本的距离较远,默认值是1.0。如果数据是三维或者三维以下的,可以通过可视化观察来调参。 | ||
近邻权weights | 主要用于标识每个样本的近邻样本的权重 如果是KNN,就是K个近邻样本的权重, 如果是限定半径最近邻,就是在距离在半径以内的近邻样本的权重。 可以选择"uniform","distance" 或者自定义权重。 选择默认的"uniform",意味着所有最近邻样本权重都一样,在做预测时一视同仁。 如果是"distance",则权重和距离成反比例,即距离预测目标更近的近邻具有更高的权重,这样在预测类别或者做回归时,更近的近邻所占的影响因子会更加大。 当然,我们也可以自定义权重,即自定义一个函数,输入是距离值,输出是权重值。这样我们可以自己控制不同的距离所对应的权重。 一般来说,如果样本的分布是比较成簇的,即各类样本都在相对分开的簇中时,我们用默认的"uniform"就可以了,如果样本的分布比较乱,规律不好寻找,选择"distance"是一个比较好的选择。 如果用"distance"发现预测的效果的还是不好,可以考虑自定义距离权重来调优这个参数。 | |||
KNN和限定半径最近邻法使用的算法algorithm | 算法一共有三种,第一种是蛮力实现,第二种是KD树实现,第三种是球树实现。 这三种方法在K近邻法(KNN)原理小结中都有讲述,如果不熟悉可以去复习下。对于这个参数,一共有4种可选输入,‘brute’对应第一种蛮力实现,‘kd_tree’对应第二种KD树实现,‘ball_tree’对应第三种的球树实现, ‘auto’则会在上面三种算法中做权衡,选择一个拟合最好的最优算法。需要注意的是,如果输入样本特征是稀疏的时候,无论我们选择哪种算法,最后scikit-learn都会去用蛮力实现‘brute’。 个人的经验,如果样本少特征也少,使用默认的 ‘auto’就够了。 如果数据量很大或者特征也很多,用"auto"建树时间会很长,效率不高,建议选择KD树实现‘kd_tree’,此时如果发现‘kd_tree’速度比较慢或者已经知道样本分布不是很均匀时,可以尝试用‘ball_tree’。而如果输入样本是稀疏的,无论你选择哪个算法最后实际运行的都是‘brute’。 | |||
停止建子树的叶子节点阈值leaf_size | 这个值控制了使用KD树或者球树时, 停止建子树的叶子节点数量的阈值。这个值越小,则生成的KD树或者球树就越大,层数越深,建树时间越长,反之,则生成的KD树或者球树会小,层数较浅,建树时间较短。默认是30. 这个值一般依赖于样本的数量,随着样本数量的增加,这个值必须要增加,否则不光建树预测的时间长,还容易过拟合。可以通过交叉验证来选择一个适中的值。 如果使用的算法是蛮力实现,则这个参数可以忽略。 | |||
距离度量metric | K近邻法和限定半径最近邻法类可以使用的距离度量较多,一般来说默认的欧式距离(即p=2的闵可夫斯基距离)就可以满足我们的需求。可以使用的距离度量参数有: a) 欧式距离 “euclidean”: ∑i=1n(xi−yi)2−−−−−−−−−−√∑i=1n(xi−yi)2 b) 曼哈顿距离 “manhattan”: ∑i=1n|xi−yi|∑i=1n|xi−yi| c) 切比雪夫距离“chebyshev”: max|xi−yi|(i=1,2,...n)max|xi−yi|(i=1,2,...n) d) 闵可夫斯基距离 “minkowski”(默认参数): ∑i=1n(|xi−yi|)p−−−−−−−−−−−√p∑i=1n(|xi−yi|)pp p=1为曼哈顿距离, p=2为欧式距离。 e) 带权重闵可夫斯基距离 “wminkowski”: ∑i=1n(w∗|xi−yi|)p−−−−−−−−−−−−−−√p∑i=1n(w∗|xi−yi|)pp 其中w为特征权重 f) 标准化欧式距离 “seuclidean”: 即对于各特征维度做了归一化以后的欧式距离。此时各样本特征维度的均值为0,方差为1. g) 马氏距离“mahalanobis”:(x−y)TS−1(x−y)−−−−−−−−−−−−−−−√(x−y)TS−1(x−y) 其中,S−1S−1为样本协方差矩阵的逆矩阵。当样本分布独立时, S为单位矩阵,此时马氏距离等同于欧式距离 还有一些其他不是实数的距离度量,一般在KNN之类的算法用不上,这里也就不列了。 | |||
距离度量附属参数p | p是使用距离度量参数 metric 附属参数,只用于闵可夫斯基距离和带权重闵可夫斯基距离中p值的选择,p=1为曼哈顿距离, p=2为欧式距离。默认为2 | |||
距离度量其他附属参数metric_params | 一般都用不上,主要是用于带权重闵可夫斯基距离的权重,以及其他一些比较复杂的距离度量的参数。 | |||
并行处理任务数n_jobs | 主要用于多核CPU时的并行处理,加快建立KNN树和预测搜索的速度。一般用默认的-1就可以了,即所有的CPU核都参与计算。 | 不适用于限定半径最近邻法 | ||
异常点类别选择outlier_label | 不适用于KNN | 主要用于预测时,如果目标点半径内没有任何训练集的样本点时,应该标记的类别,不建议选择默认值 none,因为这样遇到异常点会报错。一般设置为训练集里最多样本的类别。 | 不适用于限定半径最近邻回归 |
第3章 代码实例
3.1 创建数据集
# 创建数据集
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets import make_classification
# X为样本特征,Y为样本类别输出, 共1000个样本,每个样本2个特征,输出有3个类别,没有冗余特征,每个类别一个簇
X, Y = make_classification(n_samples=1000, n_features=2, n_redundant=0, n_clusters_per_class=1, n_classes=3)
plt.scatter(X[:, 0], X[:, 1], marker='o', c=Y)
plt.show()
print(X.shape) # 二维特征数据
print(Y.shape) # 一维特征数据
3.2 创建并训练模型
# 模型建立与训练
from sklearn import neighbors
from sklearn.model_selection import train_test_split
# 分割数据集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, Y,test_size=0.3)
print(X.shape)
print(Y.shape)
# 构建KNeighborsClassifier分类对象
clf = neighbors.KNeighborsClassifier(n_neighbors = 15 , weights='distance')
# 在训练集上模型训练
clf.fit(Xtrain, Ytrain)
# 统计分数
score = clf.score(Xtest,Ytest)
print("测试集分数:", score)
score = clf.score(Xtrain,Ytrain)
print("训练集分数:", score)
score = clf.score(X,Y)
print("整个数据集分数:", score)
(1000, 2) (1000,) 测试集分数: 0.9466666666666667 训练集分数: 1.0 整个数据集分数: 0.984
3.3 可视化背景图和样本
# 可视化数据集
from matplotlib.colors import ListedColormap
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
# 确认数据集的边界:二维特征数据(X1,X2)
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
print("x1_min =", x1_min)
print("x1_max =", x1_max)
print("x2_min =", x2_min)
print("x2_max =", x2_max)
# 生成连续的随机数据来做测试集
# meshgrid: 生成网格矩阵,可以是二维网格矩阵
x1, x2 = np.meshgrid(np.arange(x1_min, x1_max, 0.02), np.arange(x2_min, x2_max, 0.02))
# (x1,x2) 构成一个样本点
print("x1.shape =", x1.shape)
print("x2.shape =", x2.shape)
# 对随机数据进行预测,预测结果y
x = np.c_[x1.ravel(), x2.ravel()]
# 重新还原成样本格式
print("x.shape =", x.shape)
# 对连续样本数据进行预测
y = clf.predict(x)
print("y.shape =", y.shape)
# 画出测试集数据(连续量)
y = y.reshape(x1.shape)
print("y.shape =", y.shape)
print(y)
plt.figure()
# 绘制彩色背景图, 坐标由(x1,x2)确定,颜色有y的分类类型确定:这里y有三种类型,即绘制三种颜色
plt.pcolormesh(x1, x2, y, cmap=cmap_light)
# 在背景图上再画出所有训练集数据(离散量),用于直观比较
plt.scatter(X[:, 0], X[:, 1], c=Y, cmap=cmap_bold)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("3-Class classification (k = 15, weights = 'distance')" )
x1_min = -3.7955364765385884 x1_max = 4.551430440465633 x2_min = -4.171884152492739 x2_max = 5.255183073187921 x1.shape = (472, 418) x2.shape = (472, 418) x.shape = (197296, 2) y.shape = (197296,) y.shape = (472, 418) [[0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] ... [1 1 1 ... 2 2 2] [1 1 1 ... 2 2 2] [1 1 1 ... 2 2 2]]
作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客
本文网址:https://blog.csdn.net/HiWangWenBing/article/details/123410611