zoukankan      html  css  js  c++  java
  • 机器学习实战-K-近邻算法(kNN)

      k-近邻算法(kNN)---它的工作原理是:存在一个样本数据集合,也称做训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每个数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据得到分类。

    1. 说明一下,文本数据格式:文本名字为:datingTestSet2.txt,该文件我放在项目里面的python文件同级

    2. 使用python转化文件

     1 def file2matrix(filename):
     2     """
     3     Cynthia:解析文本数据,将属性数据放在returnMat,将标签放在classLabelVector
    4 导入训练数据 5 :param filename: 数据文件路径 6 :return: 数据矩阵returnMat和对应的类别classLabelVector 7 """ 8 # 打开文件 9 fr = open(filename, 'r') 10 # 获得文件中的数据行的行数 11 numberOfLines = len(fr.readlines()) 12 # 生成对应的空矩阵 13 # 例如:zeros(2,3)就是生成一个 2*3 的矩阵,各个位置上全是 0 14 returnMat = zeros((numberOfLines, 3)) # prepare matrix to return 15 classLabelVector = [] # prepare labels return 16 fr = open(filename, 'r') 17 index = 0 18 for line in fr.readlines(): 19 # str.strip([chars]) --返回移除字符串头尾指定的字符生成的新字符串 20 line = line.strip() 21 # 以 ' ' 切割字符串 22 listFromLine = line.split(' ') 23 # 每列的属性数据,即 features 24 returnMat[index] = listFromLine[0: 3] 25 # 每列的类别数据,就是 label 标签数据 26 classLabelVector.append(int(listFromLine[-1])) 27 index += 1 28 # 返回数据矩阵returnMat和对应的类别classLabelVector 29 return returnMat, classLabelVector

    3. 数据归一化:

     1 def autoNorm(dataSet):
     2     """
     3     Desc:
     4         归一化特征值,消除属性之间量级不同导致的影响
     5     Args:
     6         dataSet -- 需要进行归一化处理的数据集
     7     Returns:
     8         normDataSet -- 归一化处理后得到的数据集
     9         ranges -- 归一化处理的范围
    10         minVals -- 最小值
    11 
    12     归一化公式:
    13         Y = (X-Xmin)/(Xmax-Xmin)
    14         其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到1的区间。
    15     """
    16     # 计算每种属性的最大值、最小值、范围
    17     minVals = dataSet.min(0)
    18     maxVals = dataSet.max(0)
    19     # 极差
    20     ranges = maxVals - minVals
    21     normDataSet = (dataSet - minVals) / ranges
    22     return normDataSet, ranges, minVals

    4. KNN分类的算法核心: 

     1 def classify0(inX, dataSet, labels, k):
    2
    """ 3 Desc: 4 kNN 的分类函数 5 Args: 6 inX -- 用于分类的输入向量/测试数据 7 dataSet -- 训练数据集的 features 8 labels -- 训练数据集的 labels 9 k -- 选择最近邻的数目 10 Returns: 11 sortedClassCount[0][0] -- 输入向量的预测分类 labels 12 13 注意:labels元素数目和dataSet行数相同;程序使用欧式距离公式. 14 15 预测数据所在分类可在输入下列命令 16 kNN.classify0([0,0], group, labels, 3) 17 """ 18 19 # -----------实现 classify0() 方法的第一种方式---------------------------------------------------------------------------------------------------------------------------- 20 # 1. 距离计算 21 #shape函数是查看矩阵或者数组的维数 22 dataSetSize = dataSet.shape[0] 23 # tile生成和训练样本对应的矩阵,并与训练样本求差 24 #tile是用于对某个序列进行重复。 25 #示例:tile([1,2],(2,2,3)),输出为array([[[1, 2, 1, 2, 1, 2],[1, 2, 1, 2, 1, 2]]  ,  [[1, 2, 1, 2, 1, 2],[1, 2, 1, 2, 1, 2]]    ]) 26 #(1)可以看到是先将[1,2]重复3次得到[1, 2, 1, 2, 1, 2], 27 #(2)再将[1, 2, 1, 2, 1, 2]重复2次得到[[1, 2, 1, 2, 1, 2],[1, 2, 1, 2, 1, 2]], 28 #(3)最后再将[[1, 2, 1, 2, 1, 2],[1, 2, 1, 2, 1, 2]]重复两次[[[1, 2, 1, 2, 1, 2],[1, 2, 1, 2, 1, 2]]  ,  [[1, 2, 1, 2, 1, 2],[1, 2, 1, 2, 1, 2]]]。 29 #总结:从上面的例子可以看出,tile(A,rep)按照rep里面从右到左的序列顺序对A进行重复,每对重复完一次,A就增加一个维度。 30 diffMat = tile(inX, (dataSetSize, 1)) - dataSet 31 #目的是计算出该测试点对训练集中的每个点的距离。 32 """ 33 欧氏距离: 点到点之间的距离 34 """ 35 # 取平方 36 sqDiffMat = diffMat ** 2 37 # 将矩阵的每一行相加 38 sqDistances = sqDiffMat.sum(axis=1) 39 # 开方 40 distances = sqDistances ** 0.5 41 # 根据距离排序从小到大的排序,返回对应的索引位置 42 # argsort() 是将x中的元素从小到大排列,提取其对应的index(索引),然后输出到y。 43 # print 'distances=', distances 44 sortedDistIndicies = distances.argsort() 45 # print 'distances.argsort()=', sortedDistIndicies 46 47 # 2. 选择距离最小的k个点 48 classCount = {} 49 for i in range(k): 50 # 找到该样本的类型 51 voteIlabel = labels[sortedDistIndicies[i]] 52 # 在字典中将该类型加一 53 # 字典的get方法 54 # 如:list.get(k,d) 其中 get相当于一条if...else...语句,参数k在字典中,字典将返回list[k];如果参数k不在字典中则返回参数d,如果K在字典中则返回k对应的value值 55 # l = {5:2,3:4} 56 # print l.get(3,0)返回的值是4; 57 # Print l.get(1,0)返回值是0; 58 # 感觉像Java中的键值对原理,键是这里的标签类型,值是标签出现的次数 59 classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    60 # 3. 排序并返回出现最多的那个类型
    61 # 字典的 items() 方法,以列表返回可遍历的(键,值)元组数组。 62 # 例如:dict = {'Name': 'Zara', 'Age': 7} print "Value : %s" % dict.items() Value : [('Age', 7), ('Name', 'Zara')] 63 # sorted 中的第2个参数 key=operator.itemgetter(1) 这个参数的意思是先比较第几个元素 64 # 例如:a=[('b',2),('a',1),('c',0)] b=sorted(a,key=operator.itemgetter(1)) >>>b=[('c',0),('a',1),('b',2)] 可以看到排序是按照后边的0,1,2进行排序的,而不是a,b,c 65 # b=sorted(a,key=operator.itemgetter(0)) >>>b=[('a',1),('b',2),('c',0)] 这次比较的是前边的a,b,c而不是0,1,2 66 # b=sorted(a,key=opertator.itemgetter(1,0)) >>>b=[('c',0),('a',1),('b',2)] 这个是先比较第2个元素,然后对第一个元素进行排序,形成多级排序。 67 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) 68 return sortedClassCount[0][0]

    5. 用约会网站的数据进行训练中的数据标签测试

     1 #分类器针对约会网站的进行测试代码
     2 def datingClassTest():
     3     """
     4     Desc:
     5         对约会网站的测试方法,并将分类错误的数量和分类错误率打印出来
     6     Args:
     7         None
     8     Returns:
     9         None
    10     """
    11     # 设置测试数据的的一个比例(训练数据集比例=1-hoRatio)
    12     hoRatio = 0.1  # 测试范围,一部分测试一部分作为样本
    13     # 从文件中加载数据
    14     datingDataMat, datingLabels = file2matrix("datingTestSet2.txt")  # load data setfrom file
    15     # 归一化数据
    16     normMat, ranges, minVals = autoNorm(datingDataMat)
    17     # m 表示数据的行数,即矩阵的第一维
    18     m = normMat.shape[0]
    19     # 设置测试的样本数量, numTestVecs:m表示训练样本的数量
    20     numTestVecs = int(m * hoRatio)
    21     print('numTestVecs=', numTestVecs)
    22     errorCount = 0
    23     for i in range(numTestVecs):
    24         # 对数据测试
    25         classifierResult = classify0(normMat[i], normMat[numTestVecs : m], datingLabels[numTestVecs : m], 3)
    26         print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))
    27         errorCount += classifierResult != datingLabels[i]
    28     print("the total error rate is: %f" % (errorCount / numTestVecs))
    29     print(errorCount)

    6. 做预测

     1 def classifyPerson():
     2     resultList = ['not at all','in small doses', 'in large doses']
     3     percentTats = float(input("percentage of time spent playing video games?"))
     4     ffMiles = float(input("frequent flier miles earned per years?"))
     5     iceCream = float(input("liters of ice cream consumed per year?"))
     6     datingDataMat, datingLabels = file2matrix("datingTestSet2.txt")
     7     normMat, ranges, minVals = autoNorm(datingDataMat)
     8     inArr = array([ffMiles, percentTats, iceCream])
     9     classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
    10     print("you will probably like this person:",resultList[classifierResult-1])

    注:python3中的控制台交互是input

    python2的是raw_input 

    控制台测试结果:

     kNN代码测试已完毕。

    本人github代码链接:https://github.com/CynthiaWendy/Machine-Learning-in-Action-KNN



    项目案例2:构造k-近邻分类器的手写识别系统

      说明:这里构造的系统只能识别数字0到9.如下图所示:

      需要识别的数据已经使用图像处理软件,处理成具有相同的色彩和大小:宽高是32像素*32像素的黑白图像。尽管采用的文本格式存储图像不饿呢过有效地利用内存空间,但是为了方便理解,还是将图像转化为文本格式。

     1. 数据准备:我们有两个数据集文件,一个是训练数据trainingDigits(大约2000个例子,每个数字大约有200个样本),一个是测试数据(大约有900个测试数据)。这两组数据没有覆盖。

      我们将把一个32*32的二进制图像矩阵转化为1*1024的向量,这样我们上面项目1的knn的分类器就可以处理数字图像信息。

     1 def img2vector(filename):
     2     '''
     3     该函数创建1*1024的Numpy数组,然后打开给定的文件,循环读出文件的前32行,
     4     并将每行的头32个字符值存储在Numpy数组中,最后返回数据。
     5     :param filename: 
     6     :return: 
     7     '''
     8     returnVect = zeros((1,1024))
     9     fr = open(filename)
    10     for i in range(32):
    11         lineStr=fr.readline()
    12         for j in range(32):
    13             #将信息取出,放在1*1024向量相应的位置上
    14             returnVect[0,32*i+j]=int(lineStr[j])
    15     return returnVect

    2. 项目1中的knn核心函数被使用在测试中。

    3. 测试代码

     1 def handwritingClassTest():
     2     """
     3         Desc:
     4             手写数字识别分类器,并将分类错误数和分类错误率打印出来
     5         Args:
     6             None
     7         Returns:
     8             None
     9         """
    10     hwLabels=[]
    11     #装入训练数据
    12     trainingFileList=os.listdir('trainingDigits')
    13     #得到该文件夹的文件个数
    14     m=len(trainingFileList)
    15     trainingMat=zeros((m,1024))
    16     for i in range(m):
    17         fileNameStr=trainingFileList[i]
    18         #得到文件的名字
    19         fileStr=fileNameStr.split('.')[0]
    20         #得到文件名字的分隔
    21         classNumStr=int(fileStr.split('_')[0])
    22         #将类别存在数组中
    23         hwLabels.append(classNumStr)
    24         #将每个文件的数据以1*1024的形式保存
    25         trainingMat[i]=img2vector("trainingDigits/%s" % fileNameStr)
    26     #导入测试数据
    27     testFileList=os.listdir('testDigits')
    28     errorCount=0.0
    29     mTest=len(testFileList)
    30     for i in range(mTest):
    31         fileNameStr=testFileList[i]
    32         fileStr=fileNameStr.split('.')[0]
    33         classNumStr=int(fileStr.split('_')[0])
    34         vectorUnderTest=img2vector("testDigits/%s" % fileNameStr)
    35         classifierResult=classify0(vectorUnderTest, trainingMat, hwLabels,3)
    36         print("the classifier came back with: %d, the real answer is: %d" % (classifierResult,classNumStr))
    37         if (classifierResult!=classNumStr): errorCount+=1.0
    38     print("
    the total number of errors is: %d" % errorCount)
    39     print("
    the total error rate is: %f" % (errorCount/float(mTest)))

    运行结果:

    该项目的代码及数据放在本人的github上:欢迎指导。

    https://github.com/CynthiaWendy/Machine-Learning-in-Action-KNN1

  • 相关阅读:
    Zend Framework 2.1.5 中根据服务器的环境配置调用数据库等的不同配置
    在基于 Eclipse 的 IDE 中安装和使用 Emmet(ZenCoding)
    【翻译】Emmet(Zen Coding)官方文档 之六 自定义 Emmet
    【翻译】Emmet(Zen Coding)官方文档 之二 缩写
    【翻译】Emmet(Zen Coding)官方文档 之七 一览表
    【翻译】Emmet(Zen Coding)官方文档 之三 CSS 缩写
    【翻译】Emmet(Zen Coding)官方文档 之四 动作
    【翻译】Emmet(Zen Coding)官方文档 之一 web 编程的必备工具
    Zend Framework 2 时区设置警告问题的解决
    【翻译】Emmet (Zen Coding) 元素类型
  • 原文地址:https://www.cnblogs.com/CynthiaWendy/p/11057789.html
Copyright © 2011-2022 走看看