用Python实现KNN算法(从原理到代码的实现)

用Python实现KNN算法(从原理到代码的实现)

环境

1.Pycharm
2.python3.6

声明:本栏的所有文章皆为本人学习时所做笔记而整理成篇,转载需授权且需注明文章来源,禁止商业用途,仅供学习交流.(欢迎大家提供宝贵的意见,共同进步)
原文链接https://editor.csdn.net/md?not_checkout=1&articleId=110291962

一、KNN算法的原理

1.1算法介绍

KNN的全称是K Nearest Neighbors,意思是K个最近的邻居,从这个名字我们就能看出一些KNN算法的蛛丝马迹了。K个最近邻居,毫无疑问,K的取值肯定是至关重要的。那么最近的邻居又是怎么回事呢?其实啊,KNN的原理就是当预测一个新的值x的时候,根据它距离最近的K个点是什么类别来判断x属于哪个类别。
听起来有点绕,还是看看图吧。
在这里插入图片描述
图中绿色的点就是我们要预测的那个点,假设K=3。那么KNN算法就会找到与它距离最近的三个点(这里用圆圈把它圈起来了),看看哪种类别多一些,比如这个例子中是蓝色三角形多一些,新来的绿色点就归类到蓝三角了。

在这里插入图片描述

但是,当K=5的时候,判定就变成不一样了。这次变成红圆多一些,所以新来的绿点被归类成红圆。从这个例子中,我们就能看得出K的取值是很重要的。
明白了大概原理后,我们就来说一说细节的东西吧,主要有两个,K值的选取和点距离的计算。

1.2距离d的计算和K的取值

在讲解算法实现前,我们需要回顾一点儿数学知识:两点之间的距离。
1》在二维平面内,若有两点,那么A到B的距离
D = 在这里插入图片描述

,若扩展到多维,则D =
在这里插入图片描述

通过公式,我们可以从侧面看出,若D的值越小,那么A和B的距离越近,则在坐标系中A和B位置越靠近,靠的越近正是因为A和B“相似”。

通过上面那两张张图我们知道K的取值比较重要,那么该如何确定K取多少值好呢?答案是通过交叉验证(将样本数据按照一定比例,拆分出训练用的数据和验证用的数据,比如6:4拆分出部分训练数据和验证数据),从选取一个较小的K值开始,不断增加K的值,然后计算验证集合的方差,最终找到一个比较合适的K值。

二、代码介绍

2.1数据集背景

众所周知,我们每个人的长相千差万别,但我们都有鼻子、耳朵、眼睛、脸蛋等,同时还有美丑之分,那么我们的美丑如何定义呢? 没有人敢说自己就是百分百的美女、美男,虽然如此,但我们确实又知道哪种五官令人舒服。如果说给我们每个人的长相进行分类打分的话,那我们应该依据什么来进行分类呢? 是否有具有公共特征使得被称为美女或美男的人都十分类似呢? 这是当然的,比如高鼻梁、大眼睛的人肯定比塌鼻子、小眼睛的人评分高,因此判断一个人是否到达美女、美男的要求,只需将他/她的五官与标准的精致五官进行比较便可知道,越相似说明五官越精致。那么人可以通过观察分辨,计算机如何分辨呢?
这里我们人工数据集是以鼻子、耳朵、眼睛、脸蛋这四个特征来判断人的美丑,范围从0-1,1为最高分值。
每个样本都是一个1*4的数据,这里我们一共创造了16 个样本。
对应的输出结果是五种:女神、淑女、可爱型、一般型、丑女。

2.2构造数据集和KNN模型

from numpy import *  # 导入科学计算包
import operator   # 导入运算符模块
import json

def createDataSet():
    data = array([[1, 1, 1, 1],
                  [0.5, 1, 1, 1],
                  [0.1, 0.1, 0.1, 0.1],
                  [0.5, 0.5, 0.5, 0.5],
                  [1, 0.8, 0.3, 1],
                  [0.6, 0.5, 0.7, 0.5],
                  [1, 1, 0.9, 0.5],
                  [1, 0.6, 0.5, 0.8],
                  [0.5, 0.5, 1, 1],
                  [0.9, 1, 1, 1],
                  [0.6, 0.6, 1, 0.1],
                  [1, 0.8, 0.5, 0.5],
                  [1, 0.1, 0.1, 1],
                  [1, 1, 0.7, 0.3],
                  [0.2, 0.3, 0.4, 0.5],
                  [0.5, 1, 0.6, 0.6]
                  ]);

    labels = ['女神',
              '淑女',
              '丑女',
              '一般型',
              '淑女',
              '一般型',
              '女神',
              '一般型',
              '淑女',
              '女神',
              '丑女',
              '可爱型',
              '可爱型',
              '淑女',
              '丑女',
              '可爱型'
              ]
    return data, labels

def classify(inX,data,labels,k):

    dataSetSize = data.shape[0]  # 计算共有多少条训练数据
    print(dataSetSize)
    print('复制输入向量 用于和样本中的每条数据进行计算 [矩阵的加减乘除]')
    print(tile(inX, (dataSetSize, 1)))

    print(dataSetSize)
    print('复制输入向量 用于和样本中的每条数据进行计算 [矩阵的加减乘除]')
    print(tile(inX, (dataSetSize, 1)))

    # 矩阵的减法 结果:每一项为输入向量和各个样本对应特征点的差值构成的新矩阵
    diffmat = tile(inX, (dataSetSize, 1)) - data
    print('\n相减后:')
    print(diffmat)

    sqDiffMat = diffmat ** 2  # 平方 矩阵中每一项都平方
    print('\n平方后:')
    print(sqDiffMat)
    sqDistances = sqDiffMat.sum(axis=1)  # axis=1 行向量相加 / axis=0 列向量相加
    print('\n各个特征点差值相加[即坐标差值相加]:')
    print(sqDistances)

    distances = sqDistances ** 0.5  # 开方
    print('\n距离:')
    print(distances)
    sortedDistIndexes = distances.argsort()  # 从小到大将距离向量排序并返回其索引值

    classCount = {}  # dict 保存对应标签出现的次数
    for i in range(k):
        voteLabel = labels[sortedDistIndexes[i]] #获得类别标签
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
    print('标签出现的次数:')
    print(json.dumps(classCount, ensure_ascii=False))
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)

    print('\n排序后:')
    print(json.dumps(sortedClassCount, ensure_ascii=False))
    # 如: print sortedClassCount ———— [('A', 2), ('B', 1)]

    return sortedClassCount[0][0]  # 返回次数出现次数最多的标签

2.3测试

import KNN
from numpy import*
data, labels = KNN.createDataSet()

print('测试模型分类样本数据,结果是否和样本中的分类一致')
input = [0.5, 1, 1, 1]
print('\n分类结果:'+KNN.classify(input, data, labels, 3))

2.4输出结果

当K取3时,

在这里插入图片描述
当k取5时,
在这里插入图片描述

可以看出当K取不同值时,结果不同。

三、代码中一下函数用法总结

3.1tile()函数

tile()函数将变量内容复制成输入矩阵同样大小的矩阵
实例:

>>>from numpy import *
>>>minVals = array([1,1],[2,2])
>>>minVals
array([[1, 1],
       [2, 2]])
>>> tile(minVals,4)
array([[1, 1, 1, 1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2, 2, 2, 2]])
>>> tile(minVals,(2,3))
array([[1, 1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2, 2],
       [1, 1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2, 2]]

在本文中就是把输入矩阵复制成(16,1)维

3.2argsort()

numpy.argsort(a, axis=-1, kind=’quicksort’, order=None)
返回的是数组值从小到大的索引值
参数:
a为要排序的数组
axis:按哪一维进行排序

3.3classCount[voteLabel] = classCount.get(voteLabel, 0) + 1

以文中代码为例:
classCount[voteIlabel] = classCount.get(voteLabel,0) + 1;
初始化classCount = {}时,此时输入classCount,输出为:

classCount = {} 
 当第一次遇到新的label时,将新的label添加到字典classCount,并初始化其对应数值为0 

然后+1,即该label已经出现过一次,此时输入classCount,输出为:

classCount = {voteLabel:1} 
 当第二次遇到同一个label时,classCount.get(voteLabel,0)返回对应的数值(此时括号内的0不起作用,因为已经初始化过了),然后+1,此时输入classCount,输出为: 

classCount = {voteLabel:2}
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值