《机器学习实战》学习笔记(一):KNN算法(附本书数据集下载)

目录

1.1KNN算法概述 (附数据集)   

1.2 KNN算法实例

1.3 使用KNN算法改进约会网站的配对效果


1.1KNN算法概述 (附数据集)   

       正在看《机器学习实战》一书,但目前正在实习,因此只能在周末时候来写一下相关学习笔记,今天算是第一天开始写博客,写的不太好大家见谅一哈,今后会加油的。首先来简单介绍一下K-近邻(KNN)算法,这是一种通过测量不同特征值之间的距离,从而进行分类的一种算法,算法的工作原理是:存在一个训练样本集,并且该样本集中每个数据都存在标签,即了解样本集中每一数据的分类标签;在接收到没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后利用该算法提取样本集中特征最为相似数据(也成为最近邻)的分类标,由于只选择训练样本集中前k个最相似的数据(k一般小于20),因此这k个最相似数据中出现次数最多的分类,也就为新数据的分类。

        KNN算法的一般流程为:1.收集数据;2.准备数据:用于进行距离计算所需要的数值,最好是结构化的数据格式;3.分析数据;4.训练算法(此步骤不适用于K-近邻算法,可以忽略);5.测试算法:计算错误率;6.使用算法:首先需要输入样本数据和结构化的数据格式,然后运行k-近邻算法判定输入数据属于哪个分类,然后对计算出的分类执行后续的处理

        由于书中的相关代码大多是基于python2的,为方便学习,我对书中的一些代码进行了调整,使其适应python3,代码运行的环境为anaconda中的spyder,我在下面也会简单介绍如何使用这个IDE来完成书中的实战项目。下面的链接就是书中相关数据集的下载:链接:https://pan.baidu.com/s/1oZ3mcNpryEbuUShT88s2zQ 提取码:2xw0

1.2 KNN算法实例

首先我们在anaconda官网上下载anaconda,然后找到spyder,为什么推荐用spyder来写呢,是因为spyder可以像matlab一样清楚的在variable explorer中描述变量。在打开spyder后,新建一个file就可以开始编写第一个KNN算法了,然后将写好的程序以KNN.py的方式命名,保存在一个文件夹下,同时将书中所提供的数据集同时保存在这个文件夹下即可。

在做好上面的准备工作后,再次说明一下KNN算法的实质,所谓KNN算法的实质,应该就是一个欧式距离问题,A->B的距离为5,A->C的距离为10,显然,若要对A进行分类的话,最好是将其分类到B类中,当然这个描述是最近邻算法,既然叫K近邻肯定要有一个K了,因此K近邻算法的步骤如下:

  1. 计算已知类别数据集中的点与当前点之间的距离(欧式距离);
  2. 按照距离递增次序对已知类别数据数据集中的点进行排序,并选取与当前点距离最小的k个点,k一般小于20;
  3. 计算出这k个点的类别出现的频率;
  4. 返回这k个点出现频率最高的类别,即为当前点的分类类别。

接下根据书上给的一个小例子,通过创建一个数据集来简单说明一下,创建数据集的代码如下:

from numpy import *    #导入科学计算包Numpy
import operator        #导入运算符模块
import os

def createDataSet():
	group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])  #这是四组数据,每组有两个我们已知的属性或者特征值;
    #group矩阵每行包含一个不同的数据,可以把它想象为某个日志文件中不同的测量点或入口
	labels = ['A','A','B','B'] #向量labels包含了每个数据点的标签信息,labels包含的元素个数等于group矩阵行数
    #根据上面的对应关系,将数据点(1,1.1)定义为类别A等
	return group, labels

 为了测试数据集是否创建成功,我们先run一下这个KNN.py,然后输入下面代码:

import KNN
group,labels = KNN.createDataSet()
group   
output:array([[ 1. ,  1.1],
               [ 1. ,  1. ],
               [ 0. ,  0. ],
               [ 0. ,  0.1]])
labels
output:['A', 'A', 'B', 'B']

#若成功运行,则说明我们成功创建了变量group和变量labels

接下来我们写出KNN的分类算法classify0(),并返回分类结果:

def classify0(inX, dataSet, labels, k):  #classify0有4个输入参数:用于分类的输入向量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             #这部分代码计算向量点xA和xB之间的距离 d=根号下(xA0-xB0)平方+(xA1-xB1)平方
    sortedDistIndicies = distances.argsort() #对距离按照从小到大的顺序排序
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1   #确定前k个距离最小元素所在的主要分类,输入的k总时正整数
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse = True) #将classCount字典分解为元组列表,然后使用itemgetter方法,按照第二个元素的次序对元组进行排序,此处的排序为逆序
    #在python3报错,将iteritems改为items即可,iteritems是为python2环境中dict的函数
    return sortedClassCount[0][0]  #返回发生频率最高的元素标签

为了预测任一数据所在分类,可以在python提示符中输入下面指令:

import KNN
KNN.classify0([1,0],group,labels,3)
#结果应该是‘B’

这样就成功完成了一个KNN分类器。

1.3 使用KNN算法改进约会网站的配对效果

这是书中第一个实战项目,项目的背景是海伦一直使用约会网站寻找适合自己的约会对象,尽管约会网站会推荐不同的任选,但是她并不是喜欢推荐的每一个人,经过一番总结,她发现自己交往过的人可以进行如下分类:

  •  不喜欢的人(didntLike)
  •  魅力一般的人(smallDoses)
  •  极具魅力的人(largeDoses)

海伦收集约会数据已经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占一行,总共有1000行。

在datingTestSet.txt中,海伦所收集的样本数据主要包含以下3种特征:

  •  每年获得的飞行常客里程数
  • 玩视频游戏所消耗时间百分比
  •  每周消费的冰淇淋公升数

在介绍完项目背景后开始着手对数据进行处理,根据上面的1.2中的小例子,要想使用KNN算法classify0()进分类,classify()所接收的数据为dataSet特征矩阵和对应的labels,因此,对于所给数据集datingTestSet.txt,需要将其中的文本数据进行转换,将文本记录转换为特征矩阵dataSet的解析程序如下:

def file2matrix(filename):   #该函数的输入为文件名字符串,输出为训练样本矩阵和类标签向量
    fr = open(filename)      
    arrayOLines = fr.readlines()    #用于读取filename文件的所有行
    numberOfLines = len(arrayOLines)  #获取文件行数
    returnMat = zeros((numberOfLines,3))  #返回Numpy矩阵,解析完成的数据为numberOfLines行,3列数据
    classLabelVector = []  #返回的分类标签向量  
    index = 0
    for line in arrayOLines:    
        line = line.strip()     #使用line.strip()函数截取掉所有的空白符,s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
        listFromLine = line.split('\t')     #将line根据'\t'分隔符进行切片
        returnMat[index,:] = listFromLine[0:3]      #选取前三列元素,将它们存储到特征矩阵中
        classLabelVector.append(listFromLine[-1])      #将最后一列数据加入classLabelVector中,作为情感分类标签
        index += 1
    return returnMat,classLabelVector

运行KNN.py,并用下述代码进行测试:

import KNN
datingDataMat,datingLabels = KNN.file2matrix('datingTestSet2.txt')
datingDataMat
output:array([[  4.09200000e+04,   8.32697600e+00,   9.53952000e-01],
               [  1.44880000e+04,   7.15346900e+00,   1.67390400e+00],
               [  2.60520000e+04,   1.44187100e+00,   8.05124000e-01],
                ..., 
               [  2.65750000e+04,   1.06501020e+01,   8.66627000e-01],
               [  4.81110000e+04,   9.13452800e+00,   7.28045000e-01],
               [  4.37570000e+04,   7.88260100e+00,   1.33244600e+00]])


接下来,由于三个特征单位不同,且数字插值最大的属性对计算结果的影响最大,因此需要对其进行数据归一化的处理,在这里文中使用了一个autoNorm的函数,代码如下:

#归一化特征值
#核心公式是newValue = (oldValue-min)/(max-min)
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals -minVals
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals,(m,1))
    normDataSet = normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals

 这时候我们已经完成了大部分的工作了,需要通过一个测试算法来验证所写的KNN分类器,测试算法的代码如下:

def datingClassTest():
    hoRatio = 0.10
    datingDataMat,datingLabels = file2matrix('datingTestSet.txt')  #用file2mtrix读取datingTestSet.txt文件
    normMat,ranges,minVals = autoNorm(datingDataMat)    #使用autoNorm函数将其转换为归一化特征值
    m = normMat.shape[0]
    numTestVecs = int(m*hoRatio)     #计算测试向量的数量,决定了normMat向量中那些数据用于测试,那些数据用于分类器的训练样本
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:,],datingLabels[numTestVecs:m],3)
        print ( "分类类别:%s, 真实列表: %s" % (classifierResult,datingLabels[i]))
        if (classifierResult != datingLabels[i]) : errorCount +=1.0
    print ("错误率 :%f " % (errorCount/float(numTestVecs)))

 通过运行下面代码,发现对于给定datingTestSet.txt,KNN分类器的错误率仅有0.05

import KNN
KNN.datingClassTest()
output:
分类类别:largeDoses, 真实列表: largeDoses
分类类别:largeDoses, 真实列表: largeDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:smallDoses, 真实列表: smallDoses
分类类别:largeDoses, 真实列表: largeDoses
分类类别:largeDoses, 真实列表: didntLike
分类类别:largeDoses, 真实列表: largeDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:smallDoses, 真实列表: smallDoses
分类类别:smallDoses, 真实列表: smallDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:didntLike, 真实列表: didntLike
分类类别:largeDoses, 真实列表: largeDoses
分类类别:smallDoses, 真实列表: largeDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:smallDoses, 真实列表: smallDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:largeDoses, 真实列表: largeDoses
分类类别:largeDoses, 真实列表: largeDoses
分类类别:smallDoses, 真实列表: smallDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:largeDoses, 真实列表: didntLike
错误率 :0.050000 

最后一步,我们可以将上述算法构建成一个完整的程序,利用该程序,海伦通过输入约会网站上相应的信息,程序便会自动给出她对南方喜欢程度的预测值。

因此在KNN.py中继续添加下面代码:

def classifySystem():
    resultList = ['not at all','in small doses','in large doses']
    percentTats = float(input("percentaged of time spent playing video games?"))
    ffMiles = float(input("frequent flier miles earned per years?"))
    icecream = float(input("liters of ice cream consumed per year?"))
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = array([ffMiles, percentTats,icecream])
    classifierResult = int(classify0((inArr-\
                                  minVals)/ranges,normMat,datingLabels,3))
    print("You will probably like this person:",\
          resultList[classifierResult - 1])   

 此时我们完成了这样一个KNN分类器,通过在cmd中运行该程序,三个特征的输入值为(20,40000,5),得到的结果如下:


percentaged of time spent playing video games?20

frequent flier miles earned per years?40000

liters of ice cream consumed per year?5
You will probably like this person: in large doses

 

 

 

你可能感兴趣的:(《机器学习实战》学习笔记(一):KNN算法(附本书数据集下载))