简单数字识别(knn算法)
实现最近这个词有很多种方法。我使用欧几里得几何中的距离公式。
二维中两点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,培训库)