目录
k-近邻算法(knn)python实现实例
1.knn算法简介
(1)算法描述及定义
(2)距离的度量(只介绍三种,还有切比雪夫距离,余弦距离。)
①.曼哈顿距离
d
=
∣
(
x
1
−
x
2
)
∣
+
∣
(
y
1
−
y
2
)
∣
d = \left| (x_1 - x_2) \right| + \left| (y_1 - y_2) \right|
d=∣(x1−x2)∣+∣(y1−y2)∣
例如计算(0,0)以及(1,2)之间的距离为
∣
(
0
−
1
)
∣
+
∣
(
0
−
2
)
∣
\left| (0 - 1) \right| + \left| (0 - 2) \right|
∣(0−1)∣+∣(0−2)∣
②欧式距离
d
=
(
x
A
0
−
x
B
0
)
2
+
(
x
A
1
−
x
B
1
)
2
d = \sqrt{(xA_0 -xB_0) ^{2} +(xA_1 -xB_1) ^{2}}
d=(xA0−xB0)2+(xA1−xB1)2
例如计算(0,0)以及(1,2)之间的距离为
(
1
−
0
)
2
+
(
2
−
0
)
2
\sqrt{(1-0)^2 + (2-0)^2}
(1−0)2+(2−0)2
③闵可夫斯基距离
d
=
∑
i
=
1
n
∣
(
x
i
−
y
i
)
∣
2
p
d = \sqrt[p]{\sum_{i=1}^{n}{ \left| (x_i - y_i) \right|}^2}
d=pi=1∑n∣(xi−yi)∣2
(3)一般流程
①计算测试时数据与各个训练数据之间的距离
②按照距离的递增关系进行排序
③选取距离最小的k个点
④确定前k个点所在类别的出现频率
⑤返回前k个点中出现频率最高的类别作为测试数据的预测分类
2.用knn实现给出坐标所在区域代码实例
有一个数据集,它包含了一个人所在区域平面坐标x,y.经训练后,给定任意一个人在平面上的坐标,判断其所在的区域名字
(1)设定训练集
def Dataset():
group = np.array([[22,11],[33,15],[40,10],[11,25],[15,40],[20,50],[11,13],[21,23],[31,33],[41,39],[51,48],[5,7],[60,43],[58,39],[30,8],[10,30],[23,45],[2,20]])
labels = ['海中区','海上区','海上区','海中区','海下区','海下区','海中区','海中区','海中区','海中区','海中区','海中区','海上区','海上区','海上区','海下区','海下区','海下区']
return group,labels
(2)实现knn算法
def KNN(in_x,label_x,label_y,k):#(需要预测的数据,准备好的数据集,准备好的标签,k)
#计算欧氏距离并排序
label_x_size = label_x.shape[0]#shape用来返回行数
distances = (np.tile(in_x,(label_x_size,1)) - label_x)**2#训练集里的每个点的x,y与需要测试的数据的x,y分别对应相减再平方
euclidean_distance = distances.sum(axis=1)**0.5#用sum函数按行进行相加即 x^2 + y^2,再开根号得到欧氏距离
sort_distance = euclidean_distance.argsort()#距离排序返回索引
#选择距离最小的k个点
classdict = {}
for i in range(k):
voteI_label = label_y[sort_distance[i]]
classdict[voteI_label] = classdict.get(voteI_label,0) + 1
#排序找到最多的类别
sort_classdict = sorted(classdict.items(),key=operator.itemgetter(1),reverse=True)
return sort_classdict[0][0]
①计算距离
这里使用欧式距离进行计算,也可以用曼哈顿距离等来进行计算.
Ⅰ.将需要预测的数据通过tile的函数,变成和训练集中的行列数相同的维度,以便可以对应位置相加减.计算出来的distances即为训练集中每一项与待预测的数据x,y对应相减再平方
Ⅱ.通过numpy中的sum函数进行相加,将axis设为1为按行进行相加,即x² + y²,在开根号得到欧式距离.
Ⅲ.通过argsort函数来排序并返回下标,返回的下标对应按顺序排好的从小到大的数据集中的下标
②计算出待测数据所在的区域
Ⅰ.通过给定的k拿出最近的k个点的区域名称及所对应的数量,并放入字典中.然后排序,将数量最多的区域排到前面,返回排序后第一个数值的区域名
(3)绘图
def draw(group,labels,test_x):#画散点图
x1 = []
x2 = []
x3 = []
y1 = []
y2 = []
y3 = []
#获得每个区的所有点的坐标
for i in range(len(labels)):
if labels[i] == '海上区':
x1.append(group[i][0])
y1.append(group[i][1])
elif labels[i] == '海中区':
x2.append(group[i][0])
y2.append(group[i][1])
else:
x3.append(group[i][0])
y3.append(group[i][1])
#设置点的参数
plt.scatter(x1, y1, marker='^', c='b', label="海上区")#marker为点的形状,c为颜色
plt.scatter(x2, y2, marker='o', c='g', label="海中区")
plt.scatter(x3, y3, marker='x', c='r', label="海下区")
plt.scatter(test_x[0],test_x[1], marker='*', c='y', label="海下区")
plt.show()
(4)以下为测试代码
if __name__ == '__main__':
group,labels = Dataset()
test_x = [22,44]
print('输入数据所属区为:{}'.format(KNN(test_x,group,labels,3)))
draw(group,labels,test_x)
(5)测试结果
3.knn算法模型评估
(1)留出法
1.留出法基本概念
留出法是一种简单且广泛使用的模型评估方法。在这种方法中,可用的数据集被分为两部分:训练集和测试集。训练集用于构建模型,而测试集用于评估模型的性能。这种分割方法的核心思想是模拟模型在未知数据上的表现,从而为模型在实际应用中的性能提供一个可靠的估计。
2.步骤
1-数据集的准备
将数据集分割成两部分,一部分用来训练模型,一部分用来测试模型
def hold_out_set_split(group, labels, test_size = 0.3):#分割数据集用于留出法评估
#1.将数据打乱
index = np.arange(len(group))
np.random.shuffle(index)
group = np.array(group)[index]
labels = np.array(labels)[index]
#2.找到分界的下标
test_size_index = int(len(group) * test_size)
#3.分割
train_group = group[test_size_index:]
test_group = group[:test_size_index]
train_labels = labels[test_size_index:]
test_labels = labels[:test_size_index]
return train_group,test_group,train_labels,test_labels
2-模型评估(准确率)
将分割好的数据集训练后,用测试集来计算出准确率,通过遍历测试集中样本,用KNN处理后得到的标签与数据真实标签做对比,来计算出准确率.
def hold_out_evalute_model(train_set,train_labels,test_set,test_labels,k):#留出法评估
correct = 0
#测试集中每个实例都进行预测,如果预测结果与它自带的标签相同,计数器加一
for test_x,true_label in zip(test_set,test_labels):
label = KNN(test_x,train_set,train_labels,k)
if label == true_label:
correct+= 1
#计算出准确率
accuracy = correct/len(test_set)
return accuracy
3-评估结果
使用以下代码来进行评估
if __name__ == '__main__':
group,labels = Dataset()
k = 3
#留出法
train_set,test_set,train_labels,test_labels = hold_out_set_split(group, labels)#分割数据集
accuracy = hold_out_evalute_model(train_set, train_labels, test_set, test_labels, k)
print('留出法准确率为:',f'{accuracy:.2f}','当k =',k,'时')
(2)交叉验证法
1.交叉验证基本概念
交叉验证法是一种统计学方法,用于评估并比较机器学习模型的性能,确保模型具有良好的泛化能力。在交叉验证中,数据集被分割成多个子集,每个子集轮流作为验证集,其余作为训练集。这种方法可以减少模型评估的偏差和方差,提供更稳健的模型性能估计。
2.步骤
此处使用留一法验证(交叉验证的一种特殊形式,每次留下一个样本作为测试集,其余所有样本作为训练集。)
1-数据集准备
def cross_validate_set_split(group,labels):#交叉验证法数据分割
#数据集打乱
index = np.arange(len(group))
np.random.shuffle(index)
#循环给出每个样本索引
for i in range(len(group)):
#将对对应位置删除
train_set = np.delete(group,i,axis=0)#因group中是列表,需要让axis=0,表示按行删除
train_labels = np.delete(labels,i)
#将删除的位置元素取出
test_set = group[i:i+1]
test_labels = labels[i:i+1]
yield train_set,train_labels,test_set,test_labels#需返回多组值,使用yield返回数值,如使用return会使函数结束从而只得到一组值
2-模型评估(准确率)
def cross_balidate_evalute_model(group,labels,k):#交叉验证评估(留一交叉验证)
correct = 0
for train_set,train_labels,test_set,test_labels in cross_validate_set_split(group,labels):
label = KNN(test_set,train_set,train_labels,k)
if label == test_labels[0]:
correct += 1
accuracy = correct/len(group)
return accuracy
3-评估结果
使用以下代码进行测试
if __name__ == '__main__':
group,labels = Dataset()
k = 3
#交叉验证法
accuracy = cross_balidate_evalute_model(group,labels,k)
print('交叉验证(留一法)准确率为',f'{accuracy:.2f}','当k =',k,'时')
4.k-近邻算法总结
K最近邻算法是指在给定的训练数据(样本)集中,对于新的输入样本,通过计算新样本与所有训练样本的距离,找到与新样本最近的K个训练样本(K个邻居),对于分类问题,K个训练样本中属于某类标签的个数最多,就把新样本分到那个标签类别中.
优缺点
优点: 精度高、对异常值不敏感、无数据输入假定
缺点: 计算复杂度高、空间复杂度高