《机器学习实战》笔记(一):K近邻

机器学习最近太火,本来不想跟风,但是看到alphaGo的表现和最近的业内新闻,深深感觉到不赶紧跟一跟就要被时代的浪潮拍死在沙滩上了……没太多时间潜心磨练代码,只能从经典《机器学习实战》开始跟着写写,一章一章慢慢过,然后记录一下自己的理解和碰到的问题。
事实上,我在大学里已经学过一门《模式识别》,算是机器学习的前身,那时的内容和现在的实际应用相比已经是垂垂老矣,但有的部分也可以算作是复习。整本书的示例代码是python2+numpy,但我觉得照书打一遍一点意思也没有,于是改用自己相对比较熟悉的python3+pandas(其实也不太熟,边百度能写出代码的水平)重构代码,顺便练习python。这里要吐槽一下作者的github,还专门放了个 machinelearninginaction3x的项目,我估计就是从原版fork出来还没来得及改吧,打开里面还是python2的代码。

你看到的是非授权版本!爬虫凶猛,请尊重知识产权!

转载请注明出处:http://conanwhf.github.io/2017/10/02/KNN/

访问原文「《机器学习实战》笔记(一):K近邻」获取最佳阅读体验并参与讨论

CH01:机器学习基础

第一章就没啥可说的了,介绍一点机器学习的概念。我的理解是机器学习并不是一种创造的过程,不能无中生有,它其实就是通过各种数据在学习怎么将事物分类。这个分类和学习都不是狭义上的,学习的数据可以没有正确答案(无监督学习),而分类目标可以不是单纯的类别(预测数值型数据)。简单来说,机器学习的目的就是通过已有数据构造一个分类器,来预测其他数据的结果。

CH02:k-近邻算法

算法

k-近邻(kNN,k-NearestNeighbor),顾名思义就是通过k个最近的邻居的身份来决定自己身份的分类方法。而所谓的邻居和自己的距离,是通过欧式距离来计算的。具体算法:

  1. 对所有已知数据归一化处理;
  2. 对n组(即每个邻居)已知数据(包含m个特征点数据)进行计算,得出这个邻居与待分类数据的欧式距离;
  3. 对得到的所有距离进行排序,取出前k个距离;
  4. 对前k个距离进行统计计数,将最多的一个类别作为待分类数据的类别。
    以下照搬:

    优点:精度高、对异常值不敏感、无数据输入假定。
    缺点:计算复杂度高、空间复杂度高。 适用数据范围:数值型和标称型。

计算复杂度高很容易理解,毕竟如果已知的训练样本集内数据是n组,每组数据包含m个特征点,那么即使不考虑归一化,要获取任何一个新样本的分类,需要n*m次计算。不过我认为空间复杂度倒是还好,毕竟可以通过各种技巧来节约空间,例如每次只读一个样本,并且只记录最小的前k个距离(k一般小于20所以几乎可以忽略),这样最少只需要2*m个数据空间,只是每次分类都要全部重新读取训练样本,时间会大大增加了。

实践

这一章的实践内容主要是手写数字识别。程序上的事情没什么好说的,我的主要改动就是把作者的numpy改成了pandas,并且将分类结果存在了dataframe同一个表中而不是单独分开,代码在此
碰到了两个坑。首先是约会对象的demo中,我获得的结果和书上不一样。查了半天确定程序没写错,原来是作者在github上提供的数据集和书中用的不一样。
另外一个坑就太坑了:手写数字识别的时候,同一个程序会跑出不一样的结果!这个问题我debug了半天,先是抽出有争议的待测样本专门测试,发现这个问题是random的😱;然后反复加打印看数据,最终发现坑在这里:
neighbors = data.head(k)[CLASS_COL_NAME].value_counts()
这个value_counts是pandas中用来统计计数并排序的,会降序输出统计结果。如原始数据是(2,2,2,3,3,1),则会输出(大意,格式可能不对):2:3, 3:2, 1:1。而问题在于当我的原始数据是(1, 2, 3)时,它的output变成随机的!!!1:1, 2:1, 3:1; 2:1, 1:1, 3:1; 3:1, 2:1, 1:1……一切皆有可能😳!你说它错了吧,它也没错,可是你这个函数输出不确定是什么鬼!我原本是根据value_counts的输出,取第一名作为结果,也就是k近邻中最多的那个类别,但由于这种“并列第一”时的不确定,我的程序判定结果也变得不确定了。最后我只好手动处理,如果有“并列第一”的情况,就选择距离最近的那个邻居的类别作为分类结果。

杂谈

手写数字识别这个问题刚好大学学模式识别的时候我自己做过。那时候是纯粹用特征点匹配解决的,先把图片框定范围,骨架化,提取特征点,然后对照带权重的特征点计算特征向量,最后匹配最佳结果,识别率大概在85%-90%之间。所以当看到书上用kNN做手写数字识别,真的是震惊到了,从没想过kNN可以做这个,因为在我的想法中,kNN的每个特征都是有意义的,而手写数字图片中每个像素点的0或1,是无意义的。但实践证明,使用kNN的方法,成功率高达98%以上。这个事实让我有一种豁然开朗的感觉:过去模式匹配的那一套确实已经过时了,现在有这么多新的方法,或者是旧方法的新思路在向我招手,加上硬件日益强大的计算能力所能做到的远超从前,仅凭我自己能做到的事一定更多更复杂,想想还挺激动人心的。
不过话说回来,我对kNN的神奇功效依然并不那么信任,因为我总觉得kNN的成功率是建立在特征点的有效性上面的。例如一个预测学生成绩的程序,过去的成绩、作业完成度都可以是特征点,但在家喝水的杯子是玻璃杯还是塑料杯这种事就不能作为特征点,因为和学习成绩毫无关系,将其加入计算的话反而会影响预测结果。在手写数字识别这一点上,我依然持同样的保留态度:这一章的图片都是经过特别归一化的,边距、大小都完全统一,风格也基本类似,这就人为地建立了一种像素点和数字本身的特征映射;而在真正的手写体中,不同字体或者风格所带来的区别可能完全毁掉这种映射关系,除非重新建立一套训练样本集。我想这本书将来的东西应该会讲到更多的东西,来解决这个问题吧?拭目以待。