kNN算法

<pre class="markdown-doc">k-近邻算法(kNN) --------------- ### 概述 - 简单而言,k-近邻算法采用测量不同特征值之间的距离方法进行分类 - 工作原理: 1. 存在一个样本数据集合(训练样本集),其中每个数据存在标签 2. 输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较 3. 算法提取样本集中数据中最相似(最相邻)的分类标签,一般而言,我们只选择样本数据前k个最相似数据 4. k即为k-近邻算法中k的出处,一般k取值不大于20 ### 示例 #### 场景一 收集到约会数据,保存在 'datingTestSet.txt' 中,每个样本数据占据一行,总共有1000行。 样本包含以下3种特征以及1种label: - 每年获得的飞行常客里程数 - 玩视频游戏所耗时间百分比 - 每周消费冰淇淋公升数 - label分为三种:很喜欢,一般喜欢,不喜欢 ##### 考虑 1. 算法采用 k 近邻算法 2. label可以依次用数字1,2,3代表很喜欢,一般喜欢以及不喜欢,假设这样的数据文件变为了 'datingTestSet2.txt' 3. 数据样本的前10%可以作为测试样本,剩余样本可以作为训练样本 ##### 实现 1. 将数据读入,保存为一个 `array` 待用 ``` {.python} from numpy import * def file2matrix(filename): fr = open(filename) arrayOLines = fr.readlines() m = len(arrayOLines) returnMat = zeros((m,3)) classLabelVector = [] index = 0 for line in arrayOLines: line = line.strip() listFromLines = line.split('\t') returnMat[index,:] = listFromLines[0:3] classLabelVector.append(int(listFromLines[-1])) index += 1 return returnMat, classLabelVector ``` 2. 三种数据的数值范围差别过大,会导致对label的影响不一致,因此,需要对三种数据进行归一化 ``` {.python} from numpy import * def autoNorm(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals dataSetSize = dataSet.shape[0] dataSet = dataSet - tile(minVals, (dataSetSize,1)) normDataSet = dataSet/tile(ranges, (dataSetSize,1)) return normDataSet, ranges, minVals ``` 3. 算法实现 ``` {.python} from numpy import * import operator def classify0(inX, dataSet, labels, k): # 计算距离 m = dataSet.shape[0] diffMat = tile(inX, (m, 1)) - dataSet sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis=1) distances = sqDistances**0.5 # 将距离进行排序 # argsort() 函数,可以将 array 中的数值,按从小到大编号 sortedDistIndices = distances.argsort() classCount = {} for i in range(k): # 前k个离测试点最近的label voteIlabel = labels[sortedDistIndices[i]] classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] ``` 4. 进行测试 ``` {.python} from numpy import * def datingClassTest(): # 前10%作为测试样本 hoRatio = 0.1 # 导入数据并归一 dataFile = '/home/curiousbull/WorkSpace/Python/MachineLearning/day01_kNN/datingTestSet2.txt' datingDataMat, datingLabels = file2matrix(dataFile) 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) 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))) ``` #### 场景二 目录digits下,分别有两个目录,testDigits和trainingDigits,代表用于测试的数据和训练的数据, 样本名包含需要识别的数字,也就是label,样本中是需要识别的手写数字数据集,每个单独文件都是32x32 的矩阵形式,包含 0 和 1 ##### 考虑 1. 使用 k 近邻算法 2. 将文件名进行分割,得到对应的 labels 向量 3. 将每个文件内的32x32的0,1数字存储到一个vector,也就是一个1x1024的向量,作为识别的输入 ##### 实现 1. 得到labels向量 ``` {.python} from numpy import * from os import listdir def file2labels(dirName): trainingFileList = listdir(dirName) numberOfFiles = len(trainingFileList) hwLabels = [] for i in range(numberOfFiles): strTrainingFiles = trainingFileList[i].split('.')[0] hwLabels.append(int(strTrainingFiles.split('_')[0])) trainingFileList[i] = dirName + trainingFileList[i] return hwLabels, trainingFileList ``` 2. 将img(32x32矩阵)转换为1x1024向量形式 ``` {.python} from numpy import * from os import listdir def img2vec(filename): fr = open(filename) vecImg = zeros((1,1024)) for row in range(32): strLine = fr.readline() for col in range(32): vecImg[0, 32*row+col] = int(strLine[col]) return vecImg ``` 3. 实现kNN算法 ``` {.python} from numpy import * from os import listdir import operator def classify0(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] diffMat = tile(inX, (dataSetSize, 1))-dataSet sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis=1) distances = sqDistances**0.5 sortedDistIndicies = distances.argsort() countLabels = {} errorCount = 0.0 for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] countLabels[voteIlabel] = countLabels.get(voteIlabel, 0) + 1 sortedCountLabels = sorted(countLabels.items(), key=operator.itemgetter(1),\ reverse=True) return sortedCountLabels[0][0] ``` 4. 测试样本 ``` {.python} def hwTest(): testDirName = 'digits/testDigits/' trainingDirName = 'digits/trainingDigits/' hwLabels, trainingFiles = file2labels(trainingDirName) numberOfTrainingFiles = len(trainingFiles) trainingDataSet = zeros((numberOfTrainingFiles, 1024)) for i in range(numberOfTrainingFiles): trainingDataSet[i,:] = img2vec(trainingFiles[i]) testHwLabels, testFiles = file2labels(testDirName) numberOfTestFiles = len(testFiles) errorCount = 0.0 for i in range(numberOfTestFiles): testVec = img2vec(testFiles[i]) classifierLabel = classify0(testVec, trainingDataSet, hwLabels, 3) print("the classifer come back to: %d while the true value is: %d"\ %(classifierLabel, testHwLabels[i])) if(classifierLabel != testHwLabels[i]): errorCount += 1.0 print("the total error classifiers number is: %f" %(errorCount/float(numberOfTestFiles))) print("the error rate is: %f" %(errorCount/float(numberOfTestFiles))) ``` ##### 注意点 1. 要使用 `listdir()` 函数,需要从 `os` 模块导入,但是不要将 `os` 模块中将所有内容都导入,以下代码 ``` {.python} from os import * ``` 会导致 `open()` 函数不可用 2. 注意 'labels' 从字符串中导入时,将字符串强转为 `int` 型 </pre>

posted @ 2016-03-24 11:47  CuriousBull  阅读(89)  评论(0编辑  收藏  举报