离得最近这个词具体实现有很多种,我使用的是欧式几何中的距离公式
二维中两点x(x1,y1),y(x2,y2)间距离公式为sqrt( (x1-x2)^2+(y1-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的1024维坐标表示,然后拿这个待识别的图像和库中的1000个实例进行距离计算,选出前k个距离最近的。比如50个,这50个里面出现次数最多的数字除以50就是结果数字的概率。比如50个里面数字8出现40次,那么待识别数字是8的可能性就是40/50 = 80%
个人理解:
只能识别单个数字,背景不能有干扰。如果想多数字识别或者背景有干扰需要针对具体情况考虑具体的图像转01的方法。
数字识别非常依赖库中的图像,库中的图像的样子严重影响图像的识别(因为我们是和库中的一一对比找出距离最近的前k个),所以数字的粗细,高低,胖瘦等待都是决定性因素,建库时一定全面考虑数字的可能样子
计算量比较大,待识别图像要和库中所有实例一一计算,如果使用32*32,就已经是1024维了。如果库中有1000个,那就是1024维向量之间的1000次计算,图像更清晰,库更丰富只会使计算量更大
对于其他可以直接计算距离的数值型问题,可以用欧式距离,也可以用其他能代表距离的计算公式,对于非数值型的问题需要进行合适的转换,转换方式很重要,我觉得首先信息不能丢失,其次要精确不能模糊,要实现图片转换前后是一对一的关系
参考资料:机器学习实战 [美] Peter Harrington 人民邮电出版社
python源码
import numpy
import os
from PIL import Image
import heapq
from collections import Counter
def pictureconvert(filename1,filename2,size=(32,32)):
#filename1待识别图像,filename2 待识别图像转换为01txt文件输出,size图像大小,默认32*32
image_file = Image.open(filename1)
image_file = image_file.resize(size)
width,height = image_file.size
f1 = open(filename1,'r')
f2 = open(filename2,'w')
for i in range(height):
for j in range(width):
pixel = image_file.getpixel((j,i))
pixel = pixel[0] + pixel[1] + pixel[2]
if(pixel == 0):
pixel = 0
elif(pixel != 765 and pixel != 0):
pixel = 1
# 0代表黑色(无图像),255代表白色(有图像)
# 0/255 = 0,255/255 = 1
f2.write(str(pixel))
if(j == width-1):
f2.write('\n')
f1.close()
f2.close()
def imgvector(filename):
#filename将待识别图像的01txt文件转换为向量
vector = numpy.zeros((1,1024),numpy.int)
with open(filename) as f:
for i in range(0,32):
linestr = f.readline()
for j in range(0,32):
vector[0,32*i+j] = int(linestr[j])
return vector
def compare(filename1,filename2):
#compare直接读取资源库识别
#filename1资源库目录,filename2 待识别图像01txt文档路径
trainingfilelist = os.listdir(filename1)
m = len(trainingfilelist)
labelvector = []
trainingmatrix = numpy.zeros((m, 1024), numpy.int8)
for i in range(0,m):
filenamestr = trainingfilelist[i]
filestr = filenamestr.split('.')[0]
classnumber = int(filestr.split('_')[0])
labelvector.append(classnumber)
trainingmatrix[i,:] = imgvector(filename1 + '/' + filenamestr)
textvector = imgvector(filename2)
resultdistance = numpy.zeros((1,m))
result = []
for i in range(0,m):
resultdistance[0,i] = numpy.vdot(textvector[0],trainingmatrix[i])
resultindices = heapq.nlargest(50,range(0,len(resultdistance[0])),resultdistance[0].take)
for i in resultindices:
result.append(labelvector[i])
number = Counter(result).most_common(1)
print('此数字是',number[0][0],'的可能性是','%.2f%%' % ((number[0][1]/len(result))*100))
def distinguish(filename1,filename2,filename3,size=(32,32)):
# filename1 png,jpg等格式原始图像路径,filename2 原始图像转换成01txt文件路径,filename3 资源库路径
pictureconvert(filename1,filename2,size)
compare(filename3,filename2)
url1 = "/Users/wang/Desktop/number.png"
url2 = "/Users/wang/Desktop/number.txt"
traininglibrary = "/Users/wang/Documents/trainingDigits"
distinguish(url1,url2,traininglibrary)
1.K-近邻(KNearestNeighbor,KNN)算法简介 :对于一个未知的样本,我们可以根据离它最近的k个样本的类别来判断它的类别。
以下图为例,对于一个未知样本绿色小圆,我们可以选取离它最近的3的样本,其中包含了2个红色三角形,1个蓝色正方形,那么我们可以判断绿色小圆属于红色三角形这一类。
我们也可以选取离它最近的5个样本,其中包含了3个蓝色正方形,2个红色三角形,那么我们可以判断绿色小圆属于蓝色正方形这一类。
3.API文档
下面我们来对KNN算法中的参数项做一个解释说明:
'n_neighbors':选取的参考对象的个数(邻居个数),默认值为5,也可以自己指定数值,但不是n_neighbors的值越大分类效果越好,最佳值需要我们做一个验证。
'weights': 距离的权重参数,默认uniform。
'uniform': 均匀的权重,所有的点在每一个类别中的权重是一样的。简单的说,就是每个点的重要性都是一样的。
'distance':权重与距离的倒数成正比,距离近的点重要性更高,对于结果的影响也更大。
'algorithm':运算方法,默认auto。
'auto':根绝模型fit的数据自动选择最合适的运算方法。
'ball_tree':树模型算法BallTree
'kd_tree':树模型算法KDTree
'brute':暴力算法
'leaf_size':叶子的尺寸,默认30。只有当algorithm = 'ball_tree' or 'kd_tree',这个参数需要设定。
'p':闵可斯基距离,当p = 1时,选择曼哈顿距离;当p = 2时,选择欧式距离。
n_jobs:使用计算机处理器数目,默认为1。当n=-1时,使用所有的处理器进行运算。
4.应用案例演示
下面以Sklearn库中自带的数据集--手写数字识别数据集为例,来测试下kNN算法。上一章,我们简单的介绍了机器学习的一般步骤:加载数据集 - 训练模型 - 结果预测 - 保存模型。这一章我们还是按照这个步骤来执行。
[手写数字识别数据集] https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_digits.html#sklearn.datasets.load_digits
5.模型的方法
每一种模型都有一些它独有的属性方法(模型的技能,能做些什么事),下面我们来了解下knn算法常用的的属性方法。
6.knn算法的优缺点
优点:
简单,效果还不错,适合多分类问题
缺点:
效率低(因为要计算预测样本距离每个样本点的距离,然后排序),效率会随着样本量的增加而降低。