1.概述

1.1 原理:(测量不同的特征值之间的距离进行分类)

    存在样本数据集合,即训练样本集,并且样本集中的每个数据都存在多个特征和标签,即我们知道样本数据和其所属分类,在我们输入没有标签的新数据后,将新数据的每个特征和样本集中的数据对应的特征进行比较,然后根据相应算法(本节选择的是欧氏距离)提取与样本集中特征最相近数据得分类标签。一般选择与样本集中最相近的前k个数据,即k-近邻。最后,选择k个最相似数据中出现次数最多的分类最为新数据的分类。

  • 优点: 精度高、对异常值不敏感、无数据输入假定(???)
  • 缺点:计算复杂度高,需要大量存储空间,耗时,无法给出任何数据的基础结构信息(???)
  • 适用数据范围:数值型(目标变量从无限的数值集中取值,主要做回归分析)和标称型(目标变量从有限的数值集中取值,主要做分类)

1.2 KNN算法

k近邻算法的一般流程

  • 收集数据:可以使用任何方法。
  • 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
  • 分析数据:可以使用任何方法。
  • 训练算法:此步骤不适用于k近邻算法。
  • 测试算法:计算错误率。
  • 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。

伪代码:

 

利用一组测试数据:

1 from numpy import *
2 import operator
3 
4 def createDataSet():
5     group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
6     labels = ['A','A','B','B']
7     return group, labels

 在jupyter notebook中进行编辑:

K-近邻算法:

使用欧氏距离公式,计算两个向量点xA和xB之间的距离:

如果数据集存在4个特征值,则点(1, 0, 0, 1)与(7, 6, 9, 4)之间的距离计算为:

 1 def classify0(inX, dataSet, labels, k):
 2     '''
 3     :param inX: 用于分类的向量,即新数据
 4     :param dataSet: 训练样本集,每个特征的值
 5     :param labels: 训练样本的标签向量
 6     :param k: 选择的k个最近的数据
 7     :return: 最终的分类标签,即发生频率最高的标签
 8     '''
 9     dataSetSize = dataSet.shape[0]  # 矩阵行数,即样本个数
10     diffMat = tile(inX, (dataSetSize,1)) - dataSet  # np.tile([0,0],[4,1]) 将[0,0]复制为4行1列,对应位置相减
11     sqDiffMat = diffMat**2
12     sqDistances = sqDiffMat.sum(axis=1)  # 按列求和,即求和后,行数不变,列为1
13     distances = sqDistances**0.5
14     sortedDistIndicies = distances.argsort()    # 返回数组从小到大排序的索引
15     classCount={}
16     # 得到前k个最近的分类标签,以及出现的频率
17     for i in range(k):
18         voteIlabel = labels[sortedDistIndicies[i]]
19         classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
20     # 将classCount字典分解为元组,并按照第二个元素对元组进行逆序排序
21     sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
22     # 返回频率最高的标签
23     return sortedClassCount[0][0]

 

预测数据所在分类:

2. 实例

2.1 改进约会网站的配对效果

(1)数据准备:从文本文件中解析数据

前三列为特征数据,最后一列为类别(标签)数据

 1 def file2matrix(filename):
 2     '''
 3     :param filename:
 4     :return: 特征值和类别值
 5     '''
 6     fr = open(filename)
 7     arrayOLines = fr.readlines()
 8     numberOfLines = len(arrayOLines)         # get the number of lines in the file
 9     returnMat = zeros((numberOfLines,3))        # prepare matrix to return
10     classLabelVector = []                       # prepare labels return
11     index = 0  # 行号
12     for line in arrayOLines:
13         line = line.strip()   # strip()默认移除字符串头尾指定字符
14         listFromLine = line.split('\t')  # 按一个tab键进行划分
15         returnMat[index,:] = listFromLine[0:3]
16         classLabelVector.append(int(listFromLine[-1]))
17         index += 1
18     return returnMat,classLabelVector

 测试结果:

 

(2)分析数据:用matplotlib画散点图

没有使用样本分类的特征值:

 

使用第二三列的特征:

使用第一二列的特征:

 

(3)准备数据:归一化数据

如果想要计算样本3和样本4之间的距离,可以使用下面的方法:

我们很容易发现,上面方程中数字差值最大的属性对计算结果的影响最大

在处理这种不同取值范围的特征值时,我们通常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:

 newValue = (oldValue - min)/(max - min)

其中min和max分别是数据集中的最小特征值和最大特征值。

 1 # 归一化特征值
 2 def autoNorm(dataSet):
 3     '''
 4     :param dataSet:
 5     :return: normDataSet:归一化后的特征值
 6             ranges:每列最大值和最小值的差值
 7             minVals:每列的最小值
 8     '''
 9     minVals = dataSet.min(0)  # axis = 0 列/垂直;axis = 1 行/水平
10     maxVals = dataSet.max(0)
11     ranges = maxVals - minVals
12     normDataSet = zeros(shape(dataSet))
13     m = dataSet.shape[0]  # 矩阵行数
14     normDataSet = dataSet - tile(minVals, (m, 1))
15     normDataSet = normDataSet/tile(ranges, (m, 1))   # element wise divide
16     return normDataSet, ranges, minVals

 测试结果:

(4)测试算法:验证分类器

 1 # 约会网站的测试代码
 2 def datingClassTest():
 3     hoRatio = 0.10      # 10%的测试数据
 4     datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')       #load data setfrom file
 5     normMat, ranges, minVals = autoNorm(datingDataMat)
 6 
 7     # random.shuffle(normMat)  # (自己加的)按行打乱,即可随机选择测试数据,
 8     # 这样不行,datingLabels也要同时打乱,并且相对应
 9 
10     m = normMat.shape[0]
11     numTestVecs = int(m*hoRatio)
12     errorCount = 0.0
13     for i in range(numTestVecs):
14         classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 5)
15         print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i])
16         if classifierResult != datingLabels[i]:
17             errorCount += 1.0
18     print "the total error rate is: %f" % (errorCount/float(numTestVecs))
19     print errorCount

 测试结果:

(5)使用算法:构建完整可用系统

 1 # 约会网站测试程序
 2 def classifyPerson():
 3     resultList = ["不喜欢", "魅力一般", "极具魅力"]
 4     percentTats = float(raw_input(unicode('玩视频游戏所耗时间百分比: ','utf-8').encode('gb2312')))
 5     ffMiles = float(raw_input(unicode("每年获取的飞机常客里程数: ",'utf-8').encode('gb2312')))
 6     iceCream = float(raw_input(unicode("每周消费的冰淇淋公斤数: ",'utf-8').encode('gb2312')))
 7     datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
 8     normMat, ranges, minVals = autoNorm(datingDataMat)
 9     inArr = array([ffMiles, percentTats, iceCream])
10     classifierResult = classify0((inArr - minVals)/ranges, normMat, datingLabels, 3)
11     print "你觉得这个男的怎么样:", resultList[classifierResult - 1]

 测试结果:

 

2.2 手写识别系统

(1) 准备数据:将图像转为测试向量

  目录trainingDigits中包含了大约2000个例子,如下图所示,每个数字大约有200个样本;目录testDigits中包含了大约900个测试数据。我们使用目录trainingDigits中的数据训练分类器,使用目录testDigits中的数据测试分类器的效果。

  为了使用前面两个例子的分类器,我们必须将图像格式化处理为一个向量。我们将把一个32x32的二进制图像矩阵转换为1x1024的向量。

1 def img2vector(filename):
2     returnVect = zeros((1, 1024))
3     fr = open(filename)
4     for i in range(32):
5         lineStr = fr.readline()
6         for j in range(32):
7             returnVect[0, 32*i+j] = int(lineStr[j])
8     return returnVect

测试结果:

(2)测试算法:使用k近邻算法识别手写数字

 1 # 测试代码
 2 def handwritingClassTest():
 3     hwLabels = []
 4     trainingFileList = listdir('trainingDigits')           # os.listdir()列出给定目录的文件名load the training set
 5     m = len(trainingFileList)
 6     trainingMat = zeros((m, 1024))
 7     for i in range(m):
 8         fileNameStr = trainingFileList[i]
 9         fileStr = fileNameStr.split('.')[0]     # 文件名格式为:5_124.txt ,按“.”分隔,取第一个
10         classNumStr = int(fileStr.split('_')[0])   # 实际的数字
11         hwLabels.append(classNumStr)  # hwLabels实际数字列表
12         trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
13     testFileList = listdir('testDigits')        #iterate through the test set
14     errorCount = 0.0
15     mTest = len(testFileList)
16     for i in range(mTest):
17         fileNameStr = testFileList[i]
18         fileStr = fileNameStr.split('.')[0]     #take off .txt
19         classNumStr = int(fileStr.split('_')[0])
20         vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
21         classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
22         print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
23         if classifierResult != classNumStr:
24             errorCount += 1.0
25     print "\nthe total number of errors is: %d" % errorCount
26     print "\nthe total error rate is: %f" % (errorCount/float(mTest))

 

 测试结果:

 

posted on 2017-12-21 20:58  夏小爱  阅读(258)  评论(0编辑  收藏  举报