参考资料
《机器学习实战》,Machine Learning in Action,本文中简称MLiA
《机器学习》周志华,本文简称西瓜书
《Web安全之机器学习》刘焱著,本文中简称WSML(Web Security in Machine Learning,该英文翻译只为记录方便,是本人杜撰的,仅限本系列文章使用)
转载请注明出处:http://blog.youkuaiyun.com/rosetta/article/details/79179004
前言
因为之前不知道怎么把机器学习的知识用起来,所以心理很没底,看了近一年的数学基础,感觉也没有什么长进,而看了《机器学习实战》第2章k-近邻算法后发现,原来机器学习也可以这么简单。
本算法真不需要什么高深的数学公式,只要会一些基础的矩阵知识就可以了。代码里Numpy ndarray相关的东西看起来挺复杂的,但打出来调试后也就不会觉得难了。
另外我发现了自己学习的方法(其实原来也发现了,只是没总结),先跟着书把代码敲一遍(或者直接复制粘贴),这个时候不要去看书上关于代码的解释,因为看不懂,看不懂会影响学习心情,所以不要看。先把代码运行一遍看结果,运行成功后再看不理解的代码,边看边调试,比看书本上关于代码的解释效果好多了。
本次学习以实践为主,理论为辅。为了加深对KNN算法的理解,我学了以下内容,虽然很多,但我相信如果大家对以下内容都理解了,那么KNN算法也就掌握了。主要内容有以下五,我会分五篇文章完成学习记录。
- 使用KNN算法对约会对象魅力程度分类
- 使用sklearn库中的KNN算法对约会对象魅力程度分类
- 使用KNN算法完成手写识别系统
- 使用KNN算法完成西瓜分类
- 使用KNN算法检测Linux系统异常操作
本篇文章先描述K-近邻算法核心思想,再记录使用KNN算法对约会对象魅力程度分类的代码的理解。
K-近邻算法
所谓的k-近邻算法(K-Nearest Neighbor,简称KNN)是这样的,首先要有一批已知的样本数据的特征(比如MLiA举的交友例子,交友对象的年飞行里程、玩游戏占的时间比和周消费的冰淇淋公升数),并且知道这些数据的标签(不喜欢的人、魅力一般的人和极具魅力的人),然后输入不带标签的新数据(知道这个人的年飞行里程、玩游戏占的时间比和周消费的冰淇淋公升数,但不知道它属于哪一类),拿这个新数据和样本数据比较,算出和新数据最相似的k个数据(k可自定义),然后看这k个数据中出现最多的类型,新数据就属于这一类。
所以整体思想是很简单的,但这里面会延伸出三个问题:
1. 新数据和样本数据距离计算公式是怎样的?
2. 如果某一项属性数据太大,这样在计算距离时其它属性就不起作用了,但是其它属性也是一样重要的,那怎么办?这就需要做归一化处理。
3. 如何测试算法的准确性。这里会拿样本数据的前x%用于作为直接的样本(因为k-近邻算法没有训练数据概念,这里姑且叫它训练数据),后面的1-x%数据拿来测试(叫测试数据)。
相关概念
本节内容可先跳过,如后续看的不明白可回来查看相关概念。
KNN算法中的训练数据和一般意义上的训练数据不太一样。因为在KNN中实际上是不需要训练数据的,只需要知道一些已知的带标签的数据就可以了,后续新来的数据只要和这些已知标签的数据做运算就可以了。
所以这里的相关解释如下:
样本数据
所有带标签的数据。其中一部分拿来用做训练数据(比如80%),一部分拿来当测试数据(比如剩下的20%)。
训练数据
带标签的标本数据,实际上是给KNN算法本身使用的。当测试数据或待预测数据来了时和这些数据做运算使用。
测试数据
测试数据也是带标签的,是在样本数据中预留出来的,用以检测算法正确率的。当算法已经设计好后,给算法传入这些测试数据,然后取得算法返回的预测结果,并把这些预测结果和已知的标签做对比,从而计算出算法的正确率。
使用KNN算法对约会对象魅力程度分类
先来看下最终效果,有一个直观的认识。
最终效果
最终效果如下。输入年飞行里程、玩游戏占的时间比和周消费的冰淇淋公升数,程序输出这个人的魅力程度。
代码分析
代码分析请直接看注释。
def classifyPerson():
resultList = ['没有魅力', '魅力一般', '极具魅力']
ffMiles = float(input("每年飞行公里数?"))
percentTats=float(input("打游戏耗费时间的百分比?"))
iceCream = float(input("每周消费的冰激凌?"))
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
inArr = np.array([ffMiles,percentTats,iceCream])
classifierResult = classify0((inArr-minVals)/ranges, normMat, datingLabels, 3)
print("这个人:", resultList[classifierResult-1])
#从文件中获取数据,并格式化成Numpy数组。
#returnMat[]数组用于存放样本的属性。
#classLabelVector[]用于存放样本的类别
def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines()) #1000
returnMat = np.zeros((numberOfLines,3)) #创建numberOfLines行,3列矩阵,初始化为0.
classLabelVector = [] #用于存放第四列的的值。
fr = open(filename)
index = 0
for line in fr.readlines():
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3] #因为index是会自增的,所以数据会往returnMat上增。
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat,classLabelVector
# returnMat={ndarray} [[ 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]]
#classLabelVector={list}[3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1,……]
#归一化处理公式:newValue=(oldValue-min)/(max-min)
def autoNorm(dataSet):
#dataSet是1000行3列的数据。
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges =