机器学习实战第二章——K-近邻算法总结( Python3 )
零 引言
因本人大学期间读的是商科专业,毕业一年后,才开始接触数据相关知识,并转至数据分析师岗。而在学习数据的道路上,越发认为自己的理想岗位是算法岗,目前正在积极的准备中。好记性不如烂笔头,故,会总结前往算法岗的路上所学的知识。
有关机器学习实战的总结,每个算法我都尽量分为以下四部分:
1、算法的原理及实现思路。
2、算法所需数学知识。
3、算法源码解析。
4、补充知识(用于理解算法)。
5、总结。
一 、K-近邻算法的原理与实现思路
1.1 K-近邻算法是什么?
我接触的第一个机器学习模型是Logistic regression,当时被告知这是机器学习最适合入门的模型,以至于让我误认为它就是机器学习里最简单的模型,直到我开始接触K-近邻……
K-近邻的原理比较容易理解,即是给定一个训练数据集,对未分类的变量(即预测集),在训练数据集中找到与未分类变量距离最短的K个变量,这K个变量的多数属于某个类,就把未分类变量分类到这个类中。
1.2 K-近邻算法的实现思路
算法的原理需要与它实现思路配合才能真正理解该算法。
实现思路:
(1)计算已知类别数据集中的点与当前点之间的距离;(欧式距离,第二部分会提及)
(2)按照距离递增次序排序;
(3)选取与当前点距离最小的k个点;
(4)确定前k个点所在类别的出现频率;
(5)返回前k个点所出现频率最高的类别作为当前点的预测分类。
二、算法所需的数学知识
K-近邻所需的数学:向量、欧式距离
计算向量xA和xB之间的距离:
三、K-近邻算法源码分析
3.1 K-近邻算法实现:
-*- coding: UTF-8 -*-
#含①②这些数字标识的为第四部分补充知识点
#creatDataSet函数说明:创建训练数据集
def creatDataSet():
group=np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) #①补充知识
labels=['A','A','B','B'] #分类变量A与B
return group,labels
group,labels=creatDataSet() #变量赋值
#classify0函数说明:实现K-近邻算法
def classify0(inX,dataSet,labels,k):
#inX预测数据,dataSet训练数据,labels分类变量,k选择多少个邻居
dataSetSize=dataSet.shape[0]
#返回矩阵的行数,shape[1]返回列
diffMat=np.tile(inX,(dataSetSize,1))-dataSet
#将inX的行数与dataSet一致然后相减(前提inX与dataSet列数一致)
#②np.tile
sqDiffMat=diffMat**2
sqDistances=sqDiffMat.sum(axis=1)
#矩阵.sum(axis=1)行相加.(axis=0列相加)
distances=sqDistances**0.5
sortedDistIndicies=distances.argsort()
#将距离从小到大排序,输出对应索引
#③np.argsort()
classCount={}
for i in range(k): #k-近邻的k,用于决定选择多少个邻居
voteIlabel=labels[sortedDistIndicies[i]] #将类别标签赋值
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1 #计算各类别出现的频次 ④字典添加元素的方法
soredClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #将类别依据频次排序,⑤operator.itemgetter
return soredClassCount[0][0] #输出分类结果
#结果测试
classify0([0,0],group,labels,3)
#输出结果 B
3.2 使用K-近邻居算法改进约会网站配对效果
3.21 数据转换:
背景简述:海伦希望通过算法将在线约会网站推荐人选分为:不喜欢的人、魅力一般的人、极具魅力的人。
数据模板:数据字段名依次为:每年获得飞行常客里程数、玩视频游戏所耗时间百分比、每周消费的冰淇淋公升数、分类:1:不喜欢的人、2:魅力一般的人、3:极具魅力的人。
可看出数据比较乱,包含\t,\n等,不符合我们模型所需数据格式(array格式),所以因此需要通过python清洗数据格式。
源码解析:
-*- coding: UTF-8 -*-
#file2matrix函数说明:清洗数据并将数据转换为模型所需array
def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines())
#读取文件的行数。
returnMat = np.zeros((numberOfLines,3))
#准备一个与训练矩阵行(既不包含目标变量1、2、3)列相同的零矩阵
classLabelVector = []
#用于存目标变量
fr = open(filename)
#重新打开文件,因为上面fr.readlines()使用之后,fr会变回空列表
index = 0 #行索引
for line in fr.readlines():
line = line.strip() #strip()默认去除空格
listFromLine = line.split('\t') #依据'\t'分割整行数据,让它变成列表
returnMat[index,:] = listFromLine[0:3] #将文件转换为矩阵的办法,[0:3]可看出目标变量不转换
classLabelVector.append(int(listFromLine[-1]))#将目标变量放入空列表中
index += 1
return returnMat,classLabelVector #输出最终array与对应的分类变量
3.22 归一化数据
如上图所示,我们假设序号为3是我们的未知分类样本,而序号4是我们训练样本,那他们之前的距离为:
我们很容易发现,上面方程中数字差值最大的属性对计算结果影响最大,也就是说每年获得的飞行常客里程数的影响远大于玩视频游戏所消耗时间与每周消费冰淇淋公升数的影响。但在你无法明确哪个特征更重要时,应默认所有特征同等重要,那解决这个问题办法就是归一化。
归一化公式:
源码解析:
-*- coding: UTF-8 -*-
#autoNorm函数说明:归一化数据
def autoNorm(dataSet):
minVals = dataSet.min(0)
#矩阵行最小值
maxVals = dataSet.max(0)
#矩阵行最大值
ranges = maxVals - minVals
#公式中的(max-min)
normDataSet = np.zeros(np.shape(dataSet))
#建立一个形状为目标矩阵相同的零矩阵(这步骤去掉程序也能跑)
m = dataSet.shape[0]
normDataSet = dataSet - np.tile(minVals, (m,1))#相当于公式里的(oldvalue - min)
normDataSet = normDataSet/np.tile(ranges, (m,1)) #数据归一化
return normDataSet, ranges, minVals #返回归一化数据,极值差,矩阵数值最小行
3.33 测试算法
当把算法所需数据都准备好后,终于可以开始使用K-近邻算法并测试我们的算法有没用。这里我们用比较简单的指标:错误率 来测试。
-*- coding: UTF-8 -*-
#datingClassTest函数说明:算法测试
def datingClassTest(dataSet):
hoRatio = 0.30
#取30%作为测试集
datingDataMat,datingLabels = file2matrix(dataSet)
#读取并转换原始数据
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) #使用K-近邻算法
print ("预测结果: %d, 真实结果: %d" % (classifierResult, datingLabels[i]))
if (classifierResult != datingLabels[i]):
errorCount += 1.0
print ("总错误率 is: %f" % (errorCount/float(numTestVecs)))
print (errorCount)
最终输出:预测300个,错25个,这准确率还是相当OK的。
四、补充知识
(1)标识①:Numpy的数组类ndarray,称array。一开始,我将它理解为线性代数的矩阵,但令我不解的是,它的四则运算又与线性代数不同。后来我查了《利用python进行数据分析》的Numpy篇才找到了答案:
(2)np.tile:函数原型:numpy.tile(A,reps) #简单理解是此函数将A进行重复输出,A可以是:array,list,tuple,dict,matrix以及基本数据类型int,string,float以及bool类型,reps的类型可以是tuple,list,dict,array,int,bool,但不可以是float,string,matrix类型。例子:
(3)np.argsort: 将数组中的元素从小到大排列,提取其对应的index(索引),然后输出index
(4)给字典添加元素的方法:
(5)operator.itemgetter函数: operator模块提供的itemgetter函数用于获取对象的哪些维的数据
五、总结
这块引用了“http://www.cnblogs.com/pinard/p/6061661.html”,写得很全。
KNN的主要优点有:
1) 理论成熟,思想简单,既可以用来做分类也可以用来做回归
2) 可用于非线性分类
3) 训练时间复杂度比支持向量机之类的算法低,仅为O(n)
4) 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
5) 由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
6)该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分
KNN的主要缺点有:
1)计算量大,尤其是特征数非常多的时候
2)样本不平衡的时候,对稀有类别的预测准确率低
3)KD树,球树之类的模型建立需要大量的内存
4)使用懒散学习方法,基本上不学习,导致预测时速度比起逻辑回归之类的算法慢
5)相比决策树模型,KNN模型可解释性不强
上一篇: 时间戳与格式化时间之间的相互转换