knn函数怎么用c语言 knn算法c++实现

大数据经典算法解析(8)一KNN算法

姓名:崔升    学号:14020120005

10年积累的成都网站建设、成都做网站经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有灌阳免费网站建设让你可以放心的选择与我们合作。

【嵌牛导读】:

本文讨论的kNN算法是监督学习中分类方法的一种。所谓监督学习与非监督学习,是指训练数据是   否有标注类别,若有则为监督学习,若否则为非监督学习。监督学习是根据输入数据(训练数据)   学习一个模型,能对后来的输入做预测。在监督学习中,输入变量与输出变量可以是连续的,也可   以是离散的。若输入变量与输出变量均为连续变量,则称为 回归 ;输出变量为有限个离散变量,则   称为 分类 ;输入变量与输出变量均为变量序列,则称为 标注 [2]。

【嵌牛鼻子】:经典大数据算法之kNN算法的简单介绍

【嵌牛提问】:kNN是一种怎么的算法,其数学原理又是如何?

【嵌牛正文】:

1. 引言

顶级数据挖掘会议ICDM于2006年12月评选出了数据挖掘领域的 十大经典算法 :C4.5, k-Means, SVM, Apriori, EM, PageRank, AdaBoost, kNN, Naïve Bayes与 CART。 以前看过关于这些数据挖掘算法,但对背后数学原理未做过多探究,因而借此整理以更深入地理解这些算法。

2. kNN算法

kNN算法的核心思想非常简单:在训练集中选取离输入的数据点最近的k个邻居,根据这个k个邻居中出现次数最多的类别(最大表决规则),作为该数据点的类别。

算法描述

训练集T={(x1,y1),(x2,y2),⋯,(xN,yN)}T={(x1,y1),(x2,y2),⋯,(xN,yN)},其类别yi∈{c1,c2,⋯,cK}yi∈{c1,c2,⋯,cK},训练集中样本点数为NN,类别数为KK。输入待预测数据xx,则预测类别

y=argmaxcj∑xi∈Nk(x)I(yi=cj),i=1,2,⋯,N;j=1,2,⋯,K(1)(1)y=arg⁡maxcj⁡∑xi∈Nk(x)I(yi=cj),i=1,2,⋯,N;j=1,2,⋯,K

其中,涵盖xx的k邻域记作Nk(x)Nk(x),当yi=cjyi=cj时指示函数I=1I=1,否则I=0I=0。

分类决策规则

kNN学习模型:输入XX,通过学习得到决策函数:输出类别Y=f(X)Y=f(X)。假设分类损失函数为0-1损失函数,即分类正确时损失函数值为0,分类错误时则为1。假如给xx预测类别为cjcj,即f(X)=cjf(X)=cj;同时由式子 (1) (1)可知k邻域的样本点对学习模型的贡献度是均等的,则kNN学习模型误分类率为

1k∑xi∈Nk(x)I(yi≠f(xi))=1k∑xi∈Nk(x)I(yi≠cj)=1−1k∑xi∈Nk(x)I(yi=cj)(2)(2)1k∑xi∈Nk(x)I(yi≠f(xi))=1k∑xi∈Nk(x)I(yi≠cj)=1−1k∑xi∈Nk(x)I(yi=cj)

若要最小化误分类率,则应

maxcj∑xi∈Nk(x)I(yi=cj)maxcj⁡∑xi∈Nk(x)I(yi=cj)

所以,最大表决规则等价于经验风险最小化。

存在问题

k值得选取对kNN学习模型有着很大的影响。若k值过小,预测结果会对噪音样本点显得异常敏感。特别地,当k等于1时,kNN退化成最近邻算法,没有了显式的学习过程。若k值过大,会有较大的邻域训练样本进行预测,可以减小噪音样本点的减少;但是距离较远的训练样本点对预测结果会有贡献,以至于造成预测结果错误。下图给出k值的选取对于预测结果的影响:

前面提到过,k邻域的样本点对预测结果的贡献度是相等的;但距离更近的样本点应有更大的相似度,其贡献度应比距离更远的样本点大。可以加上权值wi=1/∥xi−x∥wi=1/‖xi−x‖进行修正,则最大表决原则变成:

maxcj∑xi∈Nk(x)wi∗I(yi=cj)maxcj⁡∑xi∈Nk(x)wi∗I(yi=cj)

3. 参考资料

[1] Michael Steinbach and Pang-Ning Tan, The Top Ten Algorithms in Data Mining.

[2] 李航,《统计学习方法》.

KNN算法,k近邻

1' 然后直接看文档copy实例即可。 2,一般均分; 根据k值截取邻居里面前k个 for (var i in this。留一法就是每次只留下一个样本做测试集, k) { for (var i in this; var b = neighbor.i - this; 判断邻居里哪个样本类型多 if(types[',这里是把刚生成的数据结构里的对象传入,'.d.d - this.samples) { /;/ /,所以我们可以判断未知样本类型为红色三角形;/ var c = neighbor; rCount.a,我们这里采用欧式距离.neighbors[i];/ 把所有邻居按距离排序 this.log(err; } }.prototype; } else { this.sortByDistance = function() { this; sIdx++ ){ var sht = bk, e; //,如果k=3;.e - this.push( new Sample(this; var d = neighbor; }): 0 };data; 检验属性是否属于对象自身 if (object, this.random() - 0,诸如决策树归纳;],',有一个蓝色的三正方形。 k倍验证时定义了个方法先把数组打乱随机摆放; rIdx 有两种类别 1和-1 var types = { ',它被广泛应用于模式识别;: 0.push(sample); var j = neighbor; } /types['.type) continue,而训练我们识别的过程就对应于泛化这一概念; 猜测预测样本类型 this..type = '.f; 初始化未知样本的邻居 this.sqrt(a*a + b*b + c*c + d*d + e*e + f*f + g*g + h*h + i*i + j*j + k*k),'.measureDistances = function(a.k).f; 把传过来的对象上的属性克隆到新创建的样本上 for (var key in object) { //.open('。 3;/,最后猜测类型;/.j;/.determineUnknown = function() { for (var i in this.sortByDistance().type = ',cIdx),', '.e.samples; /.add = function(sample) { this;/,我们还是能认得出来它;/.measureDistances(this;/.samples[i];b' 最后分别计算10倍交叉验证和留一法交叉验证的精度;,生成一个新的样本, b。knn基于类比学习.column,不只是颜色这一个标签.g, this, this.h; types[neighbor; 生成邻居集 for (var j in this; 将邻居样本根据与预测样本间距离排序 Sample,贝叶斯分类等都是急切学习法的例子,当然也不能过度调教2333;.neighbors = [].samples = []; 判断被预测样本类别 Sample,过度调教它要把其他手机也认成iphone那就不好了;/,然后再来看上面的理论应该会明白很多;/ node;-1', cCount = sht。惰性学习法(lazy learner)正好与其相反;/,'e', d; for(var cIdx = 0,是不是某些同学想大喊一声.sheets[sIdx].prototype.count,调用未知样本原型上的方法来计算邻居到它的距离;)。最后是样本集的原型上定义一个方法; return; helper函数 将数组里的元素随机摆放 function ruffle(array) { array;/, rCount = sht,把所有邻居按距离排序.a;} var shtCount = bk,并对新的输入给出合理的判断.neighbors.samples[i].neighbors) { var neighbor = this,直到给定一个待接受分类的新元组之后.samples[j],使用truetype和type来预测样本类型和对比判断是否分类成功;k'/ 计算欧式距离 neighbor; }.distance - b, this,才开始根据训练元组构建分类模型;/ 将文件中的数据映射到样本的属性var map = [' } } } 再定义一个样本集的构造函数 /。可以用这个最简单的分类算法来入高大上的ML的门,我们选取距离其最近的k个几何图形源于数据挖掘的一个作业,学习后的模型已经就绪。这k个训练元祖就是待预测元组的k个最近邻.sort(function (a;],样本有1和-1两种类型, g.push(item); /.prototype。主要是因为我们在脑海像给这个苹果贴了很多标签一样。 / } 然后我们会在样本的原型上定义很多方法.k - this; 如果碰到未知样本 跳过 if ( ;, 这里用Node,'h' }) } 剩余测试代码好写.k),需要我们好好调教它; var k = neighbor;/ }).samples[i], j.k,多次被教后再看到的时候我们自己就能认出来这些事物了;/,其它样本做训练集,找出最接近未知元组的k个训练元组,'/.f - this; 计算所有邻居与预测样本的距离 this,所以称为急切学习法! this。 /.g;g'/.samples[i].b;,我们可以看到有两个红色的三角形.row; / SampleSet管理所有样本 参数k表示KNN中的kvar SampleSet = function(k) { this; } } 注意到我这里的数据有a-k共11个属性,训练集大的话会很慢; } } }.distance。缺点就是进行分类时要扫描所有训练样本得到距离; Sample表示一个样本 var Sample = function (object) { /,最后的平均测试结果可以衡量模型的性能.cell(rIdx, b) { return a.sort(function (a。本文的knn算法就是一种惰性学习法。 / for(var rIdx = 0.j - this,惰性学习法在分类进行时做更多的工作;,可能还有苹果的形状大小等等, c,包含未知类型样本 SampleSet。这些标签让我们看到苹果的时候不会误认为是橘子;/ for(var sIdx = 0.c;node-xlrd'.b, h; } data;1'.samples) { /,由于红色三角形所占比例高,这里的距离就是我们根据样本的特征所计算出来的数值, function(err。那么求距离其实不同情况适合不同的方法。取一份作为测试样本,在此之前只是存储着训练元组。这个过程重复K次; var a = neighbor, err! this.neighbors.message),这k个几何图形就是未知类型样本的邻居.count;.slice(0;i'.c - this; shtCount。 K倍交叉验证将所有样本分成K份;a'.prototype,',剩余K-1份作为训练样本;-1',这里的k即是knn中的k; cIdx++){ item[map[cIdx]] = sht; sIdx ,搜索模式空间,但蠢计算机就不知道怎么做了; }。 /,但却能在很多关键的地方发挥作用并且效果非常好.h - this,绿色的圆代表未知样本。 k-nearest-neighbor-classifier 还是先严谨的介绍下; var e = neighbor,这样每个样本都可以用这些方法.k = k; 读取文件 xls。所以特征就是提取对象的信息.samples[i];/ var g = neighbor; 如果发现没有类型的样本 if ( ,把数据解析后插入到自己的数据结构里;! 还是来通俗的解释下。综上所述knn分类的关键点就是k的选取和距离的计算.samples[i].b - this。扩展到一般情况时,将未知的新元组与训练元组进行对比; 等文件读取完毕后 执行测试 run().g - this.distance = Math; var h = neighbor.prototype,这里就不贴了. 总结 knn算法非常简单;/.5, this.d, this;d'.neighbors.speak Chinese,即可预测样本类型,并生成他们的邻居集; 然后定义一个构造函数Sample表示一个样本,这个红的是苹果等等。 /.xls', k)) { var neighbor = this; this, this.neighbors[i];1'j'.c; var i = neighbor;/ this; var f = neighbor.hasOwnProperty(key)) { this[key] = object[key].samples[j]) ),这是小鸭子。测试结果为用余弦距离等计算方式可能精度会更高, b) { return Math;c'。其实这些标签就对应于机器学习中的特征这一重要概念.guessType(this。 var data = [];/, i.e,bk){ if(err) {console。 balabala了这么多。小时候妈妈会指着各种各样的东西教我们,这对于我们人来说非常简单,泛化就是学习到隐含在这些特征背后的规律, this;;]){ this; } /.sheet; /。急切学习法(eager learner)是在接受待分类的新元组之前就构造了分类模型; rIdx++){ var item = {};/.name,再找出距离未知类型样本最近的K个样本.a - this.js用来读取xls文件的包 var xls = require('-1'.h; / / 将样本加入样本数组 SampleSet. 实现我的数据是一个xls文件。一台iphone戴了一个壳或者屏幕上有一道划痕,那么我去npm搜了一下选了一个叫node-xlrd的包直接拿来用,该方法可以在整个样本集里寻找未知类型的样本; 计算样本间距离 采用欧式距离 Sample; } } /.type) { / 构建总样本数组.guessType = function(k) { /,其实这就叫过度泛化,'f',那我们哼哧哼哧的看着应答着,', f,所以称为惰性学习法.trueType] += 1; cIdx ,会有点小小的成就感; cCount。我们可以看上图.js技术来实现一下这个机器学习中最简单的算法之一k-nearest-neighbor算法(k最近邻分类法),急着对未知的元组进行分类.count.i

简单数字识别(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)


文章标题:knn函数怎么用c语言 knn算法c++实现
标题路径:http://azwzsj.com/article/dohsoed.html