KNN算法简介
(k-th nearestneighbour),邻近算法,或者K最近邻分类算法,这可以说是整个数据挖掘分类技术中最简单的方法了。kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。NN方法在类别决策时,只与极少量的相邻样本有关。
KNN决策过程
下图中,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被赋予红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。
KNN存在的问题
样本不均衡:当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的k个邻居中大容量类的样本占多数。该算法只计算最近的邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。
计算量大:对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的k个最近邻点。
目前常用的解决办法是事先对已知样本点进行剪枝,去除对分类作用不大的样本。所有的分类算法都存在的问题:特征的选择和距离的归一化。选择不同特征的分类结果是不一样的;如果距离不归一化,那么将会影响分类的结果。
KNN适用范围:该方法适用于样本容量比较大的类域的自动分类。
KNN实例:
数据集简介:MNIST Handwritten Digits字符库中含有0-9的训练数据集和0-9测试数据集两种图片,每张图片灰度级都是8,图像大小为28*28。训练样本有60000张图片,测试样本有10000张图片。
数字识别(Digital Recognition)是光学字符识别技术(Optical Character Recognition,OCR)的一个分支。我们选用粗网格特征作为特征。粗网格特征是指不同数字在不同位置块内的黑色像素点个数特征。将处理后的样本图像划分不重叠(non-overlapping)的块,计算每个块中黑色像素点占全部黑色像素的百分比,并以此作为特征,实验中将图像分别分成7*7和4*4大小的块。所取的块越小,识别率越高,但是会带来处理时间的增长。
训练过程代码:
directory=uigetdir('','选择学习图片路径');
ImageNum=60000;
numis=textread('study.txt','%1d'); %numis 是正确的ImageNum个样本值
feasum=zeros(10,16); %10*16的特征之和数组
numsum=zeros(10); %0-9的个数,0用10代替
h_w=waitbar(0,'请稍后,正在处理中>>>>>>>>');
for i=1:ImageNum
impath=fullfile(directory,['TrainImage_' num2str(i,'%05d') '.bmp']);
rawim=imread(impath);
xx=find(rawim<150);
rawim(xx)=0;
x=find(rawim>=150);
rawim(x)=255;
bwim=im2bw(rawim,0.5);%二值化
gridnum=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];%保存16个块的黑色像素个数
xbase=1;
ybase=1;
for yz=0:3
for xz=0:3 %这两个是4*4的大块
for ybase=1:7
for xbase=1:7 %这两个是在7*7的小块内
if(bwim(yz*7+ybase,xz*7+xbase)==0) %如果是黑色像素
gridnum(yz*4+xz+1)=gridnum(yz*4+xz+1)+1;
end
end
end
end
end
jud=numis(i);
if jud==0 %如果是0,放在第十个
jud=10;
end
numsum(jud)=numsum(jud)+1; %统计0-9的个数
for t=1:16
feasum(jud,t)=feasum(jud,t)+gridnum(t);
end
waitbar(i/ImageNum);
end
sum=zeros(10); %sum保存0-9所有节点黑色像素总和,用于归一化
for i=1:10
for j=1:16
sum(i)=sum(i)+feasum(i,j);
end
end
feature=zeros(10,16);
for i=1:10
for j=1:16
feature(i,j)=feasum(i,j)/sum(i);
end
end
fid=fopen('features.txt','w'); %将特征值写到 feature.txt 文件
for i=1:10
fprintf(fid,'%1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f\n',feature(i,1),feature(i,2),feature(i,3),feature(i,4),feature(i,5),feature(i,6),feature(i,7),feature(i,8),feature(i,9),feature(i,10),feature(i,11),feature(i,12),feature(i,13),feature(i,14),feature(i,15),feature(i,16));
end
fclose(fid);
close(h_w);
识别过程代码:
clear;
clc;
directory=uigetdir('','选择测试图片路径《注意是测试集》');
ImageNum=10000;
numis=textread('test.txt','%1d'); %numis 是正确的ImageNum个样本值
numsum=zeros(1,10); %0-9的个数,0用10代替
errornum=0; %识别错误个数
estat=zeros(1,10);
fid=fopen('log.txt','w');
%读取学习来的特征值
fea=load('features.txt'); %10*16的每个数字的特征数
h_w=waitbar(0,'请稍后,正在处理中>>>>>>>>');
for i=1:ImageNum
impath=fullfile(directory,['TestImage_' num2str(i,'%05d') '.bmp']);
rawim=imread(impath);
xx=find(rawim<150);
rawim(xx)=0;
x=find(rawim>=150);
rawim(x)=255;
bwim=im2bw(rawim,0.5);%二值化
%读取每个块的像素个数
gridnum=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];%保存16个块的黑色像素个数
xbase=1;
ybase=1;
for yz=0:3
for xz=0:3 %这两个是4*4的大块
for ybase=1:7
for xbase=1:7 %这两个是在7*7的小块内
if(bwim(yz*7+ybase,xz*7+xbase)==0) %如果是黑色像素
gridnum(yz*4+xz+1)=gridnum(yz*4+xz+1)+1;
end
end
end
end
end
%统计16块中所有黑色像素并归一化
sum=0;
for c=1:16
sum=sum+gridnum(c);
end
%每个格子像素个数归一化
numfea=zeros(1,16);
for c=1:16
numfea(c)=gridnum(c)/sum;
end
%计算距离
dist=zeros(1,10); %dist(c) 代表与标准第c个数字的距离
for c=1:10
for d=1:16
temp=numfea(d)-fea(c,d); %该数字第d块减去数字c第d块特征值
dist(c)=dist(c)+temp^2;
end
end
%计算最小距离和对应的index
[a,index]=min(dist);
%numis 标准值
jud=numis(i);
if jud==0 %如果是0,放在第十个
jud=10;
end
numsum(jud)=numsum(jud)+1; %统计0-9的个数
%比较index 和jud 相等否,相等识别正确,不相等识别错误并记录
if index~=jud
errornum=errornum+1;
if jud==10;
temp=0;
else
temp=jud;
end
if index==10;
tempi=0;
else
tempi=index;
end
estat(jud)=estat(jud)+1;
fprintf(fid,'%5dth misrecognize %d to %d\n',i,temp,tempi);
end
waitbar(i/ImageNum);
end
ErrorRate = errornum/ImageNum;
disp('错误率为:');
disp(ErrorRate);
disp('错误识别的图片个数为:');
disp(errornum);
fclose(fid);
close(h_w);