k-NearestNeighbor(k近邻)算法是基于实例的一种学习算法。
基于实例的学习算法只是简单的把训练样例存储起来,将泛化的工作推迟到分类新的实例时。
基于实例的学习好处:
可以为不同的待分类查询实例建立不同的目标函数逼近。当目标函数很复杂但却可以用不太复杂的局部逼近描述时,这样做有显著的优势
基于实例的学习不足:
1.分类新实例的开销可能很大
2.当检索相似的训练样例时,他们一般考虑实例的所有属性,如果目标概念仅依赖与很多属性中的几个时,那么真正最相似的实例之间很可能相去甚远(特别对kNN,可能产生维度灾难)
kNN算法:
假定所有的实例对应于n维空间R中的点。一个实例的最近邻是通过标准欧式距离定义的。他是通过计算距离待分类样例x的k个训练样例中最普遍的目标值,返回其中最普遍的目标值作为x的估计值
优点:精度高、对异常值不敏感、无数据输入假定
缺点:计算复杂度高、空间复杂度高
适用数据范围:数值型和标称型
kNN的改进:
对k个近邻的贡献加权,根据他们相对查询点x的距离,将较大的权值赋给较近的近邻,权值一般采用1/(d平方) 其中d是x的近邻y与x的距离
最原始的分类器的算法:
def classify0(intX, dataSet, labels, k): dateSetSize = dataSet.shape[0] diffMat = tile(intX, (dateSetSize, 1)) - dataSet; sqDiffMat = diffMat ** 2; sqDistance = sqDiffMat.sum(axis=1) distance = sqDistance ** 0.5; sortedDistIndicies = distance.argsort(); classCount = {} for i in range(k): voteIlabel = labels[sortedDistIndicies[i]]; classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True); return sortedClassCount[0][0]
初次看有很多函数不懂,写在这里做个记录
第1行是dataSet.shape是获得dataSet这一个数组的行数跟列数,返回的是一个元组,【0】即获得行数
第2行tile函数是将intX这一个输入向量扩展为同dataSet具有相同的行数(这个函数还不是太懂,后面参数是(dateSetSize,
1
)
即把他扩展成dataSetSize行,列数不变,别的参数就不太明白了)
第5行的axis=1是指将sqDiffMat
数组的每行元素相加,默认(axis=None)是把所有的元素相加,axis=0则是把每列元素相加,numpy中0轴表示列,1轴表示行
第7行的distance.argsort()函数,是指将distance按从小到达排序,返回排序后的元素在未排序的数组中的下标
第13行的sorted函数中,classCount.iteritems()函数返回字典classCount键值对的可迭代对象,operator.itemgetter(
1
)
是通过operator模块的itemgetter定义了获取下标为1的元素
从文本文件解析数据
将32*32的二进制图像转换成1*1024的向量
def p_w_picpath2Vector(filename): returnVector = zeros((1, 1024)) fr = file(filename) flag = True; for i in range(32): lineStr = fr.readline(); for j in range(32): returnVector[0, 32 * i + j] = int(lineStr[j]); return returnVector;
第1行,构建了一个1*1024,初值都为0的数组
归一化特征值
def autoNorm(dataSet): minValue = dataSet.min(0); maxValue = dataSet.max(0); range = maxValue - minValue; normDataSet = zeros(shape(dataSet)); m = dataSet.shape[0]; normDataSet = dataSet - tile(minValue, (m, 1)); normDataSet = normDataSet / (tile(range, (m, 1))) return normDataSet, range, minValue;
第1行参数0代表是返回每列的最小值,若为1则是返回每行的最小值,默认返回整个数组最小值
第2行同理
第5行的shape(dataSet)等价与dataSet.shape(),返回包含dataSet的行数和列数的元组
手写数字识别系统的测试代码
取k=3
dirName = "/home/quincy/python/MachineLearing/machinelearninginaction/Ch02/trainingDigits"; hwLabels = [] trainingFileList = os.listdir(dirName); m = len(trainingFileList); trainingMat = numpy.zeros((m, 1024)); for i in range(m): fileNameStr = trainingFileList[i]; fileStr = fileNameStr.split(".")[0]; classNumStr = int(fileStr.split("_")[0]); hwLabels.append(classNumStr); trainingMat[i, :] = kNN.p_w_picpath2Vector('%s/%s' % (dirName, fileNameStr)); testFileList = os.listdir("/home/quincy/python/MachineLearing/machinelearninginaction/Ch02/testDigits"); errorCount = 0; mTest = len(testFileList); for i in range(mTest): fileNameStr = testFileList[i]; fileStr = fileNameStr.split(".")[0]; classNumStr = int(fileStr.split("_")[0]); vectorUnderTest = kNN.p_w_picpath2Vector( "/home/quincy/python/MachineLearing/machinelearninginaction/Ch02/testDigits/%s" % fileNameStr); classifierResult = kNN.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 "the total number of errors is:%d" % errorCount; print "the total error rate is: %g" % ( errorCount / float(mTest));
第4行的listdir()函数是os模块中用于返回指定目录中全部文件的方法
自己测了下 随着k的增加,错误率如下
k=3 错误率=0.0137421
k=4 错误率=0.0147992
k=5 错误率=0.0190275
k=6 错误率=0.0200846
k=7 错误率=0.0221987
将分类器自己改进成按距离加权
def classify0(intX, dataSet, labels, k): dateSetSize = dataSet.shape[0] diffMat = tile(intX, (dateSetSize, 1)) - dataSet; sqDiffMat = diffMat ** 2; sqDistance = sqDiffMat.sum(axis=1) distance = sqDistance ** 0.5; sortedDistIndicies = distance.argsort(); classCount = {} for i in range(k): voteIlabel = labels[sortedDistIndicies[i]]; classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1/distance[i]** 2 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True); return sortedClassCount[0][0]
改变在第11、12行
改后的k值和错误率如下:
k=3 错误率=0.0137421
k=4 错误率=0.0137421
k=5 错误率=0.0169133
k=6 错误率=0.0211416
k=7 错误率=0.0221987
k=8 错误率=0.02537
k=9 错误率=0.02537
k=10 错误率=0.0232558