python实现k近邻(knn)算法在minst数据集上验证

这篇博客是用python实现k近邻算法,没有调用已有的knn库中的函数,根据伪代码实现算法的每个部分,初学python,可能代码部分写的不够规范。但是最终在数据集上测试效果还是不错的,准确率可以达到98.5%以上。

1.k近邻(knn)算法

K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。举例来说,有两类不同的样本数据,分别用蓝色的小正方形和红色的小三角形表示,而图正中间的那个绿色的圆所标示的数据则是待分类的数据。也就是说,现在, 我们不知道中间那个绿色的数据是从属于哪一类(蓝色小正方形or红色小三角形),下面,我们就要解决这个问题:给这个绿色的圆分类。
在这里插入图片描述
我们不是要判别上图中那个绿色的圆是属于哪一类数据么,好说,从它的邻居下手。但一次性看多少个邻居呢?从上图中,你还能看到:
如果K=3,那么离绿色点最近的有2个红色三角形和1个蓝色的正方 形,这3个点投票,于是绿色的这个待分类点属于红色的三角形。
如果K=5,那么离绿色点最近的有2个红色三角形和3个蓝色的正方 形,这5个点投票,于是绿色的这个待分类点属于蓝色的正方形。
那么给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居), 这K个实例的多数属于某个类,就把该输入实例分类到这个类中,这样就可以实现数分类功能了。下面就在minst数据集进行验证。

2.inst数据集

MNIST数据集是一个手写体数据集,简单说就是一堆这样东西
在这里插入图片描述
每张图片大小为28x28,每个像素点用一个 灰度值表示,这里是将28x28的像素展开为一个一维的行向量(每行 784个值),标签为0-9。加载数据集的方式为:

# 导入数据集
from sklearn.datasets import fetch_mldata 
mnist = fetch_mldata('MNIST original') 
X = mnist.data #行向量
y = mnist.target #标签

这种方式加载的是完整的数据集,即共7000个样本,包括60000张训练样 本和10000张测试样本。考虑到电脑实际,也没开gpu加速,只能加载缩小的数据集。加载数据集的方式为:

from sklearn.datasets import fetch_mldata
#mnist = fetch_mldata('MNIST original')
mnist = load_digits()
X = mnist.data
lable = mnist.target # 标签

这是缩小的数据集图片像素和总样本数量都减少了,缩减很大的计算量,但是在测试的时候感觉效果还是很不错的。

3.伪代码

  1. 计算已知类别数据集中的点与当前点之间的距离;
  2. 按照距离递增次序排序;
  3. 选取与当前点距离最小的k个点;
  4. 确定呢前k个点所在类别的出现概率;
  5. 返回前k个点出现频率最高的类别作为当前点的预测分类 。

4.代码部分

4.1 模块1

将数据集预处理的部分写成 ex1.py,即分出训练集(其实也没有训练的过程)和测试集。

from sklearn.datasets import load_digits
import numpy as np
from sklearn.datasets import fetch_mldata
#mnist = fetch_mldata('MNIST original')
mnist = load_digits()
x = mnist.data
lable = mnist.target # 标签

#数据集预处理
def random(lable,fenlei):
    b =set()#产生的测试集合不重复
    while(len(b)<fenlei):
        b.add(np.random.randint(0,len(lable)))
    return b

def shujuji(x,b,lable):
	#图片划分
    train_x = [x[i] for i in range(len(lable)) if (i not in b)]
    ceshi_x = [x[i] for i in range(len(lable)) if (i in b)]
    #标签划分
    train_lable = [lable[i] for i in range(len(lable)) if (i not in b)]
    ceshi_lable = [lable[i] for i in range(len(lable)) if (i in b)]
    return train_x,train_lable,ceshi_x,ceshi_lable

4.2 模块2

将测试集的每一个向量与训练集的每一个求欧式距离,保存在列表距离列表中,分别写了原始数据的距离和归一化之后距离的函数(其实数据尺度相近,归一化没什么效果,但是为了验证归一化算法,写了试一下),模块命名为 ex2.py

import numpy as np
from sklearn.datasets import load_digits
from sklearn.datasets import fetch_mldata
#mnist = fetch_mldata('MNIST original')
mnist = load_digits()
x = mnist.data
lable = mnist.target # 标签

#所有距离
def juli(x,x0,b):
    ds = [[]for i in range(len(b))]
    i = 0
    j = 0
    while i < len(b):
        while j < len(x):
            M = np.linalg.norm(x[j] - x0[i])
            #M = round(M,1)
            j = j + 1
            ds[i].append(M)
        j = 0
        i = i + 1
    return ds
#print(ds)
#归一化处理
def youhuajuli(x,x0,b):
	#用的最简单的归一化算法
    minx = [[]for i in range(len(x))]
    maxx = [[]for i in range(len(x))]
    maxx0 = [[]for i in range(len(b))]
    minx0 = [[]for i in range(len(b))]
    m = 0
    n = 0
    while m < len(x):
        minx[m] = min(x[m])
        maxx[m] = max(x[m])
        x[m] = (x[m] - minx[m])/(maxx[m] - minx[m])
        m = m + 1
    while n < len(b):
        maxx0[n]= max(x0[n])
        minx0[n] = min(x0[n])
        x0[n] = (x0[n] - minx0[n])/(maxx0[n] - minx0[n])
        n = n + 1
#print(x,y,x0,y0)
    youhuads = [[]for i in range(len(b))]
    i = 0
    j = 0
    while i < len(b):
        while j < len(x):
            M = np.linalg.norm(x[j] - x0[i])
            j = j + 1
            youhuads[i].append(M)
        j = 0
        i = i + 1
    return youhuads
    #一定要注意此处代码的缩进

4.3模块3

这是主要的部分

import ex1
import ex2
import numpy as np
from sklearn.datasets import load_digits
from sklearn.metrics import accuracy_score
from sklearn.datasets import fetch_mldata
#mnist = fetch_mldata('MNIST original')
mnist = load_digits()
X = mnist.data
lable = mnist.target # 标签
#产生测试集个数,默认500
fenlei = 500
#fenlei = 300
#fenlei = 100
print('请输入k值')
k = input('>')
k = int(k)
#产生500个随机索引,划分数据集
def suijib(fenlei,lable):
    b =set()
    while(len(b)<fenlei):
        b.add(np.random.randint(0,len(lable)))
    return b
#b = suijib(fenlei,lable)
#找出K个最小值,每次找到一个,用999999替换相应元素
def mintemp(k,b,ds):
    k = k
    b = b
    ds = ds
    m = 0
    n = 0
    Inf = 999999
    temp = [[]for i in range(len(b))]
    while m < len(b):
        while n < k:
            juli = ds[m]
            temp[m].append(juli.index(min(juli)))
            juli[juli.index(min(juli))] = Inf
            n = n + 1
            #print(juli)
        n = 0
        m = m + 1
    return temp
#print(temp)#调试用
#确定K个对应元素距离最小的标签
def minlable(temp,train_lable,b):
    temp = temp
    train_lable = train_lable
    b = b
    i = 0
    lable_train = [[]for i in range(len(b))]
    while i < len(b):
        lable_train[i] = [train_lable[la] for la in range(len(train_lable)) if (la in temp[i])]
        i = i + 1
    return lable_train
#print(lable_train)
#找出K个标签的相同元素最多的作为计算标签
def max_list(lt):
    temp = 0
    for i in lt:
        if lt.count(i) > temp:
            max_str = i
            temp = lt.count(i)
    return max_str
#调用函数实现k近邻
n = 1
accuracy1 = 0
accuracy2 = 0
while n < 21:
    b = ex1.random(lable,fenlei)
    x = X
    train_x,train_lable,ceshi_x,ceshi_lable=ex1.shujuji(x,b,lable)
    x = train_x
    x0 = ceshi_x
    ds1 = ex2.juli(x,x0,b)
    temp1 = mintemp(k,b,ds1)
    #print(temp1)
    lable_train1 = minlable(temp1,train_lable,b)
    lable_test1 = []
    j = 0
    while j < len(b):
        #print(lable_train[j])
        lable_test1.append(max_list(lable_train1[j]))
        j = j + 1

    youhuads2 = ex2.youhuajuli(x,x0,b)
    temp2 = mintemp(k,b,youhuads2)
    #print(temp2)
    lable_train2 = minlable(temp2,train_lable,b)
    #lable_train = np.array(lable_train)
    lable_test2 = []
    j = 0
    while j < len(b):
        #print(lable_train[j])
        lable_test2.append(max_list(lable_train2[j]))
        j = j + 1
    #print("正确标签:")
    #print(ceshi_lable)
    #print("归一化后K近邻计算标签:")
    #print(lable_test2)
    lable_test2 = np.array(lable_test2)
    ceshi_lable = np.array(ceshi_lable)
    a2 = sum(lable_test2==ceshi_lable)
    print("次数")
    print(n)
    print("归一化后""第"+ str(n) +"次准确率")
    print(a2 / float(len(ceshi_lable)))
    accuracy2 = accuracy2 + a2 / float(len(ceshi_lable))
    #print("正确标签:")
    #print(lable_test1)
    lable_test1 = np.array(lable_test1)
    ceshi_lable = np.array(ceshi_lable)
    a1 = sum(lable_test1==ceshi_lable)
    print("归一化前""第"+ str(n) +"次准确率")
    print(a1 / float(len(ceshi_lable)))
    accuracy1 = accuracy1 + a1 / float(len(ceshi_lable))
    print("################################################")
    n = n + 1
accuracy1 = accuracy1 / 20
accuracy2 = accuracy2 / 20
print("归一化前20次平均准确率:")
print(accuracy1)
print("归一化后20次平均准确率:")
print(accuracy2)

代码写的挺多的,有很多地方还是可以简化的,为了方便调试,留了很多输出的部分。
下面就是验证一下
直接运行最后一个模块,分别验证了一下参数
测试集100 k=3
测试集100 k=5
测试集100 k=10
测试集300 k=3
测试集300 k=5
测试集300 k=10
测试集500 k=3
测试集500 k=5
测试集500 k=10
结果如图(部分图)
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
从结果可以看出20次的平均准确率都在98%以上,效果是很不错的。
参考
百度百科:https://baike.baidu.com/item/k近邻算法/9512781?fr=aladdin
详解 MNIST 数据集:
https://blog.csdn.net/simple_the_best/article/details/75267863

  • 2
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值