knn算法实现解决目标所在区域问题及模型评估

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=(x1x2)+(y1y2)
例如计算(0,0)以及(1,2)之间的距离为
∣ ( 0 − 1 ) ∣ + ∣ ( 0 − 2 ) ∣ \left| (0 - 1) \right| + \left| (0 - 2) \right| (01)+(02)
②欧式距离
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=(xA0xB0)2+(xA1xB1)2
例如计算(0,0)以及(1,2)之间的距离为
( 1 − 0 ) 2 + ( 2 − 0 ) 2 \sqrt{(1-0)^2 + (2-0)^2} (10)2+(20)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=1n(xiyi)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个训练样本中属于某类标签的个数最多,就把新样本分到那个标签类别中.

优缺点

优点: 精度高、对异常值不敏感、无数据输入假定

缺点: 计算复杂度高、空间复杂度高

  • 34
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值