机器学习实战ByMatlab(一)KNN算法

http://blog.csdn.net/llp1992/article/details/45040685

KNN 算法其实简单的说就是“物以类聚”,也就是将新的没有被分类的点分类为周围的点中大多数属于的类。它采用测量不同特征值之间的距离方法进行分类,思想很简单:如果一个样本的特征空间中最为临近(欧式距离进行判断)的K个点大都属于某一个类,那么该样本就属于这个类。这就是物以类聚的思想。

当然,实际中,不同的K取值会影响到分类效果,并且在K个临近点的选择中,都不加意外的认为这K个点都是已经分类好的了,否则该算法也就失去了物以类聚的意义了。

KNN算法的不足点:

1、当样本不平衡时,比如一个类的样本容量很大,其他类的样本容量很小,输入一个样本的时候,K个临近值中大多数都是大样本容量的那个类,这时可能就会导致分类错误。改进方法是对K临近点进行加权,也就是距离近的点的权值大,距离远的点权值小。 
2、计算量较大,每个待分类的样本都要计算它到全部点的距离,根据距离排序才能求得K个临近点,改进方法是:先对已知样本点进行剪辑,事先去除对分类作用不大的样本。

适用性:

适用于样本容量比较大的类域的自动分类,而样本容量较小的类域则容易误分

算法描述:

    1、计算已知类别数据集合汇总的点与当前点的距离
    2、按照距离递增次序排序
    3、选取与当前点距离最近的K个点
    4、确定距离最近的前K个点所在类别的出现频率
    5、返回距离最近的前K个点中频率最高的类别作为当前点的预测分类

Python 实现

from numpy import *
import operator

def createDataSet():
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    lables = ['A','A','B','B']
    return group,lables

# KNN 分类算法
def classify0(inx,dataSet,labels,k):
    dataSetSize = dataSet.shape[0] # shape[0]获取行 shape[1] 获取列
    # 第一步,计算欧式距离
    diffMat = tile(inx,(dataSetSize,1)) - dataSet  #tile类似于matlab中的repmat,复制矩阵
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distance = sqDistances ** 0.5
    sortedDistIndecies = distance.argsort()  # 增序排序
    classCount = {}
    for i in range(k):
    # 获取类别 
        voteIlabel = labels[sortedDistIndecies[i]]
        #字典的get方法,查找classCount中是否包含voteIlabel,是则返回该值,不是则返回defValue,这里是0
        # 其实这也就是计算K临近点中出现的类别的频率,以次数体现
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
        #  对字典中的类别出现次数进行排序,classCount中存储的事 key-value,其中key就是label,value就是出现的次数
        #  所以key=operator.itemgetter(1)选中的事value,也就是对次数进行排序
    sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
        #sortedClassCount[0][0]也就是排序后的次数最大的那个label
    return sortedClassCount[0][0]

调用方式:

import sys;
sys.path.append("/home/llp/code/funcdef")
import KNN 

group,labels = KNN.createDataSet();
relust = KNN.classify0([0,0],group,labels,3)
print 'the classify relust is :' , relust

Matlab 实现

这里以一个完整实例呈现,代码如下:

function relustLabel = KNN(inx,data,labels,k)
%% 
%   inx 为 输入测试数据,data为样本数据,labels为样本标签
%%
[datarow , datacol] = size(data);
diffMat = repmat(inx,[datarow,1]) - data ;
distanceMat = sqrt(sum(diffMat.^2,2));
[B , IX] = sort(distanceMat,'ascend');
len = min(k,length(B));
relustLabel = mode(labels(IX(1:len)));
end

可以看到,整个KNN算法的Matlab代码也就只有6行,比Python少很多,这其中要得益于Matlab成熟的矩阵计算和很多成熟的函数。

实际调用中,我们利用一个数据集进行测试,该数据集是由1000个样本的3维坐标组成,共分为3个类

首先可视化我们的数据集,看看它的分布:

第一维和第二维:可以清晰地看到数据大致上分为 3 类

这里写图片描述

第1维和第3维:从这个角度看,3类的分布就有点重叠了,这是因为我们的视角原因

这里写图片描述

画出3维,看看它的庐山真面目:

这里写图片描述

由于我们已经编写了KNN代码,接下来我们只需调用就行。了解过机器学习的人都应该知道,很多样本数据在代入算法之前都应该进行归一化,这里我们将数据归一化在[0,1]的区间内,归一化方式如下:

newData = (oldData-minValue)/(maxValue-minValue)

其中,maxValue为oldData的最大值,minValue为 oldData 的最小值。

同时,我们对于1000个数据集,采取10%的数据进行测试,90%的数据进行训练的方式,由于本测试数据之间完全独立,可以随机抽取10%的数据作为测试数据,代码如下:

function KNNdatgingTest
%%
clc
clear
close all
%%
data = load('datingTestSet2.txt');
dataMat = data(:,1:3);
labels = data(:,4);
len = size(dataMat,1);
k = 4;
error = 0;
% 测试数据比例
Ratio = 0.1;
numTest = Ratio * len;
% 归一化处理
maxV = max(dataMat);
minV = min(dataMat);
range = maxV-minV;
newdataMat = (dataMat-repmat(minV,[len,1]))./(repmat(range,[len,1]));
% 测试
for i = 1:numTest
    classifyresult = KNN(newdataMat(i,:),newdataMat(numTest:len,:),labels(numTest:len,:),k);
    fprintf('测试结果为:%d  真实结果为:%d\n',[classifyresult labels(i)])
    if(classifyresult~=labels(i))
        error = error+1;
    end
end
  fprintf('准确率为:%f\n',1-error/(numTest))
end

当我们选择K为4的时候,准确率为:97%

KNN进阶

接下来我们将运用KNN算法实现一个手写识别系统,训练数据集大约2000个样本,每个数字大概有200个样本 
测试数据大概有900个样本,由于每个样本都是一个32x32的数字,我们将其转换为1x1024的矩阵,方便我们利用KNN算法 
数据如下:

这里写图片描述

由于数据量比较大,加载数据的时候回花一点时间,具体代码如下:

function handWritingTest
%%
clc
clear
close all
%% 获取目录下的所有txt文件名称
d = dir(['digits/trainingDigits/' '*.txt']); % struct 类型
dircell = struct2cell(d); %cell 类型
trainSetLen = size(dircell,2);
K = 4;
dataSize = 1024;
trainLabels = zeros(trainSetLen,1);
trainSet = [];
simpleTrainSet = zeros(1,dataSize);
simpleTestSet = zeros(1,dataSize);


%% 加载数据
fprintf('loading data...')
for i = 1:trainSetLen
    trainName =  dircell(1,i);
    trainFilename = cell2mat(trainName);
    trainLabels(i) = str2num(trainFilename(1));
    fid = fopen(['digits/trainingDigits/' trainFilename],'r');
    traindata = fscanf(fid,'%s');
    for j = 1:dataSize
        simpleTrainSet(j) =  str2num(traindata(j));
    end
    trainSet = [trainSet ; simpleTrainSet];
    fclose(fid);
end
d = dir(['digits/testDigits/' '*.txt']); % struct 类型
dircell = struct2cell(d); %cell 类型
testSetLen = size(dircell,2);
error = 0;
%% 测试数据
for k = 1:testSetLen
    testName =  dircell(1,k);
    testFilename = cell2mat(testName);
    testLabels = str2num(testFilename(1));
    fid = fopen(['digits/testDigits/' testFilename],'r');
    testdata = fscanf(fid,'%s');
    for j = 1:dataSize
        simpleTestSet(j) =  str2num(testdata(j));
    end
    classifyResult = KNN(simpleTestSet,trainSet,trainLabels,K);
    fprintf('识别数字为:%d  真实数字为:%d\n' , [classifyResult , testLabels])
    if(classifyResult~=testLabels)
        error = error+1;
    end
    fclose(fid);
end
fprintf('识别准确率为:%f\n',1-error/testSetLen)
end

不同的K识别准确率稍有不同,当K为4的时候,准确率为 98.31%


  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值