蒟蒻退役ACMer 1403mashaonan终于读完了新买的Machine Learning in Action(机器学习实战)
立的年前读完这本书的flag没有完成(主要是19-25号水了个美赛然后一周没读,不然应该能完成任务的QAQ,总的大概花了两周时间读完)
本文的目的旨在作为个人的读书总结,总结一下各个算法的核心部分,并不是详尽的笔记
以前写在作业部落,今天转到csdn上(好像是过年时候写的了)
Machine Learning in Action这本书的作者是Peter Harrington,我读的是中文翻译版的(想抓紧时间提升基础,所以读的中文版),这本书我认为是机器学习的入门级读物吧,书中讲了数据挖掘/机器学习的十大算法之中的八个:kNN,C4.5决策树,朴素贝叶斯,SVM,Adaboost,CART,K-mean,Apriori。另外还讲了Logistic回归,线性回归,FP-Growth,PCA,SVD,MapReduce等经典实用的方法。
书中对每个算法,首先是介绍算法,讲解原理,我认为本书原理讲解部分略显简单粗略,读者可以在阅读的同时也参考一些网上的blog,一些数学证明的部分可以参考paper,如果仅仅是打基础了解的话,感觉这本书省略了复杂的证明,可以降低难度,节约新手的时间。
本书的最大特点就是拥有非常详细的代码和解释,语言使用的是Python,一门非常nice的编程语言,如果C++要写1000行,java要写100行的东西,Python可能只要10行,虽然是牺牲了时间效率换取的简洁,但是Python里有很多非常高效的库,大大简化了新手入门时的编程难度。本书主要使用了Python里的Numpy库和Matplotlib绘图工具,可以非常便捷清晰的实现各个算法。
有了这些代码,读者就可以尽情享受在实践中学习的快乐了。
废话不多说了,下面进入本书总结
每本书的第一章都是照例扯淡和历史背景介绍呗
机器学习的作用是什么:机器学习能让我们从数据集中受到启发,利用计算机来彰显数据背后的真实含义。
开发机器学习程序的步骤:
1. 收集数据
2. 准备输入数据
3. 分析输入数据
4. 训练算法
5. 测试算法
6. 使用算法
Python语言的特点:
如何掌握Python Numpy的使用
Python Numpy教程
kNN(k-近邻)算法非常的简单有效并且易于掌握。这是一个监督学习的算法,用于处理分类问题。
原理:首先拥有一个训练样本集,每个样本都拥有标签。输入没有标签的新数据后,将新数据的每个特征与样本集中的数据对应比较,然后选出最相似的k个分类标签。
NN与kNN
首先介绍一下NN算法(Nearest Neighbour算法),是将新数据和样本集中的数据进行比较之后,将新数据的标签置为与它特征最相似的那个样本的标签。
但是这样做会有很大的错误率,一般来说存在很多的特殊情况。
所以我们在此使用了超参数k,选择出最相似的k个分类标签,然后按照投票排序的规则,选出最多的那个类别,就是新数据的标签。超参数k的选择一般不大于20。
代码实现书上讲的十分清晰,这里就不赘述了。
kNN算法的特点
kNN算法十分的简单易懂,所以本节的目的不是讲kNN的算法,而是总结一下它的优缺点。
决策树经常用于处理分类问题,进来的调查表明决策树也是最经常使用的数据挖掘算法。
kNN的最大缺点就是无法给出数据的内在含义,决策树的优势就在于数据形式容易理解。
决策树构造的过程一般是:
1. 寻找划分数据的最好特征
2. 划分数据集
3. 创建分支点
4. 对每个分支点递归上述操作直到不能划分为止
如何寻找最好特征
这是决策树的关键所在,完成了这部分,其他的也只是代码实现的问题了
这里使用的是ID3算法,ID3算法用信息熵为度量来构造决策树
划分数据集的原则:将无序的数据变得更加有序,划分数据集前后信息发生的变化成为信息增益,选择导致最大信息增益的特征来划分数据集是最优的。
如何计算信息增益
信息的度量方式称为信息熵,熵定义为信息的期望值,如果待分类的事物可能划分在多个分类之中,则符号 xi 的信息定义为:
信息增益:
原理了解之后,剩下的就是代码实现了,这部分不是一时半会可以学会的,需要日积月累的写代码,当然不是个难事。
下面是用matplotlib作图工具做出来的本章数据的图像:
决策树的复杂度:决策树在构建的时候采用递归的构造,一旦构造完成,对新数据的学习将非常快,优于kNN。
前两章都是要做出明确的分类,而本章的算法则是给出这个猜测的概率估计值。
朴素贝叶斯的核心思想
假设我们有两类数据,然后有两个特征x和y, p1(x,y) 表示这个数据属于1的概率, p2(x,y) 表示这个数据属于2的概率。
我们会选择概率高的类别。
条件概率分类过程
上述概率比较是简化的,其实真正需要比较的是 p(c1|x,y)和p(c2|x,y) ,表示在特征为 {x,y} 的情况下,属于 c1 的概率和 c2 的概率。
我们使用贝叶斯公式可以得到:
朴素贝叶斯总结
Logistic回归是本书目前为止首次接触最优化算法。
Logistic回归的主要思想
根据现有数据对分类边界线建立回归公式,以此进行分类。
我们想要的函数应该是能接受所有的输入然后预测出类别,两个类的情况下输出0或者1,这种函数叫做单位阶越函数。
Sigmoid函数
回归系数的确定
z=w0x0+w1x1+⋯+wnxn
也可以写成 z=wTx
w 就是我们要找的最佳系数
本书使用的是梯度上升(下降)法:
支持向量机我认为是这本书里面最难的一个算法了,因为理解其原理需要的数学知识最多,当然SVM也是用的十分之多的分类器,在12年CNN被广泛使用以前,SVM一直都是图像识别领域分类效果最好的分类器。
SVM有很多实现,本书使用的是序列最小化SMO算法(本弱还没有仔细看过SMO的论文,所以不太理解SMO的原理,不过SVM前面的原理大概了解)
大间距分类器
如果数据线性可分,那么将数据集分割开来的平面叫做分隔超平面。如果数据有n维,就需要一个n-1维的超平面来分隔数据。
点到分隔面的距离称为间隔,如果间隔越大,那么分类器将越强壮。
支持向量就是离分隔超平面最近的那些点。如何寻找最大间隔呢?
假设分隔超平面的形式为 wTx+b=0 ,那么点 xi 到分隔超平面的距离就是 |wTxi+b|||w||
如果超平面可以把训练样本正确分类,当 yi=+1 ,有 wTxi+b>0 ,当 yi=−1 时,有 wTxi+b<0
根据缩放变换,如果存在 wTxi+b≥ϵ ,那么肯定有 ϱw→w′ 和 ϱb→b′ ,使得
拉格朗日乘子法与KKT条件
这里我们使用拉格朗日乘子法可以得到其对偶问题,对每条约束添加拉格朗日乘子 αi≥0 ,约束条件就转化为 1−yi(wTxi+b)≤0
因为拉格朗日乘子法得到的是原问题的对偶问题,所以现在问题转化为了
SMO算法
用SMO求解的问题我还没有仔细研究,就说一下基本思路把,SMO的思路是选择 αi 和 αj ,并且固定其他参数,这样根据 ∑mi=1αiyi=0 这个条件,可以求出只有 αi 和 αj 的式子,然后只要有一个值违背KKT条件,目标函数就会增大,这是一个二次规划问题,本人的数学水平太菜,还没有仔细研究,留着过段时间研究下。
核函数
前面讲的样本是必须线性可分,如果线性不可分的话,我们需要把原始空间的数据映射到一个高维空间,就可以做到线性可分,这里就需要使用核函数。
核函数只是将两个向量之间的点积 xTixj 换成了核函数的形式 k(xi,xj)=ϕ(xi)Tϕ(xj) ,其余的推导过程还是一样。
常用的核函数有线性核和高斯核,这里就不多介绍了。
软间隔
前面的假定训练样本线性可分,是硬间隔,毕竟有时候会有特殊情况,完全线性可分并不一定是最好的情况,所以要用软间隔,软间隔是在原来硬间隔的基础上加上一个损失函数,然后求解的时候多一个约束,这里不想仔细讲了,细节是差不多的。详细的可以参考周志华的机器学习第六章SVM。
SVM总结
SVM的内容很多,原理也很复杂,硬间隔,软间隔,线性可分,核函数等,我还得好好看书看paper钻研一下,不过SVM的使用很方便啊,各种写好的方法直接套。不过我也没参加过竞赛或者做过项目,还不清楚,过段时间去练练手实践下。
什么是元算法:我的理解是算法最后的评估,不是靠一个模型给出的结果,而是综合考虑多个模型结果,来得出最后的结果。
bagging:基于数据随机重抽样的分类器构建方法。
boosting:关注被已有分类器错分的那些数据来获得新的分类器。
AdaBoost
自适应boosting,运行过程如下:对训练数据中的每个样本,先赋予其一个权重,这些权重开始都相等,先在一个弱分类器上计算错误率,然后在统一数据集上再次训练弱分类器,第二次训练时,将会重新调整每个样本的权重,第一次分对的样本的权重将会降低,分错的样本权重会提高。AdaBoost为每个分类器都分配了一个权重 α ,这些权重是基于错误率算出来的,错误率 ϵ 定义为:
AdaBoost的过拟合问题
当分类器数目越来越多,训练错误率肯定是越来越小,但是测试错误率却是先减后增,可见发生了过拟合。
其他算法发现过拟合可以使用一些比如正则化的方法,AdaBoost发生过拟合的解决办法本书没有提到。
其他分类性能度量指标
包括正确率(precise),召回率(recall),以及ROC曲线,这些在书上都介绍的很清楚,就不再赘述了。
本章开始预测数值型数据了,不再是讲分类算法。
本章讲的是线性回归,就是找出回归系数,和Logistic回归很像,就是去掉sigmoid函数。
平方误差
对样本训练之后需要有误差反应样本拟合的情况,平方误差定义为:
线性回归的欠拟合
线性回归非常简单,Ng的网络课上主要使用的梯度下降方法,这本书直接用了最小二乘法来解决。但是线性回归会出现欠拟合,如下图所示:
所以我们需要在估计中引入一些偏差, 从而降低误差。一个方法就是局部加权线性回归(LWLR)。
LWLR
在该算法中,我们给带预测的每个点赋予一定的权重,该方法的形式如下:
于是我们就可以先算出来这个高斯核,然后在计算回归系数 w
当k=0.01的时候,得到的图像还是比较好的:
岭回归(ridge regression)
如果特征比样本点还多的时候,即 n>m 时,输入矩阵X不是满秩,会导致 XTX 不可逆,这时候就需要用岭回归来解决。
岭回归是在矩阵 XTX 上加一个 λI 从而使得矩阵非奇异,进而对 XTX+λI 求逆。
这种情况下,回归系数计算公式变为:
前向逐步回归
这是一种贪心算法,每一步都尽可能的减少误差,一开始所有权重都设为1,然后每一步所做的决策就是对某个权重增加或减少一个很小的值。
需要设置步数和步长。逐步回归的好处在于能帮助人们理解现有模型并且做出改进,可以找出重要的特征,然后去收集重要的特征的数据。
权衡偏差与方差
误差=偏差+测量误差+噪声,随着模型复杂度增加,训练集的误差降低,但是测试集的误差会先减后增,表明从高偏差过渡到了高方差模型,这里权衡两者是很重要的。
总结
线性回归用最小二乘法的时候,以及LWLR还有岭回归里的X中,都不需要设置 x0=1 这一项(这点有待考证,因为本书的最小二乘法里面好像是没有加,但是周志华的西瓜书上是加的),这个在Logistic回归用梯度下降的时候是需要的。
本章介绍了一个新的树算法,叫做CART(分类回归树),该算法即可以用于分类还可以用于回归,后面还介绍了书剪枝,目的是防止树的过拟合。
CART
前面介绍的决策树ID3算法,不能处理连续型特征值。而CART是用二元切分来处理连续型变量的。方法大概就是找到最优切分特征,然后左右子树递归调用建树函数。
如何知道已经切分到底了,也就是如何计算连续型变量的混乱度,可以用总方差来计算比较。总方差的变化小于一个值的时候,就表示已经切分到底了。
树剪枝
如果一棵树的节点过于多,就表明可能对数据进行了过拟合。
我们有两种剪枝的方法,一种是预剪枝,另外一种叫做后剪枝。
预剪枝:预剪枝就是对节点的规模做限制,如果节点的规模小于限制,那么不可往下分,还有就是对划分前后总方差的限制,如果变化不大,那么也不可分。但是预剪枝对两个参数的取值很难把握,有时候因为数据的不同,参数对数据数量级十分敏感,一不小心就会造成过拟合。
后剪枝:需要将数据集分为训练集和测试集,先用训练集构造出回归树,然后后剪枝是对树结构的递归操作,如果两个子节点都是叶节点,并且如果合并之后能够降低测试误差,那么进行塌陷处理,就是把这个节点的值设为左右子节点的平均值,然后一层一层递归。
一般来说两种剪枝技术要同时使用可以得到更好的效果。
模型树
回归树的叶子节点是设定为常数值,还有一种方法是把叶子节点设定为分段线性函数。线性函数的节点显然比值节点的树更容易理解,这也是模型树优于回归树的特点之一。
下面看一下用来构建模型树的分段线性数据:
使用模型树只需要修改一下叶节点的生成函数即可,和回归树十分类似。
相关系数
如何比较多种模型哪个更优呢?调用Numpy库里面的corrcoef(yHat, y, rowvar = 0)
来求解相关系数 R2 ,然后做比较即可,越接近1越好。
总结
从这张开始就是务监督学习的算法了。
K-means是聚类算法,聚类是将相似的样本分到同一个簇中,根据簇内的对象越相似,聚类的效果就越好。
K-means是可以发现k个不同的簇,而且每个簇的中心采用簇中所含值的均值计算而成。
聚类与分类的最大不同在于, 分类的目标事先已知,聚类的标签事先不知道。
K—means的伪代码
创建k个点作为初始的质心点(随机选择)
当任意一个点的簇分配结果发生改变时
对数据集中的每一个数据点
对每一个质心
计算质心与数据点的距离
将数据点分配到距离最近的簇
对每一个簇,计算簇中所有点的均值,并将均值作为质心
下面是K-means运行之后的结果图:
但是K-means的聚类效果比较差,因为很容易收敛到局部最小值,而非全局最小值。
所以我们要用新的方法去改进K-means
二分K-means算法
何为二分K-means,就是首先所有点都作为一个簇,然后将该簇iyfenweier,之后选择其中一个簇继续划分,选择哪一个簇进行划分取决于对其划分之后可以最大程度降低SSE值(误差平方和)。
二分K-means的伪代码
将所有数据点看成一个簇
当簇数目小于k时
对每一个簇
计算总误差
在给定的簇上面进行k-均值聚类(k=2)
计算将该簇一分为二后的总误差
选择使得误差最小的那个簇进行划分操作
K-means的内容也没什么好讲的了,比较简单,实现起来也不是很难。
从大规模数据集中寻找物品间的隐含关系被称作关联分析或者关联规则学习。
本章介绍的就是如何使用Apriori算法来解决上述问题。
关联分析:关联分析是一种在大规模数据集中寻找有趣关系的任务,这些关系可以有两种形式:频繁项集或者关联规则。
频繁项集:经常出现在一块的物品的集合。
关联规则:暗示两种物品之间可能存在很强的关系。
支持度:一个项集的支持度被定义为数据集中包含该项集的记录所占的比例。
可信度:针对一条诸如{尿布}->{葡萄酒}的关联规则来定义的。定义为支持度({尿布,葡萄酒})/支持度({尿布}),感觉和条件概率很类似
Apriori原理
如果生成所有的组合清单,然后一个个统计,当物品种类十分之多的时候,此做法会非常慢,所以我们需要使用Apriori原理进行优化。
Apriori原理是说如果某个项集是频繁的,那么它的所有子集也是频繁的。这个原理看似没有什么帮助,但是如果倒过来看,如果说一个项集是非频繁的,那么它的所有超集也都是非频繁的。
如下图所示:
对数据集的扫描伪代码:
对数据集中的每条交易记录tran
每个候选集can
检查can是否是tran的子集
如果是,则增加can的计数值
对每个候选项集:
如果其支持度不低于最小值,则保留该项集
返回所有频繁项集列表
整个Apriori算法的伪代码:
当集合中项集的个数大于0时
构建一个k个项集组成的候选项集列表
检查数据以确认每个项集都是频繁的
保留频繁项集并构建k+1项组成的候选项集的列表
这两段代码交叉使用,先找出只含1个元素的候选集,然后判断支持度,返回频繁项集,然后构造含有2个元素的频繁项集,然后再判断,如此循环直到集合中没有元素。
并且代码中有个优化的地方就是,当两个长度为k的频繁项,去构造一个长度为k+1的候选项,是看它们两个之间只有最后一位元素不同,而不是只有一个元素不同,这样大大减少了重复值,比如01和12就不能构成012,但是01和02就能构成012。
详细代码可以参考书上内容。
挖掘关联规则
关联规则是对于一个频繁项集来说的。
一条规则 P→H 的可信度定义为 support(P|H)support(P)
对于关联规则也可以使用Apriori原理来减少规模和复杂度。
如下图所示:
代码实现有点小难度,要递归实现,这里就不赘述了。
理解思想就好了。
FP-growth算法是一种高效发现频繁集的方法。它比Apriori算法要快,这里的任务是将数据集存储在一个特定的称作FP树的结构之后发现频繁项集或者频繁项对,这种做法使得算法的执行速度要快于Apriori两个数量级以上。
FP-growth算法的优点
FP-growth算法只对数据库进行两次扫描,而Apriori算法对于每个潜在的频繁项集都会扫描,所以FP-growth算法更快。(其实我没搞明白为啥是只进行两次扫描)
FP树
FP代表频繁模式(Frequent Pattern),通过链接link来连接相似元素,被连起来的元素项可以看作一个链表。
树的形状如下:
构建FP树的方法,感觉就像构建一个字典树一样,每个节点记录出现的次数,然后用一个链表的形式,记录下每个节点出现的位置,感觉搞过ACM,学过trie树和链式前向星的话并不难理解这个构造的原理。
如何从一棵FP树中挖掘频繁项集
可以分为下面三个基本步骤:
1. 从FP树中获得条件模式基;
2. 利用条件模式基,构建一个条件FP树;
3. 迭代重复步骤1和2,直到树包含一个元素项为止。
条件模式基
条件模式基是以所查找元素项为结尾的路径集合,每一条路径其实都是一条前缀路径。
抽取出来每个频繁项集的前缀路径之后,用条件模式基构造条件FP树
如下图,就是t的条件FP树:
接下来在这个条件FP树上挖掘新的条件模式基,得到更复杂的频繁项集。
这边其实稍微有一点难理解,其实就是首先对于最简单的1个元素的,去掉不满足支持度的元素,选出长度1的频繁项集,然后把给出的元素序列,构造FP树,然后对于每个长度为1的频繁项集,找出条件模式基(前缀路径),然后根据前缀路径,去掉不满足支持度的元素,满足支持度的元素,加上原来长度1的频繁项集,就构成了长度2的频繁项集,以此类推这样。然后代码也是用递归实现了这一个操作。
总的来说FP-growth算法,理解难度和代码实现比前面的算法略微难一点,但是感觉还是挺套路的,至于复杂度少,只扫描数据集两次,我确实不是特别清楚是如何计算的,以后还需深入研究啊。
前面那些算法终于都写完了,这里是介绍一些工具了,就讲的粗略一点,毕竟也不是什么复杂的东西(虽然原理有待仔细钻研,但是用起来十分简单)
PCA
主成分分析,因为对于数据集,总有一些特征是主要的,一些特征是次要的,我们如果可以保留里面最主要的一些特征,舍去一些无关紧要的特征或者噪声,就可以对数据集进行降维压缩。
第一个主成分就是从数据差异性最大的方向提取出来的,第二个主成分就是来自于数据差异性次大的,并且该方向与第一个主成分方向正交(感觉这是肯定的结论啊?不是很懂线代)
用了PCA降维之后,数据可以去除噪声,让分类的效果更好。
下面是使用PCA之前和之后的数据图:
PCA的伪代码:
去除平均值
计算协方差矩阵
计算协方差矩阵的特征值和特征向量
将特征值从大到小排序
保留最上面的N个特征向量
将数据转换到上述N个特征向量构建的新空间中
如果我们把二维数据进行PCA降维到一维,然后再恢复到二维,会得到如下图像:
代码实现不是很重要,主要是这种思想,代码随便找找就好叻。
我们还可以做出主成分数目对方差的影响的图:
PCA总结
可见,几百维的数据,压缩到个位数维,误差都不是很大,而且由于舍弃了噪声,数据将更加干净。
如果数据集太大,不能全部调入内存,还有在线PCA的方法。
在UFLDL等一些资料上,我还看到了白化等其他技巧,不过本书都没有介绍。
SVD(奇异值分解),应用在信息检索和推荐系统中比较多。
感觉思想和PCA很类似,Ng的课里讲的PCA就是用的奇异值分解计算协方差矩阵的。
上述的 ∑ 矩阵,改矩阵只有对角元素,下面多的行都是0,其余元素也都是0,这些对角元素称为奇异值。
奇异值和特征值是有关系的,奇异值就是矩阵 Data∗DataT 特征值的平方根.
对于SVD分解的实现,我们只要用Numpy里面自带的工具箱实现即可。
在科学和工程中,一直存在这样一个普遍事实:在某个奇异值的数目(r个)之后,其他的奇异值都置为0。
这样意味着仅有r个重要特征,其他都是噪声或者冗余特征。
我们如何确定只需要保留r个奇异值呢?一个典型的做法就是保留矩阵中90%的能量信息,为了计算总能量信息,我们要将所有的奇异值求其平方和,然后累加满90%即可。
协同过滤
推荐引擎使用的主要方法是协同过滤,我们要推荐一种东西,就要把它和用户用过的另一个东西比较相似度,这里的相似度不是用来描述物品特征的,而是用用户对它们的意见来计算的。
计算相似度的方法有欧氏距离法,皮尔逊相关系数,余弦相似度。
如下图所示:
行与行之间的比较是基于用户的,列于列之间的比较是基于物品的。
当用户数很多的时候,我们倾向于基于物品相似度的计算方法(即用户评分)。
利用SVD提高推荐的效果
我们可以对上述矩阵进行SVD分解,然后构建转换后的物品,每个物品的特征缩减到r个,然后计算相似度。
利用SVD还能进行图像压缩,对于一个32*32=1024像素的图像,如果只取两个奇异值,得到的新数据依然效果很好,但是只用了32*2*2+2=130的存储空间,将近压缩了10倍。
SVD总结
SVD是相当强大的降维工具,保留80%-90%的能量,可以去除噪声并且保存重要特征,用SVD优化的协同过滤,也能获得更好的效果。
由于第十五章大数据和MapReduce,不好实践,而且我暂时用到的情况也应该没有,打算以后再系统的学习,就没有看。Ng老师的课程里介绍过大数据和MapReduce,我有了一个粗略的了解。
本书到这里就都读完了,我只是简略的写了下算法的重点,便于自己理解,如果要好好学习了解,还是得亲自看书实践,虽然这些算法我都认真看了,代码也认真读了,但是我感觉我还是不能自己独立写出来。最近学习CS231n的课程时候,作业题我就得花不少时间写(主要是Numpy库用的不熟练)。
入坑一个多月,看了Ng老师的课程和机器学习实战这本书,周志华的西瓜书看了几章,CS231n学了四五课,感觉对机器学习的基础算法有了大概的了解,对其中深刻的原理还不是特别懂,比如神经网络的一些知识,本书没有提到,但是可以通过CS231n来深入学习。SVM也是我觉得数学知识最多的一个算法,以后肯定也要读一下paper理解一下,其余的一些算法,比如FP-growth,我感觉我也只是知道如何使用,并不知道为什么这样能够提高效率,看来还需要更深入的学习啊。
学无止境,ACM退役后浑浑噩噩地晃了一个月,才真正的找到了我想要学习的东西,感觉自己又找回了当年,入门ACM时候的动力和喜悦。
在读了知乎和大数据文摘的一些文章之后,我对机器学习,深度学习,乃至是人工智能领域,产生了极大的兴趣,我也会继续努力,学习更多的知识,在这个领域,不断前行。
最后提供一下本书的代码和数据,也当自己保存下,如果有人需要,也不用去费尽心思的找了。
机器学习实战代码和数据