k-近邻算法实现

文章目录

  • 系列文章目录
  • 前言
  • 一、K-近邻分类算法的概述
  • 二、k-近邻分类算法实现流程
    • 1.收集图片数据
    • 2.对收集图片进行标注生成数据集
    • 3.将标注好的图片实现labelme批量json_to_dataset方法
    • 4.转换后,会形成json文件夹,里面有四种内容
    • 5.将文件里内容单独拿出来
    • 6.将img文件里的图片转为文本文件
    • 7.读取标签
    • 8. 读取图像数据,即转换后的文本数据,将它转换为向量
    • 9.构建一个分类器
    • 10.最终结果
    • 11.运行结果
  • 总结


前言

K-近邻分类算法的数据集主要两种:文本文件,图片,文本文件的数据收集比较简单,这里我使用的是图像数据集,图片可以从网上下载你所感兴趣的,我所用的是猫咪和狗狗的图片各5张


提示:以下是本篇文章正文内容,下面案例可供参考

一、k-近邻分类的概述

机器学习的任务之一:分类

何谓分类:

        一土豪有20座矿山(只有金、银、煤、铁矿),已知有5座金矿、7座银矿、3座铁矿、4座煤矿,我们需要对这最后1座矿山进行分类,判断它是金矿、银矿、铁矿还是煤矿。

k-近邻算法(kNN)基本思想:

        测量不同特征值之间的距离方法进行分类。

工作原理:

        存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每一个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似的数据(最近邻)的分类标签。(一般来说,我们只选择样本数据中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数)最后,选择k个最相似数据中出现次数最多的分类 ,作为新数据的标签。



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

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

适用数据范围:数值型和标称型

二、k-近邻分类算法实现流程

1.收集图片数据

        可以在网上进行收集、下载

2.对收集图片进行标注生成数据集

        这里我们需要安装环境,首先需要安装Anacoda,可以根据电脑的系统版本在官网上安装对应的版本(注意:如果电脑之前安装过python,在安装Anacoda之前卸掉)Anacoda的安装可以在网上寻找教程,安装成功后可以下载安装labelme(详细安装即使用方法可以去B站上搜索Labelme标注流程_哔哩哔哩_bilibili)

安装命令:pip insatll labelme

3.将标注好的图片实现labelme批量json_to_dataset方法

        这里推荐的是一位大神的作品:(2条消息) 实现labelme批量json_to_dataset方法_蹦跶的小羊羔的博客-CSDN博客

注:如果不能批量转换,可进行一次一次的转换

4.转换后,会形成json文件夹,里面有四种内容

       1. img.png:即原图

       2. label.png:即标注后的图片,如下图

k-近邻算法实现_第1张图片

        3.label_name:即标注名

k-近邻算法实现_第2张图片

        4.label_viz.png:如下图

k-近邻算法实现_第3张图片

5.将文件里内容单独拿出来

        因为我们只需要用到上述文件中的2和3,所以将它们单独拿出,形成两个文件img(图片文件)和label(标签文件),每一张图片名需要与每一标签名一一对应

6.将img文件里的图片转为文本文件

        这里是为了方便之后图片矩阵转为向量,需要将图像文件转为文本文件,这里是一张图片一张图片的转为文本文件,而不是批量转为文本文件的


from PIL import Image

fr = Image.open('file_path/**.png')    #读取图像

fh = open('save_path/**.txt','a') #打开要写入的文本文档
width=fr.size[0]    #读取图像的宽
height=fr.size[1]    #读取图像的高
#读取图片中的每一个像素
for i in range(0,height):
    for j in range(0,width):
        cl=fr.getpixel((j,i))    #获取从第几行到第几列的像素
        clall=cl
        #如果是黑色,黑色的RGB值均为0
        if(clall==0):
            #在txt中写入1
            fh.write("1")
        else:
            #在txt中写入0
            fh.write("0")
    #读取完之后进行换行
    fh.write("\n")
#关闭文本文件
fh.close()

7.读取标签

        从label文件里批量读取文本标签

from os import listdir

labels = []
bacth_readlabels = listdir('E:/Learn_work/dataset/train/label')
for i in bacth_readlabels:    #用循环打开目录下的文件,得到标签
        
    domain = os.path.abspath('E:/Learn_work/dataset/train/label') #这里表示的是label所在文件的绝对路径
    batch_readlabe = os.path.join(domain,i)  #i其实是没有意义的,但必须要有一个相对路径,所用这里可以写任何参数
        
    with open (batch_readlabe,'rb') as f:
        label = f.readlines()[1]    #因为生成的标签名是有两个的,一个是我们标注的标签,一个是图像背景,所以读取的时候要读取第二个,我们标注的标签,如第4点里的文本图片
        labels.append(label)    #将读取的标签加入到一个列表中
print(labels)

8. 读取图像数据,即转换后的文本数据,将它转换为向量,这里我将它封装成了一个函数,前面的没有:

import numpy as np

def img2vector(filename):
    returnVect = np.zeros((1,250000)) #因为我的图片像素大小为500x500的,所以在转换为向量的时候需要变为250000
    
    fr = mpimg.imread(filename) #用matplotlib库读取文件中的图像
    plt.imshow(fr)
    
    for i in range(500):
        lineStr = fr.readline() # type: ignore #readline()函数,一行一行的读取
        for j in range(500):
            returnVect[0,500*i+j] = int(lineStr[j])
    return returnVect 

这里是修改后批量读取文本数据

from os import litdir

bacth_readtxts = listdir('E:/Learn_work/dataset/train/imgtxt')
m = len(bacth_readtxts)
returnVect = np.zeros([m,250000]) #因为我的图片像素大小为500x500的,所以在转换为向量的时候需要变为250000
for i in bacth_readtxts:    #用循环打开目录下的文件,得到图像数据
        
    domain = os.path.abspath('E:/Learn_work/dataset/train/imgtxt') #这里表示的是图像所在文件的绝对路径
    batch_readtxt = os.path.join(domain,i)  #i其实是没有意义的,但必须要有一个相对路径,所用这里可以写任何参数
        
    with open (batch_readtxt,'rb') as f:
        for t in range(500):
            fr = f.readline()
            for j in range(500):
                returnVect[0,500*t+j] = fr
              
print(returnVect)

9.构建一个分类器:

import numpy as np
import operator

#构建一个分类器
def Classify(inX,dataSet,labels,K):
    #Classify()函数有4个参数:inX是用于分类的输入向量,dataSet是输入的训练集样本,labels是标签向量,K是用于选择最近邻的数目
    dataSetSize = dataSet.shape[0]  #对于二维矩阵,shape[0]读取矩阵的第一维度,即多少行,shape[1]读取矩阵的第二维度,即多少列;对于三维还有shape[2],读取的是图片的通道数;shape[-1]读取的是最后一个维度
    diffMat = np.tile(inX,(dataSetSize,1))-dataSet  #tile()函数格式为:tile(A,reps),A代表数组;reps代表重复A多少次,单独的数字代表重复A数组几行,而[n,m]代表行数重复n次,列数重复m次
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1) #官方解释:轴用来定义超过一维数组的属性,比如二维数组有两个轴:第0轴沿着行垂直往下(即y轴),第1轴沿着列的方向水平延申(即x轴)
    distance = sqDistances**0.5
    #以上为对类别进行欧氏距离计算
    sortedDistIndices = distance.argsort()  #argsort()函数返回的是数组值从小到大的索引值,即进行排序,升序排序:argsort(x),降序排序:argsort(-x)
    ClassCounts = {}
    for i in range(K):
        voteIlabels = labels[sortedDistIndices[i]]
        #以上为对计算出的距离进行排序,并选择距离最小的K个点
        ClassCounts[voteIlabels] = ClassCounts.get(voteIlabels,0)   #用法1.字典用法,利用键值获取值,格式为get(key);2.利用字典统计列表中的元素出现的次数,格式为:get(key,初始化元素出现的次数),这里就是用法2
        #以上是统计在K个最小点标签中出现的次数
    sortedClassCounts = sorted(ClassCounts.iteritems(),key=operator.itemgetter(1),reverse = True)   # type: ignore #sort()是一个排序函数,iteritem()是列表的一个用法,即将键值对一起表现出来,itemgetter(1)是按照数组A的第二个元素进行排序,reverse=True表示从大到小进行排序
    #以上对统计出的次数进行排序
    return sortedClassCounts[0][0]

10.这里是没有包含1将图像转换为文本文件的最终代码,如果要运行此代码需要先将运行1,将图像转文本文件后在进行运行

from fileinput import filename
from unicodedata import name
import numpy as np
import operator
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from os import listdir
import os
import cv2

 
labels = []
bacth_readlabels = listdir('E:/Learn_work/dataset/train/label')
for i in bacth_readlabels:    #用循环打开目录下的文件,得到标签
        
    domain = os.path.abspath('E:/Learn_work/dataset/train/label') #这里表示的是label所在文件的绝对路径
    batch_readlabe = os.path.join(domain,i)  #i其实是没有意义的,但必须要有一个相对路径,所用这里可以写任何参数
        
    with open (batch_readlabe,'rb') as f:
        label = f.readlines()[1] 
        labels.append(label)          
print(labels)

bacth_readtxts = listdir('E:/Learn_work/dataset/train/imgtxt')
m = len(bacth_readtxts)
returnVect = np.zeros([m,250000]) #因为我的图片像素大小为500x500的,所以在转换为向量的时候需要变为250000
for i in bacth_readtxts:    #用循环打开目录下的文件,得到图像数据
        
    domain = os.path.abspath('E:/Learn_work/dataset/train/imgtxt') #这里表示的是图像所在文件的绝对路径
    batch_readtxt = os.path.join(domain,i)  #i其实是没有意义的,但必须要有一个相对路径,所用这里可以写任何参数
        
    with open (batch_readtxt,'rb') as f:
        for t in range(500):
            fr = f.readline()
            for j in range(500):
                returnVect[0,500*t+j] = fr
              
print(returnVect)

train = 0.8*returnVect  
test = 1-train


dataSetSize = train.shape[0]  #对于二维矩阵,shape[0]读取矩阵的第一维度,即多少行,shape[1]读取矩阵的第二维度,即多少列;对于三维还有shape[2],读取的是图片的通道数;shape[-1]读取的是最后一个维度
diffMat = test-train #tile()函数格式为:tile(A,reps),A代表数组;reps代表重复A多少次,单独的数字代表重复A数组几行,而[n,m]代表行数重复n次,列数重复m次
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1) #官方解释:轴用来定义超过一维数组的属性,比如二维数组有两个轴:第0轴沿着行垂直往下(即y轴),第1轴沿着列的方向水平延申(即x轴)
distance = sqDistances**0.5
    #以上为对类别进行欧氏距离计算

sortedDistIndices = distance.argsort()  #argsort()函数返回的是数组值从小到大的索引值,即进行排序,升序排序:argsort(x),降序排序:argsort(-x)
ClassCounts = {}
for i in range(6):
    voteIlabels = labels[sortedDistIndices[i]]
        #以上为对计算出的距离进行排序,并选择距离最小的K个点
    ClassCounts[voteIlabels] = ClassCounts.get(voteIlabels,0)+1   #用法1.字典用法,利用键值获取值,格式为get(key);2.利用字典统计列表中的元素出现的次数,格式为:get(key,初始化元素出现的次数),这里就是用法2
        #以上是统计在K个最小点标签中出现的次数

sortedClassCounts = sorted(ClassCounts.items(),key=operator.itemgetter(1),reverse = True)   #sort()是一个排序函数,items()是列表的一个用法,即将键值对一起表现出来,itemgetter(1)是按照数组A的第二个元素进行排序,reverse=True表示从大到小进行排序
    #以上对统计出的次数进行排序
print(sortedClassCounts[0][0])

11.运行结果:

当K=1时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

... [ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

当K = 2时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

...

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

当K = 3时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

...

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

当K = 4时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

...

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

当K = 5时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

...

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

当K = 6时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

...

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

当K = 7时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

...

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

当K = 8时

[b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'cat\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n', b'dog\r\n']

[[inf inf inf ... inf inf inf]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

...

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]

[ 0. 0. 0. ... 0. 0. 0.]]

b'cat\r\n'

总结

从几个K值,我们发现它得出的结论始终cat,导致这个结果的原因可能有:

1.样本数量太少,我这里只用了10个样本,5张猫咪,5张狗狗

2.猫咪和狗狗的样子形状相识,导致分类器进行分类的时候将所有的都分类为了猫咪

好了,本篇文章到这里就结束了,希望这此篇文章能够给各位码友带去帮助,大家一起进步,如果大家有什么建议可以在写在下方评论区。

你可能感兴趣的:(机器学习,近邻算法,算法)