微信公众号:王了个博
专注于大数据技术,人工智能和编程语言
个人既可码代码也可以码文字。欢迎转发与关注关注我,在公众号回复 "机器学习" 可领取电子书
什么是KNN?
维基百科给出的解释:
在模式识别领域中,最近邻居法(k-NN算法,又译K-近邻算法)是一种用于分类和回归的非参数统计方法。在这两种情况下,输入包含特征空间(Feature Space)中的k个最接近的训练样本
-
在k-NN分类中,输出是一个分类族群。一个对象的分类是由其邻居的“多数表决”确定的,k个最近邻居(k为正整数,通常较小)中最常见的分类决定了赋予该对象的类别。若k = 1,则该对象的类别直接由最近的一个节点赋予
-
在k-NN回归中,输出是该对象的属性值。该值是其k个最近邻居的值的平均值
最近邻居法采用向量空间模型来分类,概念为相同类别的案例,彼此的相似度高,而可以借由计算与已知类别案例之相似度,来评估未知类别案例可能的分类
无论是分类还是回归,衡量邻居的权重都非常有用,使较近邻居的权重比较远邻居的权重大。例如,一种常见的加权方案是给每个邻居权重赋值为1/ d,其中d是到邻居的距离。
邻居都取自一组已经正确分类(在回归的情况下,指属性值正确)的对象。虽然没要求明确的训练步骤,但这也可以当作是此算法的一个训练样本集。
k-NN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。k-NN方法在类别决策时,只与极少量的相邻样本有关。由于k-NN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,k-NN方法较其他方法更为适合
k-NN算法流程
-
准备数据,对数据进行预处理
后面代码准备的数据是Iris
Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集。数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类 -
选用合适的数据结构存储训练数据和测试元组
-
设定参数,如k
-
维护一个大小为k的按距离由大到小的优先级队列,用于存储最近邻训练元组。随机从训练元组中选取k个元组作为初始的最近邻元组,分别计算测试元组到这k个元组的距离,将训练元组标号和距离存入优先级队列
-
遍历训练元组集,计算当前训练元组与测试元组的距离,将所得距离L 与优先级队列中的最大距离Lmax
-
进行比较。若L>=Lmax,则舍弃该元组,遍历下一个元组。若L < Lmax,删除优先级队列中最大距离的元组,将当前训练元组存入优先级队列
-
遍历完毕,计算优先级队列中k 个元组的多数类,并将其作为测试元组的类别
-
测试元组集测试完毕后计算误差率,继续设定不同的k值重新进行训练,最后取误差率最小的k 值
k-NN的一些数学算法
-
距离度量
根据选择的距离度量(如曼哈顿距离或欧几里得距离),可计算测试实例与训练集中的每个实例点的距离
曼哈顿距离
我们可以定义曼哈顿距离的正式意义为L1-距离,也就是在欧几里得空间的固定直角坐标系上两点所形成的线段对轴产生的投影的距离总和
例如在平面上,坐标(x1, y1)的点P1与坐标(x2, y2)的点P2的曼哈顿距离为
欧几里得距离
欧氏距离也称欧几里得距离或欧几里得度量,是一个通常采用的距离定义,它是在m维空间中两个点之间的真实距离。在二维和三维空间中的欧氏距离的就是两点之间的距离。使用这个距离,欧氏空间成为度量空间。相关联的范数称为欧几里得范数。较早的文献称之为毕达哥拉斯度量
欧氏距离的计算公式
二维空间
三维空间
多维空间
-
k值的选择
k值的选择会对k近邻法的结果产生重大影响。在应用中,k值一般取一个比较小的数值,通常采用交叉验证法来选取最优的k值 -
分类决策规则
k近邻法中的分类决策规则往往是多数表决,即由输入实例的k个邻近的训练实例中的多数类,决定输入实例的类
k近邻算法代码实现
1. 引入依赖
1import numpy as np
2import pandas as pd
3
4# 这里直接引入sklearn里的数据集,iris鸢尾花(上面介绍过)
5from sklearn.datasets import load_iris
6from sklearn.model_selection import train_test_split # 切分数据集为训练集和测试集
7from sklearn.metrics import accuracy_score # 计算分类预测的准确率
2. 数据加载和预处理
1iris = load_iris()
2
3df = pd.DataFrame(data = iris.data, columns = iris.feature_names)
4df['class'] = iris.target
5df['class'] = df['class'].map({0: iris.target_names[0], 1: iris.target_names[1], 2: iris.target_names[2]})
6df.head(10)
7df.describe()
8x = iris.data
9y = iris.target.reshape(-1,1)
10print(x.shape, y.shape)
df.describe()数据效果
print(x.shape, y.shape)数据效果
(150, 4) (150, 1)
3. 划分训练集和测试集
1# 划分训练集和测试集
2x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=35, stratify=y)
3
4print(x_train.shape, y_train.shape)
5print(x_test.shape, y_test.shape)
6a = np.array([[3,2,4,2],
7 [2,1,4,23],
8 [12,3,2,3],
9 [2,3,15,23],
10 [1,3,2,3],
11 [13,3,2,2],
12 [213,16,3,63],
13 [23,62,23,23],
14 [23,16,23,43]])
15b = np.array([[1,1,1,1]])
16print(a-b)
17np.sum(np.abs(a - b), axis=1)
18dist = np.sqrt( np.sum((a-b) ** 2, axis=1) )
19print(dist)
结果
(105, 4) (105, 1)
(45, 4) (45, 1)
[[ 2 1 3 1]
[ 1 0 3 22]
[ 11 2 1 2]
[ 1 2 14 22]
[ 0 2 1 2]
[ 12 2 1 1]
[212 15 2 62]
[ 22 61 22 22]
[ 22 15 22 42]]
[ 3.87298335 22.22611077 11.40175425 26.17250466
3.12.24744871 221.39783197 71.92357055 54.3783045 ]
4. 核心算法实现
1# 距离函数定义
2def l1_distance(a, b):
3 return np.sum(np.abs(a-b), axis=1)
4def l2_distance(a, b):
5 return np.sqrt( np.sum((a-b) ** 2, axis=1) )
6
7# 分类器实现
8class kNN(object):
9 # 定义一个初始化方法,__init__ 是类的构造方法
10 def __init__(self, n_neighbors = 1, dist_func = l1_distance):
11 self.n_neighbors = n_neighbors
12 self.dist_func = dist_func
13
14 # 训练模型方法
15 def fit(self, x, y):
16 self.x_train = x
17 self.y_train = y
18
19 # 模型预测方法
20 def predict(self, x):
21 # 初始化预测分类数组
22 y_pred = np.zeros( (x.shape[0], 1), dtype=self.y_train.dtype )
23
24 # 遍历输入的x数据点,取出每一个数据点的序号i和数据x_test
25 for i, x_test in enumerate(x):
26 # x_test跟所有训练数据计算距离
27 distances = self.dist_func(self.x_train, x_test)
28
29 # 得到的距离按照由近到远排序,取出索引值
30 nn_index = np.argsort(distances)
31
32 # 选取最近的k个点,保存它们对应的分类类别
33 nn_y = self.y_train[ nn_index[:self.n_neighbors] ].ravel()
34
35 # 统计类别中出现频率最高的那个,赋给y_pred[i]
36 y_pred[i] = np.argmax( np.bincount(nn_y) )
37
38 return y_pred
39
40
41nn_index = np.argsort(dist)
42print("dist: ",dist)
43print("nn_index: ",nn_index)
44nn_y = y_train[ nn_index[:9] ].ravel()
45#print(y_train[:8])
46print("nn_y: ",nn_y)
47print(np.bincount(nn_y))
48print(np.argmax(np.bincount(nn_y)))
结果
dist: [ 3.87298335 22.22611077 11.40175425 26.17250466 3.12.24744871 221.39783197 71.92357055 54.3783045 ]
nn_index: [4 0 2 5 1 3 8 7 6]
nn_y: [1 1 2 1 2 2 0 0 1]
[2 4 3]
1
5. 测试
1# 定义一个knn实例
2knn = kNN(n_neighbors = 3)
3# 训练模型
4knn.fit(x_train, y_train)
5# 传入测试数据,做预测
6y_pred = knn.predict(x_test)
7
8print(y_test.ravel())
9print(y_pred.ravel())
10
11# 求出预测准确率
12accuracy = accuracy_score(y_test, y_pred)
13
14print("预测准确率: ", accuracy)
结果
[2 1 2 2 0 0 2 0 1 1 2 0 1 1 1 2 2 0 1 2 1 0 0 0 1 2 0 2 0 0 2 1 0 2 1 0 2
1 2 2 1 1 1 0 0]
[2 1 2 2 0 0 2 0 1 1 1 0 1 1 1 2 2 0 1 2 1 0 0 0 1 2 0 2 0 0 2 1 0 2 1 0 2
1 2 1 1 2 1 0 0]
预测准确率: 0.9333333333333333
1# 定义一个knn实例
2knn = kNN()
3# 训练模型
4knn.fit(x_train, y_train)
5
6# 保存结果list
7result_list = []
8
9# 针对不同的参数选取,做预测
10for p in [1, 2]:
11 knn.dist_func = l1_distance if p == 1 else l2_distance
12
13 # 考虑不同的k取值,步长为2
14 for k in range(1, 10, 2):
15 knn.n_neighbors = k
16 # 传入测试数据,做预测
17 y_pred = knn.predict(x_test)
18 # 求出预测准确率
19 accuracy = accuracy_score(y_test, y_pred)
20 result_list.append([k, 'l1_distance' if p == 1 else 'l2_distance', accuracy])
21df = pd.DataFrame(result_list, columns=['k', '距离函数', '预测准确率'])
22df
最终结果
k | 距离函数 | 预测准确率 | |
---|---|---|---|
0 | 1 | l1_distance | 0.933333 |
1 | 3 | l1_distance | 0.933333 |
2 | 5 | l1_distance | 0.977778 |
3 | 7 | l1_distance | 0.955556 |
4 | 9 | l1_distance | 0.955556 |
5 | 1 | l2_distance | 0.933333 |
6 | 3 | l2_distance | 0.933333 |
7 | 5 | l2_distance | 0.977778 |
8 | 7 | l2_distance | 0.977778 |
9 | 9 | l2_distance | 0.977778 |
至此k-NN算法介绍完了
机器学习未完待续 ……欢迎关注
微信公众号:王了个博
人要去的地方,除了远方,还有未来
欢迎关注我,一起学习,一起进步!关注我,在公众号回复 "机器学习" 可领取电子书