导航:首页 > 源码编译 > knn算法如何减少计算次数

knn算法如何减少计算次数

发布时间:2024-12-21 00:29:47

‘壹’ 简单数字识别(knn算法)

knn算法,即k-NearestNeighbor,后面的nn意思是最近邻的意思,前面的k是前k个的意思,就是找到前k个离得最近的元素

离得最近这个词具体实现有很多种,我使用的是欧式几何中的距离公式

二维中两点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)

‘贰’ K-近邻算法(K-NN)

给定一个训练数据集,对于新的输入实例, 根据这个实例最近的 k 个实例所属的类别来决定其属于哪一类 。所以相对于其它机器学习模型和算法,k 近邻总体上而言是一种非常简单的方法。

找到与该实例最近邻的实例,这里就涉及到如何找到,即在特征向量空间中,我们要采取 何种方式来对距离进行度量

距离的度量用在 k 近邻中我们也可以称之为 相似性度量 ,即特征空间中两个实例点相似程度的反映。在机器学习中,常用的距离度量方式包括欧式距离、曼哈顿距离、余弦距离以及切比雪夫距离等。 在 k 近邻算法中常用的距离度量方式是欧式距离,也即 L2 距离, L2 距离计算公式如下:

一般而言,k 值的大小对分类结果有着重大的影响。 当选择的 k 值较小的情况下,就相当于用较小的邻域中的训练实例进行预测,只有当与输入实例较近的训练实例才会对预测结果起作用。但与此同时预测结果会对实例点非常敏感,分类器抗噪能力较差,因而容易产生过拟合 ,所以一般而言,k 值的选择不宜过小。但如果选择较大的 k 值,就相当于在用较大邻域中的闷郑握训练实例进行预测,但相应的分类误差也会增大,模型整体变得简单,会产生一定程度的欠拟合。所以一般而言,我们需要 采用交叉验证的方式来选择合适的 k 值

k 个实例的多数属于哪丛裤个类,明显是多数表决的归类规则。当然还可能使用其他规则,所以第三个关键就是 分类决策规则。

回归:k个实例该属性值的平均值

它是一个二叉树的数据结构,方便存储 K 维空间的数据

KNN 的计算过程是大量计算样本点之间的距离。为了减少计算距离次数,提升 KNN 的搜索效率,人们提出了 KD 树(K-Dimensional 的缩写)。KD 树是对数据点在 K 维空间中划分的一种数据结构。在 KD 树的构造中,每个节点都是 k 维数值点的二叉树。蚂庆既然是二叉树,就可以采用二叉树的增删改查操作,这样就大大提升了搜索效率。

如果是做分类,你需要引用:from sklearn.neihbors import KNeighborsClassifier
如果是回归, 需要引用:from sklearn.neighbors import KNeighborsRegressor

sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights='uniform', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=None, **kwargs)

‘叁’ KNN算法常见问题总结

给定测试实例,基于某种距离度量找出训练集中与其最靠近的k个实例点,然后基于这k个最近邻的信息来进行预测。

通常,在分类任务中可使用“投票法”,即选择这k个实例中出现最多的标记类别作为预测结果;在回归任务中可使用“平均法”,即将这k个实例的实值输出标记的平均值作为预测结果;还可基于距离远近进行加权平均或加权投票,距离越近的实例权重越大。

k近邻法不具有显式的学习过程,事实上,它是懒惰学习(lazy learning)的着名代表,此类学习技术在训练阶段仅仅是把样本保存起来,训练时间开销为零,待收到测试样本后再进行处理。

KNN一般采用欧氏距离,也可采用其他距离度量,一般的Lp距离:

KNN中的K值选取对K近邻算法的结果会产生重大影响。如果选择较小的K值,就相当于用较小的领域中的训练实例进行预测,“学习”近似误差(近似误差:可以理解为对现有训练集的训练误差)会减小,只有与输入实例较近或相似的训练实例才会对预测结果起作用,与此同时带来的问题是“学习”的估计误差会增大,换句话说,K值的减小就意味着整体模型变得复杂,容易发生过拟合;

如果选择较大的K值,就相当于用较大领域中的训练实例进行预测,其优点是可以减少学习的估计误差,但缺点是学习的近似误差会增大。这时候,与输入实例较远(不相似的)训练实例也会对预测器作用,使预测发生错误,且K值的增大就意味着整体的模型变得简单。

在实际应用中,K值一般取一个比较小的数值,例如采用交叉验证法来选择最优的K值。经验规则:k一般低于训练样本数的平方根

1、计算测试对象到训练集中每个对象的距离

2、按照距离的远近排序

3、选取与当前测试对象最近的k的训练对象,作为该测试对象的邻居

4、统计这k个邻居的类别频率

5、k个邻居里频率最高的类别,即为测试对象的类别

输入X可以采用BallTree或KDTree两种数据结构,优化计算效率,可以在实例化KNeighborsClassifier的时候指定。

KDTree

基本思想是,若A点距离B点非常远,B点距离C点非常近, 可知A点与C点很遥远,不需要明确计算它们的距离。 通过这样的方式,近邻搜索的计算成本可以降低为O[DNlog(N)]或更低。 这是对于暴力搜索在大样本数N中表现的显着改善。KD 树的构造非常快,对于低维度 (D<20) 近邻搜索也非常快, 当D增长到很大时,效率变低: 这就是所谓的 “维度灾难” 的一种体现。

KD 树是一个二叉树结构,它沿着数据轴递归地划分参数空间,将其划分为嵌入数据点的嵌套的各向异性区域。 KD 树的构造非常快:因为只需沿数据轴执行分区, 无需计算D-dimensional 距离。 一旦构建完成, 查询点的最近邻距离计算复杂度仅为O[log(N)]。 虽然 KD 树的方法对于低维度 (D<20) 近邻搜索非常快, 当D增长到很大时, 效率变低。

KD树的特性适合使用欧氏距离。

BallTree

BallTree解决了KDTree在高维上效率低下的问题,这种方法构建的树要比 KD 树消耗更多的时间,但是这种数据结构对于高结构化的数据是非常有效的, 即使在高维度上也是一样。

KD树是依次对K维坐标轴,以中值切分构造的树;ball tree 是以质心C和半径r分割样本空间,每一个节点是一个超球体。换句简单的话来说,对于目标空间(q, r),所有被该超球体截断的子超球体内的所有子空间都将被遍历搜索。

BallTree通过使用三角不等式减少近邻搜索的候选点数:|x+y|<=|x|+|y|通过这种设置, 测试点和质心之间的单一距离计算足以确定距节点内所有点的距离的下限和上限. 由于 ball 树节点的球形几何, 它在高维度上的性能超出 KD-tree, 尽管实际的性能高度依赖于训练数据的结构。

BallTree适用于更一般的距离。

1、优点

非常简单的分类算法没有之一,人性化,易于理解,易于实现

适合处理多分类问题,比如推荐用户

可用于数值型数据和离散型数据,既可以用来做分类也可以用来做回归

对异常值不敏感

2、缺点

属于懒惰算法,时间复杂度较高,因为需要计算未知样本到所有已知样本的距离

样本平衡度依赖高,当出现极端情况样本不平衡时,分类绝对会出现偏差,可以调整样本权值改善

可解释性差,无法给出类似决策树那样的规则

向量的维度越高,欧式距离的区分能力就越弱

样本空间太大不适合,因为计算量太大,预测缓慢

文本分类

用户推荐

回归问题

1)所有的观测实例中随机抽取出k个观测点,作为聚类中心点,然后遍历其余的观测点找到距离各自最近的聚类中心点,将其加入到该聚类中。这样,我们就有了一个初始的聚类结果,这是一次迭代的过程。

2)我们每个聚类中心都至少有一个观测实例,这样,我们可以求出每个聚类的中心点(means),作为新的聚类中心,然后再遍历所有的观测点,找到距离其最近的中心点,加入到该聚类中。然后继续运行2)。

3)如此往复2),直到前后两次迭代得到的聚类中心点一模一样。

本算法的时间复杂度:O(tkmn),其中,t为迭代次数,k为簇的数目,m为记录数,n为维数;

空间复杂度:O((m+k)n),其中,k为簇的数目,m为记录数,n为维数。

适用范围:

K-menas算法试图找到使平凡误差准则函数最小的簇。当潜在的簇形状是凸面的,簇与簇之间区别较明显,且簇大小相近时,其聚类结果较理想。前面提到,该算法时间复杂度为O(tkmn),与样本数量线性相关,所以,对于处理大数据集合,该算法非常高效,且伸缩性较好。但该算法除了要事先确定簇数K和对初始聚类中心敏感外,经常以局部最优结束,同时对“噪声”和孤立点敏感,并且该方法不适于发现非凸面形状的簇或大小差别很大的簇。

1)首先,算法只能找到局部最优的聚类,而不是全局最优的聚类。而且算法的结果非常依赖于初始随机选择的聚类中心的位置。我们通过多次运行算法,使用不同的随机生成的聚类中心点运行算法,然后对各自结果C通过evaluate(C)函数进行评估,选择多次结果中evaluate(C)值最小的那一个。k-means++算法选择初始seeds的基本思想就是:初始的聚类中心之间的相互距离要尽可能的远

2)关于初始k值选择的问题。首先的想法是,从一个起始值开始,到一个最大值,每一个值运行k-means算法聚类,通过一个评价函数计算出最好的一次聚类结果,这个k就是最优的k。我们首先想到了上面用到的evaluate(C)。然而,k越大,聚类中心越多,显然每个观测点距离其中心的距离的平方和会越小,这在实践中也得到了验证。第四节中的实验结果分析中将详细讨论这个问题。

3)关于性能问题。原始的算法,每一次迭代都要计算每一个观测点与所有聚类中心的距离。有没有方法能够提高效率呢?是有的,可以使用k-d tree或者ball tree这种数据结构来提高算法的效率。特定条件下,对于一定区域内的观测点,无需遍历每一个观测点,就可以把这个区域内所有的点放到距离最近的一个聚类中去。这将在第三节中详细地介绍。

相似点:都包含这样的过程,给定一个点,在数据集中找离它最近的点。即二者都用到了NN(Nears Neighbor)算法,一般用KD树来实现NN。

k-d tree 与 ball tree

1)k-d tree[5]

把n维特征的观测实例放到n维空间中,k-d tree每次通过某种算法选择一个特征(坐标轴),以它的某一个值作为分界做超平面,把当前所有观测点分为两部分,然后对每一个部分使用同样的方法,直到达到某个条件为止。

上面的表述中,有几个地方下面将会详细说明:(1)选择特征(坐标轴)的方法 (2)以该特征的哪一个为界 (3)达到什么条件算法结束。

(1)选择特征的方法

计算当前观测点集合中每个特征的方差,选择方差最大的一个特征,然后画一个垂直于这个特征的超平面将所有观测点分为两个集合。

(2)以该特征的哪一个值为界 即垂直选择坐标轴的超平面的具体位置。

第一种是以各个点的方差的中值(median)为界。这样会使建好的树非常地平衡,会均匀地分开一个集合。这样做的问题是,如果点的分布非常不好地偏斜的,选择中值会造成连续相同方向的分割,形成细长的超矩形(hyperrectangles)。

替代的方法是计算这些点该坐标轴的平均值,选择距离这个平均值最近的点作为超平面与这个坐标轴的交点。这样这个树不会完美地平衡,但区域会倾向于正方地被划分,连续的分割更有可能在不同方向上发生。

(3)达到什么条件算法结束

实际中,不用指导叶子结点只包含两个点时才结束算法。你可以设定一个预先设定的最小值,当这个最小值达到时结束算法。

图6中,星号标注的是目标点,我们在k-d tree中找到这个点所处的区域后,依次计算此区域包含的点的距离,找出最近的一个点(黑色点),如果在其他region中还包含更近的点则一定在以这两个点为半径的圆中。假设这个圆如图中所示包含其他区域。先看这个区域兄弟结点对应区域,与圆不重叠;再看其双亲结点的兄弟结点对应区域。从它的子结点对应区域中寻找(图中确实与这个双亲结点的兄弟结点的子结点对应区域重叠了)。在其中找是否有更近的结点。

k-d tree的优势是可以递增更新。新的观测点可以不断地加入进来。找到新观测点应该在的区域,如果它是空的,就把它添加进去,否则,沿着最长的边分割这个区域来保持接近正方形的性质。这样会破坏树的平衡性,同时让区域不利于找最近邻。我们可以当树的深度到达一定值时重建这棵树。

然而,k-d tree也有问题。矩形并不是用到这里最好的方式。偏斜的数据集会造成我们想要保持树的平衡与保持区域的正方形特性的冲突。另外,矩形甚至是正方形并不是用在这里最完美的形状,由于它的角。如果图6中的圆再大一些,即黑点距离目标点点再远一些,圆就会与左上角的矩形相交,需要多检查一个区域的点,而且那个区域是当前区域双亲结点的兄弟结点的子结点。

为了解决上面的问题,我们引入了ball tree。

2)ball tree[4]

解决上面问题的方案就是使用超球面而不是超矩形划分区域。使用球面可能会造成球面间的重叠,但却没有关系。ball tree就是一个k维超球面来覆盖这些观测点,把它们放到树里面。图7(a)显示了一个2维平面包含16个观测实例的图,图7(b)是其对应的ball tree,其中结点中的数字表示包含的观测点数。

不同层次的圆被用不同的风格画出。树中的每个结点对应一个圆,结点的数字表示该区域保含的观测点数,但不一定就是图中该区域囊括的点数,因为有重叠的情况,并且一个观测点只能属于一个区域。实际的ball tree的结点保存圆心和半径。叶子结点保存它包含的观测点。

使用ball tree时,先自上而下找到包含target的叶子结点,从此结点中找到离它最近的观测点。这个距离就是最近邻的距离的上界。检查它的兄弟结点中是否包含比这个上界更小的观测点。方法是:如果目标点距离兄弟结点的圆心的距离大于这个圆的圆心加上前面的上界的值,则这个兄弟结点不可能包含所要的观测点。(如图8)否则,检查这个兄弟结点是否包含符合条件的观测点。

那么,ball tree的分割算法是什么呢?

选择一个距离当前圆心最远的观测点i1,和距离i1最远的观测点 i2,将圆中所有离这两个点最近的观测点都赋给这两个簇的中心,然后计算每一个簇的中心点和包含所有其所属观测点的最小半径。对包含n个观测点的超圆进行分割,只需要线性的时间。

与k-d tree一样,如果结点包含的观测点到达了预先设定的最小值,这个顶点就可以不再分割了。

‘肆’ knn算法优缺点

KNN算法的优缺点如下:

优点:

1. 简单直观:KNN算法是一种基于实例的学习算法,它不需要建立复杂的数学模型,而是直接利用训练数据集进行预测。这种方法的逻辑非常直观,易于理解和实现。

2. 无需参数估计:KNN算法在训练阶段基本上不需要进行参数估计和模型训练,这避免了因参数设置不当而导致的模型性能下降。它主要依赖于数据集本身,使得算法更加灵活。

3. 适用于多分类问题:KNN算法可以很方便地扩展到多分类问题中,只需在预测时考虑K个最近邻的类别即可。

4. 对异常值不敏感:由于KNN算法是基于局部信息进行预测的,因此它对数据集中的异常值相对不敏感。这意味着即使数据集中存在少量噪声或异常点,算法的性能也不会受到太大影响。

缺点:

1. 计算量大:KNN算法在预测阶段需要计算待预测样本与所有训练样本之间的距离,这导致在大数据集上运行时计算成本非常高。随着数据量的增加,算法的效率会显着下降。

2. K值选择困难:KNN算法中的K值是一个关键参数,它直接影响到算法的预测性能。然而,选择合适的K值并不容易,通常需要通过交叉验证等方法来确定。此外,不同的K值可能导致完全不同的预测结果,使得算法的稳定性受到质疑。

3. 样本不平衡问题:当训练数据集中各类别的样本数量不平衡时,KNN算法的预测性能可能会受到影响。因为算法是基于最近邻的类别进行预测的,如果某一类别的样本数量过多,那么待预测样本的最近邻很可能都属于这个类别,从而导致预测偏差。

4. 维度灾难:在高维空间中,距离的计算可能变得不再有意义,因为随着维度的增加,数据点之间的距离差异可能会变得越来越大。这会导致KNN算法在高维数据上的性能下降,即所谓的“维度灾难”。

综上所述,KNN算法具有简单直观、无需参数估计等优点,但同时也面临着计算量大、K值选择困难等挑战。在实际应用中,我们需要根据具体的数据集和问题特点来选择是否使用KNN算法,并针对性地调整其参数以优化性能。

阅读全文

与knn算法如何减少计算次数相关的资料

热点内容
控制单片机输出四组pwm 浏览:405
怎么设置qq背面的app 浏览:601
如何把pdf转换cad格式 浏览:161
好看的综艺轻松解压 浏览:136
win10手机如何远程连接到服务器地址 浏览:75
编辑样条线子的命令 浏览:363
安卓如何设置不要钱的铃声 浏览:453
行动与命令 浏览:791
空气能的压缩机多少钱 浏览:951
网易手机加密码登录 浏览:98
手机怎么打开pdf文件 浏览:830
支付宝美版加密货币 浏览:120
南京银行app怎么取消人脸识别 浏览:268
吉里吉里安卓10用什么版本 浏览:719
程序员15k初薪 浏览:797
抖店app上架怎么操作 浏览:274
美萍软件数据库在哪个文件夹 浏览:495
压缩卷没有足够空间 浏览:830
反诈app怎么反诈的 浏览:748
跨界解压视频大全 浏览:224