【机器学习实战】K-近邻算法:手写识别系统

【机器学习实战】K-近邻算法:手写识别系统

0.收集数据

由于我们用的对比集是《机器学习实战》提供的文件,它提供的都是 32 32 32 * 32 32 32 . t x t .txt .txt 文件。因此我们需要把我们待识别的图片变成 32 32 32 * 32 32 32 . t x t .txt .txt 文件。

ps. t x t txt txt 文件是有颜色部分为1,空白部分为0。

0.1 转化为黑白图

为了更好的转换 t x t txt txt 文件,我们先将图片转化为黑白图。

首先要导入 P T L PTL PTL ,我们后面很多的函数都要依靠它。

from PIL import Image

我们的初始图片是 3.png 将其转化为黑白图,保存为 2.png

img = Image.open('3.png').convert('L')
img.save('2.png')

convert()是图像实例对象的一个方法,接受一个 mode 参数,用以指定一种色彩模式

参数意义
1 1 11位像素,黑白,每字节一个像素存储
L L L8位像素,黑白
P P P8位像素,使用调色板映射到任何其他模式
R G B RGB RGB3x8位像素,真彩色
R G B A RGBA RGBA4x8位像素,带透明度掩模的真彩色
C M Y K CMYK CMYK4x8位像素,分色
Y C b C r YCbCr YCbCr3x8位像素,彩色视频格式
I I I32位有符号整数像素
F F F32位浮点像素

转化前后的图片:

3. p n g 3.png 3.png 原始图片 2. p n g 2.png 2.png 黑白图片
img3img2

0.2 改变图片大小

由于给我们提供的是 32 32 32 * 32 32 32 . t x t .txt .txt 文件。但此时我们的 2.png 文件却不一定是 32 32 32 * 32 32 32 ,因此我们需要将 2.png 转化为 32 32 32 * 32 32 32 的图片并保存为 1.png

infile = '2.png'
outfile = '1.png'
im = Image.open(infile)
(x,y) = im.size #获取当前图片的大小
x_s = 32 
y_s = 32 #设置转化后的长和宽
out = im.resize((x_s,y_s),Image.ANTIALIAS)
out.save(outfile)

改变大小后的图片:

在这里插入图片描述

0.3 将图片转化为01文档

逐像素点判断每个点的颜色,我这里是将每个点的 R G B RGB RGB 值加起来,如果小于 450 450 450 则判断该位置应该为 1 1 1 ,否则为 0

ps.这个450是我自己测试出一个效果比较好的值,如果你有更好的取法可对其进行修改。

im=Image.open('1.png').convert('RGBA')
width=im.size[0]
height=im.size[1]
fh=open('1.txt','w')
for i in range(height):
    for j in range(width):
        #获取像素点颜色
        color=im.getpixel((j,i))
        colorsum=color[0]+color[1]+color[2]
        print(str(color[0])+","+str(color[1])+","+str(color[2]))
        if(colorsum < 450):
            fh.write('1')
        else:
            fh.write('0')
    fh.write('\n')
fh.close()

转化后的文档截图:

在这里插入图片描述

1.准备数据:将图像转化为测试向量

为了更好的计算,我们需要将 32 32 32 * 32 32 32 的图像矩阵转化为 1 1 1 * 1024 1024 1024 的向量。

该函数功能是循环读出文件前32行,并将每行的头32个字符值存储到NumPy数组中,最后返回数组。

def img2vector(filename):
    returnVect = zeros((1, 1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32 * i + j] = int(lineStr[j])
    return returnVect

2.编写测试算法:使用 k-近领算法识别手写数字

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

在计算两点之间的距离我们采用的是欧式距离公式: 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

故我们将所有的距离计算出来后,对数据按照从小到大的次序排序。然后,确定前 k k k 个距离最小元素所在的主要分类。

def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet #tile函数是输出数组,该数组是将inX复制dataSetSize行1列
    sqDiffMat = diffMat**2 #平方
    sqDistances = sqDiffMat.sum(axis = 1) # axis=1以后就是将一个矩阵的每一行向量相加
    distances = sqDistances**0.5 #开根号
    sortedDistIndicies = distances.argsort() #排序,返回的是数组值从小到大的索引值
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]] # 获取该数据的标签
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 
        # classCount.get(voteIlabel, 0)为查找classCount中voteIlabel的值,如果不存在赋值为1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True)
    return sortedClassCount

测试代码(我这里对其进行了修改,未按照《机器学习实战》书上的来):

def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('02KNN/trainingDigits') #获取对比数据集
    m = len(trainingFileList)
    trainingMat = zeros((m, 1024))
    for i in range(m):
        fileNameStr = trainingFileList[i] # 获取文件名
        fileStr = fileNameStr.split('.')[0] # 选取后缀前的部分
        classNumStr = int(fileStr.split('_')[0]) # 知道该数字是多少
        hwLabels.append(classNumStr)
        trainingMat[i, :] = img2vector('02KNN/trainingDigits/%s' % fileNameStr) # 矩阵转换成一维并保存
    vectorUnderTest = img2vector('1.txt')
    classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 10)
    for i in range(len(classifierResult)):
        print("该图片有%d%%的可能是%d" % (classifierResult[i][1] * 10 , classifierResult[i][0]))# 输出所有可能性

完整代码

from numpy import *
from os import listdir
import operator
from PIL import Image

def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis = 1)
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True)
    return sortedClassCount

def img2vector(filename):
    returnVect = zeros((1, 1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32 * i + j] = int(lineStr[j])
    return returnVect

def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('02KNN/trainingDigits') #获取对比数据集
    m = len(trainingFileList)
    trainingMat = zeros((m, 1024))
    for i in range(m):
        fileNameStr = trainingFileList[i] # 获取文件名
        fileStr = fileNameStr.split('.')[0] # 选取后缀前的部分
        classNumStr = int(fileStr.split('_')[0]) # 知道该数字是多少
        hwLabels.append(classNumStr)
        trainingMat[i, :] = img2vector('02KNN/trainingDigits/%s' % fileNameStr) # 矩阵转换成一维并保存
    vectorUnderTest = img2vector('1.txt')
    classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 10)
    for i in range(len(classifierResult)):
        print("该图片有%d%%的可能性是%d" % (classifierResult[i][1] * 10 , classifierResult[i][0]))

def prepare():
    img = Image.open('3.png').convert('L')
    img.save('2.png')

    infile = '2.png'
    outfile = '1.png'
    im = Image.open(infile)
    (x, y) = im.size
    x_s = 32
    y_s = 32
    out = im.resize((x_s, y_s), Image.ANTIALIAS)  # resize image with high-quality
    out.save(outfile)

    im = Image.open('1.png').convert('RGBA')
    width = im.size[0]
    height = im.size[1]
    fh = open('1.txt', 'w')
    for i in range(height):
        for j in range(width):
            color = im.getpixel((j, i))
            colorsum = color[0] + color[1] + color[2]
            if (colorsum < 450):
                fh.write('1')
            else:
                fh.write('0')
        fh.write('\n')
    fh.close()

if __name__ == "__main__":
    prepare()
    handwritingClassTest()

识别结果:

在这里插入图片描述

3.总结

对于这个程序,我并没有完全按照书上的来,我自己添加了将我手写的部分转化为测试集~~【没事找事】~~。

k-近邻算法是分类数据最简单的算法,虽然采用书中的测试集最后错误率只有 1.2% ,但我自己实际手写写了3个数字进行测试,只有一次识别成功的。。。。【自闭ing】,也不知道是不是因为它这个对比集的字迹和我的字迹差别太大还是什么,总而言之这个算法效率不行,以后应该也不会经常用。基础算法原理也理解了,这一章就这么过吧~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值