1 KNN算法:
优点:精度高,对异常值不敏感,无数据输入假定
缺点:计算复杂度高,空间复杂度高
适用数据范围:数值型和标称型
算法原理:先给定训练集A,当一个测试集样本t,选择t与A中距离最近的k个训练样本(k通常不大于20),选择这些训练样本中出现次数最多的标签作为t的新标签。
2 采用R实现的代码:
library(hash) KNN=function(testVec,trainMat,trainLables,k){#分类算法 #testVec=c(0,0); #trainMat=matrix(c(1,1.1,1,1,0,0,0,0.1),4,2,byrow=T); #trainLables=c('A','A','B','B'); #k=3; testMat=matrix(testVec,nrow(trainMat),length(testVec)); diffMat=testMat-trainMat; #sqDiffMat=diffMat%*%t(diffMat); #digEle=diag(sqDiffMat); #distances=sqrt(digEle); sqDiffMat=diffMat**2; distances=rowSums(sqDiffMat); sortedDist=order(distances); classCount=hash();#hash函数 for (i in 1:k){ #i=2; label=as.character(trainLables[sortedDist[i]]); if(has.key(label,classCount)){#如果label存在于hash中 num=classCount[[label]]+1; }else{ num=1; } classCount[label]=num; #cat(i,label,num,'\n') } KLabels=values(classCount); a=which(KLabels==max(KLabels)); names(a); } autoNorm=function(data){#归一化,这里是三列数据 max1=max(data[,1]); min1=min(data[,1]); max2=max(data[,2]); min2=min(data[,2]); max3=max(data[,3]); min3=min(data[,3]); minMat=matrix(c(min1,min2,min3),nrow(data),3,byrow=T); range1=max1-min1; range2=max2-min2; range3=max3-min3; normData=data-minMat; normData=normData/matrix(c(range1,range2,range3),nrow(data),3,byrow=T); normData; } #主程序,这里用的是《机器学习实战》第二章里的约会数据前50行 datingData=read.csv("datingTestSet2.tx",sep='\t',header=F);#数据集格式:40920 8.326976 0.953952 3前三个是特征,最后一个是分类 NormData=autoNorm(datingData[,1:3]);#特征的归一化 testVec=c(65000,10,1)#测试样本特征 testLabels=1;#测试样本分类号 label=KNN(testVec,NormData,datingData[,4],3);#测试样本的预测标号 as.numeric(label)==testLabels;#若预测成功则返回T
from numpy import * import operator#运算符模块 from os import listdir import time def classify0(inX, dataSet, labels, k):#kNN分类器 dataSetSize = dataSet.shape[0]#行数 diffMat = tile(inX, (dataSetSize,1)) - dataSet#tile将inX扩充为dataSetSize行1倍inX列的矩阵, sqDiffMat = diffMat**2#每个元素平方相当于multiply(diffMat,diffMat) sqDistances = sqDiffMat.sum(axis=1)#axis=0表示列方向相加,axis=1表示行方向相加 distances = sqDistances**0.5#开方 sortedDistIndicies = distances.argsort()#.argsort对数组升序排序 classCount={}#字典map for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#map::get(key,default=None)若key没在map里则返回default sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)#map::iteritems()返回键值对的迭代器,items()返回键值对元素,operator.itemgetter(1)指操作的对象是第二个域即value,reverse是否逆序,sorted是排序生成新的序列 return sortedClassCount[0][0] def createDataSet(): group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) labels = ['A','A','B','B'] return group, labels def file2matrix(filename):#提取'\t'为分隔符文件的数据 fr = open(filename)#只读方式打开文件 numberOfLines = len(fr.readlines())#对于结构化文件可以读取行数 returnMat = zeros((numberOfLines,3))#全零矩阵 classLabelVector = []#空list fr = open(filename)# index = 0 for line in fr.readlines():#readlines读取整个文件并以行构成一个列表,每行作为for的一个循环 line = line.strip()#删除开头和结尾处的空白符(包括'\n', '\r', '\t', ' ') listFromLine = line.split('\t')#以\t为分割,返回一个list returnMat[index,:] = listFromLine[0:3]#替代每行 classLabelVector.append(int(listFromLine[-1]))#使用负下标选取最后一列 index += 1 return returnMat,classLabelVector def autoNorm(dataSet):#归一化,矩阵化操作 minVals = dataSet.min(0)#返回最高维的最小值,注意数组a.shape返回(2,3,4)表示最底层数组4个元素,然后3个一维数组,2个二维数组,所以这里最高维指2那个#这里假设dataSet是1000行3列的数组,那么min(0)返回三列的最小值 maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = zeros(shape(dataSet))#生成一个和dataset大小一致的全零数组 m = dataSet.shape[0]#返回最高维的元素数目 normDataSet = dataSet - tile(minVals, (m,1)) normDataSet = normDataSet/tile(ranges, (m,1))#归一化 return normDataSet, ranges, minVals#返回归一化矩阵,max-min,min def datingClassTest():#约会数据测试 hoRatio = 0.50#测试集的比例 datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')#加载数据 normMat, ranges, minVals = autoNorm(datingDataMat) m = normMat.shape[0]#二维矩阵返回行数 numTestVecs = int(m*hoRatio)#测试集大小 errorCount = 0.0 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)#1~numTestVecs行是测试集,numTestVecs~m行是训练集 print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]) if (classifierResult != datingLabels[i]): errorCount += 1.0 print "the total error rate is: %f" % (errorCount/float(numTestVecs)) print errorCount#打印错误率 def img2vector(filename):#32×32维的图片矩阵用1×1024的向量表示 returnVect = zeros((1,1024)) fr = open(filename) for i in range(32): lineStr = fr.readline() for j in range(32): returnVect[0,32*i+j] = int(lineStr[j]) return returnVect def handwritingClassTest():#手势识别测试程序 start=time.clock() hwLabels = [] trainingFileList = listdir('trainingDigits') #import os里的listdir是列出目录下的文件名 #训练集 m = len(trainingFileList) trainingMat = zeros((m,1024))#m×1024维的全零矩阵 for i in range(m): fileNameStr = trainingFileList[i]#获取文件名,如9_45.txt表示该图片分类是9,它是数字9的第45个实例 fileStr = fileNameStr.split('.')[0] #以'.'分割字符串,并返回'.'前的子串 classNumStr = int(fileStr.split('_')[0])#返回'_'前的子串 hwLabels.append(classNumStr)#分类号 trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) testFileList = listdir('testDigits')#测试集 errorCount = 0.0 mTest = len(testFileList) for i in range(mTest): fileNameStr = testFileList[i] fileStr = fileNameStr.split('.')[0] #take off .txt classNumStr = int(fileStr.split('_')[0]) vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)#字符串格式化拼接出测试集中文件的相对路径 classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr) if (classifierResult != classNumStr): errorCount += 1.0 print "\nthe total number of errors is: %d" % errorCount print "\nthe total error rate is: %f" % (errorCount/float(mTest)) print("Time used:",time.clock()-start)