简单数字识别(knn算法)

Knn算法,即K-nearest neighborhood,后面的nn表示最近邻,前面的K表示第一个K,即寻找前K个最近元素。

实现最近这个词有很多种方法。我使用欧几里得几何中的距离公式。

二维中两点x (x1,y1)与y (x2,y2)的距离公式为sqrt((x 1-x2)2+(y 1-y2)2)。

扩展到n维。

x(x1,x2,…,xn),y(y1,y2,…,yn)

sqrt [ ∑( x[i] - y[i] )^2 ] (i=1,2,…,n)

Knn算法是计算距离,也就是数字之间的运算,图像是png和jpg的格式,不是数字,不能直接参与运算,所以我们需要进行转换。

如图所示,有一个数字8。首先要确定的是,这一步我做的是最简单的变换,因为我假设背景和图形之间没有杂物,整个图形只有一个数字(0-9)。如果有其他情况,比如背景色不纯或者其他干扰图像,就需要重新设计变换函数。

接下来就是最简单的变换,把图片的白色部分(背景)改为0,有图像的部分改为1。转换后的大小要合适,太小会影响识别精度,太大会增加计算量。所以我用了书上的32*32,换算后的结果如图。

这样,图片就变成了一个可以计算的数字。

接下来,我们需要创建一个库,其中包含类似于上图的数字0-9的各种示例。因为我们要对要识别的图像进行比对,所以选取前k个最近的对象,比对对象就是我们的库。假设库中有从0到9的十个数字,每个数字有100个这样的由0和1表示的实例,那么我们就有一个***1000个实例。

最后一步是对比。利用开头提到的欧几里德几何距离计算公式,首先要把这个32*32的方阵转换成1*1024维的坐标表示。然后,计算该待识别图像与库中1000个样本之间的距离,选择最接近的前k个样本。比如50,结果数的概率除以50。例如,如果数字8在50次中出现40次,那么要识别的数字是8的概率是40/50 = 80%。

个人理解:

只能识别单个数字,不能干扰背景。如果要识别多个数字或者背景被干扰,需要根据具体情况考虑图像转换为01的具体方法。

数字识别非常依赖于库中的图像,而库中图像的外观严重影响图像识别(因为我们与库中的图像进行比较,找出最接近的top k),所以数字的粗细、高矮、胖瘦等是决定性因素,在建库时必须充分考虑数字可能的外观。

计算量比较大,要识别的图像要用库中所有的例子逐一计算。如果用32*32,那就已经是1024维度了。如果库中有1000,那就是1024维向量之间的1000次计算。图像更清晰,更丰富的库只会让计算更复杂。

对于其他可以直接计算距离的数值问题,可以使用欧氏距离,也可以使用其他可以表示距离的公式。对于非数值问题,需要适当的变换,变换方法很重要。我觉得信息首先不能丢,其次要准确,不能含糊。需要实现图像变换前后的一一对应关系。

参考资料:

实战中的机器学习[美]彼得·哈灵顿人民邮电出版社

Python源代码

进口数量

导入操作系统

从PIL进口图片

导入heapq

从集合导入计数器

def pictureconvert(文件名1,文件名2,大小=(32,32)):

#filename1待识别图像,filename2待识别图像转换成01txt文件输出,图像大小默认为32*32。

image_file = Image.open(文件名1)

image _ file = image _ file . resize(size)

width,height = image_file.size

f1 =打开(文件名1,' r ')

f2 =打开(文件名2,' w ')

对于范围内的I(高度):

对于范围内的j(宽度):

pixel = image_file.getpixel((j,I))

像素=像素[0] +像素[1] +像素[2]

if(pixel == 0):

像素= 0

elif(像素!= 765和像素!= 0):

像素= 1

# 0代表黑色(无图像),255代表白色(有图像)。

# 0/255 = 0,255/255 = 1

f2.write(str(pixel))

if(j == width-1):

f2.write('\n ')

f1.close()

f2.close()

定义图像向量(文件名):

#filename将要识别的图像的01 text文件转换为矢量。

vector = numpy.zeros((1,1024),numpy.int)

用open(文件名)作为f:

对于范围内的I(0.32):

linestr = f.readline()

对于范围(0,32)内的j:

vector[0,32*i+j] = int(linestr[j])

回归?矢量

def compare(文件名1,文件名2):

#compare直接读取存储库标识。

#filename1资源库目录,filename2待识别图像01文本文档路径。

trainingfilelist = os.listdir(文件名1)

m = len(培训文件列表)

labelvector = []

trainingmatrix = numpy.zeros((m,1024),numpy.int8)

对于范围(0,m)内的I:

filenamestr = training filelist[I]

filestr = filenamestr.split(' . ')[0]

class number = int(filestr . split(' _ ')[0])

labelvector.append(classnumber)

trainingmatrix[i,:= img vector(filename 1+'/'+filenamestr)

textvector = imgvector(文件名2)

result distance = numpy . zeros((1,m))

结果= []

对于范围(0,m)内的I:

结果距离[0,i] = numpy.vdot(textvector[0],trainingmatrix[i])

result indexes = heapq . nlargest(50,range(0,len(resultdistance[0])),resultdistance[0]。拍)

对于结果索引中的I:

result.append(labelvector[i])

数字=计数器(结果)。最常见(1)

Print('这个数是',数[0][0],'是'的概率,' % .2f%'%((数[0] [1]/len(结果))* 100))

def distinguish(文件名1,文件名2,文件名3,大小=(32,32)):

# filename1 png、jpg等原始图像路径,filename2原始图像转换成01 text文件路径,filename3资源库路径。

pictureconvert(文件名1,文件名2,大小)

比较(文件名3,文件名2)

URL 1 = "/Users/Wang/Desktop/number . png "

URL 2 = "/Users/Wang/Desktop/number . txt "

training library = "/Users/Wang/Documents/training digits "

区分(url1,url2,培训库)