1 说明
该书主要以原理简介+项目实战为主,本人学习的主要目的是为了结合李航老师的《统计学习方法》以及周志华老师的西瓜书的理论进行学习,从而走上机器学习的“不归路”。因此,该笔记主要详细进行代码解析,从而透析在进行一项机器学习任务时候的思路,同时也积累自己的coding能力。
正文由如下几部分组成:
1、实例代码(详细注释)
2、知识要点(函数说明)
3、调试及结果展示
2 正文
(1)准备:使用python导入数据
1、将如下内容写入kNN.py文件:
from numpy import *
import operator
def createDataSet():
group = array([[1.0,1.1],
[1.0,1.0],
[0 ,0 ],
[0 ,0.1]])
labels = ['A','A','B','B']
return group, labels
知识要点:
①operator模块:该模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。接下的代码会用到该模块中的一个非常重要的方法:itemgetter。
2、打开python交互式开发环境,执行以下命令并得到结果:
******
PyDev console: starting.
Python 3.6.7 |Anaconda, Inc.| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)] on win32
>>>import kNN
>>>group, labels = kNN.createDataSet()
>>>group
array([[1. , 1.1],
[1. , 1. ],
[0. , 0. ],
[0. , 0.1]])
>>>labels
['A', 'A', 'B', 'B']
(2)实施kNN分类算法
1、在kNN.py文件中添加如下代码,该函数用于k-近邻算法的实现,其中4个输入分别是:输入向量inX(欲进行分类的数据),输入的训练样本集dataSet,标签向量labels以及所选k值。具体实现如下代码所示:
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0]#获取训练样本集的行数,即样本个数
diffMat = tile(inX, (dataSetSize,1)) - dataSet#利用tile函数将inX向量构造成一个和dataset有相同行数列数的矩阵,并与之相减
sqDiffMat = diffMat**2#各个元素分别平方
sqDistances = sqDiffMat.sum(axis=1)#按列求和,即得到了每一个距离的平方
distances = sqDistances**0.5#各个元素开平方即得到了距离矩阵
sortedDistIndicies = distances.argsort()#把向量中每个元素进行排序,而它的结果是元素的索引形成的向量
classCount={
}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]#把按值大小顺序排列的欧氏距离索引list前k个对应的labels遍历出来
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#统计labels中各类出现的频次,以字典的形式输出
#分解为元组列表,operator.itemgetter(1)按照第二个元素的次序对元组进行排序,reverse=True是逆序,即按照从大到小的顺序排列
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
知识要点:
①tile():tile函数位于python模块 numpy中,其功能是重复某个数组,从而形成新的数组。
②argsort():tile函数位于python模块 numpy中,其功能是将目标数组中的元素从小到大排列,提取其对应的index(索引),然后输出。
③sorted():sorted函数是python的内置函数,用来做排序任务,该函数可以对list按一定的规则进行排序。
④items():Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。返回值类型为dict_items。
⑤itemgetter():operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号),其定义的是一个函数,通过该函数作用到对象上才能获取值。
2、我们假设现在有个待测试数据(0,0),k值选择3,下面就来预测一下该数据所在分类是什么,还是接着之前的python交互式开发环境,所得结果是B分类:
******
>>>kNN.classify0([0,0], group, labels, 3)
'B'
(3)案例1-使用k-近邻算法改进约会网站的配对效果
1、书中案例给定了3个维度的特征,共计1000组,存放在工程根目录下的datingTestSet2.txt文本文件下。在将特征数据输入到分类器之前,需要将待处理数据的格式改变为分类器可以接受的格式。案例中定义了file2matrix函数,该函数的输入为文本文件名字符串,输出为训练样本矩阵和类标签向量。具体实现代码如下:
def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines())#获取文件的行数
returnMat = zeros((numberOfLines,3))#构造返回的矩阵
classLabelVector = []#构造返回的labels列表
fr = open(filename)#此处为何还要开一次呢?
index = 0
for line in fr.readlines():
line = line.strip()#按行去除头尾字符、空白符(包括\n、\r、\t、' ',即:换行、回车、制表符、空格)
listFromLine = line.split('\t')#拆分字符串,通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list)
returnMat[index,:] = listFromLine[0:3]#通过切片操作抽取特征向量
classLabelVector.append(int(listFromLine[-1]))#获取labels
index += 1
return returnMat,classLabelVector
知识要点:
①open():open() 函数是python内置的file对象中的一个方法,用于打开一个文件,创建一个 file 对象,相关的方法才可以调用它进行读写。
②readlines():用于读取所有行(直到结束符 EOF)并返回列表,该列表可以由 Python 的 for… in … 结构进行处理。如果碰到结束符 EOF 则返回空字符串。
③strip():用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
④split():str.split(str="", num=string.count(str))。通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串。
2、在python交互开发环境中执行的命令和结果如下:
******
PyDev console: starting.
Python 3.6.7 |Anaconda, Inc.| (default, Oct 28 2018, 19:44:12) [MSC v.1915 64 bit (AMD64)] on win32
>>>from kNN import *
>>>datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
>>>datingDataMat
>>>array([[4.0920000e+04, 8.3269760e+00, 9.5395200e-01],
[1.4488000e+04, 7.1534690e+00, 1.6739040e+00],
[2.6052000e+04, 1.4418710e+00, 8.0512400e-01],
...,
[2.6575000e+04, 1.0650102e+01, 8.6662700e-01],
[4.8111000e+04, 9.1345280e+00, 7.2804500e-01],
[4.3757000e+04, 7.8826010e+00, 1.3324460e+00]])
>>>datingLabels[0:20]
[3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3]
此处我是重新打开了交互环境,如果你是按着书上的步骤一步一步做下来的,需要reload我们创建的kNN模块。而书中例程用的是python2,reload还是属于python内置的。如果我们用的是python3,那么就要通过下面的方式进行重载:
******
PyDev console: starting.
Python 3.6.7 |Anaconda,