写在前面
这两天研究了一下机器学习中较为简单的一类回归问题,对数回归。写一篇博客总结一下自己的体会和经验,若是其中出现错误,还希望读者能够留言帮我指出。
对数回归问题是机器学习中线性模型的一种,也是非常基础的一种分类器模型。线性回归中最广为人知的莫过于最小二乘估计,这是高中阶段就讲过的知识。本篇文章所要讲的是如何将线性模型应用到机器学习中,并且如何求解模型。
对数回归
机器学习领域最常见的应用就是分类,对数回归进行分类的主要思想就是根据现有标记数据(训练集)对分类边界建立回归公式,以此进行分类。现在,可以试想我们已经得到了一个有效的分类模型,这个模型的输入是待分类的一个向量,输出是分类结果(用0代表反例,1代表正例)。然而,回归模型的输出一般都是连续值。如果要使模型能够应用于分类,就必然需要确定一个阈值,若回归模型的输出大于这个阈值则返回正例,反之返回反例。很显然分段函数能够满足上述的需求,然而分段函数不连续可微,在最优化问题中这不是一个很好的数学性质,因此我们不能使用分段函数。我们需要的函数要既要拥有分段函数的性质,又要连续可微。幸好,存在这样的函数,这就是sigmod函数,该函数的图像如下图所示:
sigmod函数的自变量是线性模型的输出。写到这里就带出了对数回归的概念,对数回归其实就是在传统线性回归的基础上增加了一层sigmod函数。
线性模型
这一节来论述对数回归的核心——线性模型的建立。一个线性模型可以建立为以下形式:设权重列向量为w=(w0;w1;w2…;wn),输入行向量为x=(x0,x1,x2…,xn),线性模型如下式所示:
其中x可以为一个行向量也可以为一个输入矩阵。该模型的输出就是模型的预测值,由于训练数据都是带标签的因此我们可以计算出预测值与真实值的差:
需要说明的是若输入x为向量则误差e为一个实数,若x为矩阵则e为一个列向量。我们的目标是让该模型在所有训练集上的误差之和最小。然而误差e可能出现正值也可能出现负值,如果简单相加可能出现正负抵消现象,因此好的方案是让误差平方后相加。这样,我们就得到了线性模型的优化目标函数:
看到这里你可能会发现为什么一直没有用到sigmod函数,这主要是因为sigmod函数只是用于最后输出,在模型求解过程中sigmod函数并没有什么特别的影响,因此出于简化目的就将其省去。
梯度下降法
梯度下降法是求解最优化的问题时最常用的一种方法,只是由于其计算量巨大已经很少用到了。不过很多其他方法都是由梯度下降法改进或衍生出的,因此还是有必要提一下梯度下降法。该方法适用于求解凸函数最小值,其思想概括起来就是让函数的自变量沿着函数变化率最大的那个方向以某一个步长移动,递归这一过程,直到函数值满足某个预设条件,递归返回,迭代更新公式如下式:
函数变化率最大的那个方向就是函数微分结果的方向,步长用户可以自行指定,但是要注意防止陷入局部最小。
模型求解
目前我们已知模型的目标函数,首先我们要求目标函数对w的微分,由于函数中的变量都是矩阵形式因此在微分操作时与普通函数有一点不同。详细过程如下,将原始的目标函数展开如下形式:
上式共有四项,可以看出最后一项不含w因此其微分为0,我们主要对上式的前三项进行微分,第一项的微分:
第二项的微分:
第三项的微分:
将三项微分结果组合在一起就得到了目标函数的微分结果:
这个结果中的常数2可以忽略掉,对结果不会产生什么影响。权重向量w的迭代公式也就可以确定下来:
我们只需要在模型中迭代求解w值,直到某一个迭代次数或模型输出达到某个标准便可以得到模型的解。
实现
这一部分笔者将详细介绍对数回归的编程实现,在这里要感谢《机器学习实战》这本书,这部分所使用到的两个数据集都来源于这本书的附录,前一个数据集用于测试和改进算法,后一个数据集用来解决一个实际的问题。
首先实现传统梯度下降算法以求解对数回归模型,然后再引出本篇博客的另一个主题——随机梯度下降算法。该部分使用第一个数据集,该数据集由若干个二维向量组成,每一个向量还带有0或1的标记值。传统梯度下降算法的代码如图所示:
该算法首先初始化权重值weight全部为1,然后每次计算在sigmod函数下与标准分类结果的误差error,使用倒数第二行的代码对weight进行更新,如此进行10000次迭代,最后返回weight值。该函数最后还返回了一个res矩阵,该矩阵用来存储每一次迭代weight的值,用于作图。运行结果如下图所示:
图中两种颜色的点代表不同类别的数据,中间的一条分割线即是分类器给出的最佳分类结果,可以看出在线性模式下该分类结果还是比较好的。然而我们分析上文给出的代码可以看出,传统的梯度下降算法每一次迭代都需要全部的数据集参与计算,这样的模式在较小的数据集下尚能接受,若数据集较大这样的方法就无法继续使用了。
随机梯度下降算法
随机梯度下降算法是针对传统梯度下降算法每一次迭代运算量过大的改进。在正式引入随机梯度下降算法之前笔者先介绍一个中间算法,该算法也是对梯度下降算法的改进。传统的梯度下降算法每一次迭代都使用全部数据集对权重值进行更新,能否换一种思路,每次迭代只用一个向量对权重值进行更新,迭代的规模限定在数据集规模以内。这样不但降低了迭代次数,而且这样的学习算法是在线的,即能根据新的数据不断优化模型。实现代码如下:
该算法与传统梯度下降算法的区别主要在于单次迭代中对权重值的更新只用到了数据集中的一个子集。这样的设定减少了算法的时间复杂度,提高了运行速度,而且算法是在线的。运行效果如下:
可以看出,该算法的分类效果不是很好,需要改进。首先我们需要找出算法分类效果变差的原因,然后再制定改进方案。算法分类效果的好坏很大程度上与权重值的计算有关,我们首先需要确定在改进算法的前提下权重值是否是收敛的。上文所列出的两个算法都返回了res矩阵,该矩阵中存储了权重值的变化情况,刚好可以用来观察权重值的变化趋势,作图如下:
图中横坐标为迭代次数,纵坐标代表权重向量分量的大小。左图为传统梯度下降法下权重向量三个分量的变化情况,右图是改进算法下权重向量的变化情况。从右图中我们可以看出除w2分量外,另外两个分量都还看不出收敛的趋势,这可能是迭代次数过少造成的。除此之外,还可以看出右图的曲线有较多波折,这可能是数据集中有一些不能被正确分类的点在每一次迭代中都对权重值造成很大干扰造成的。针对以上两点笔者介绍一种改良算法——随机梯度下降算法。该算法的实现代码如下:
该算法主要有三个地方的改进:首先是增加了迭代次数,且迭代次数可以由用户传入,这样能使权重向量收敛;其次是对权重向量更新时选取的数据是随机的,这样能避免奇异点对权重向量的干扰;最后是每次迭代的步长做了动态调整,保证随着迭代的增加步长是减小且不是严格减小的,这样的设定也是为了权重向量的加速收敛。采用随机梯度下降算法的效果如下:
可以看出使用新的随机梯度下降算法训练出的分类器也能取得不错的分类结果。接下来,检查一下权重向量的收敛情况,效果如下图:
左图是最初的改进算法的迭代次数与权重向量的关系,右图是随机梯度下降算法下迭代次数与权重向量的关系,可以看出在一共执行了15000次迭代后权重向量已经很好的收敛了,且不存在很大的噪声,说明算法的效果还是不错的。
一个小应用
这个应用的例子也来源于《机器学习实战》这本书,真的要感谢这本书给我启蒙。这个小demo用对数回归模型预测马匹生病后是否会死亡,数据集分训练集和测试集,代码如下:
算法在读入测试数据后就分别使用传统梯度下降算法,改进算法和随机梯度下降算法对分类器进行训练,最后得到三个模型的解,然后用这三个解分别对测试数据集进行测试,最终得出三个算法的错误率,结果如下:
从结果上可以看出效果最好的仍然是传统梯度下降算法,随机梯度下降算法的表现也十分不错,最差的是改进算法,分错了60%的数据。
从左到右依次是传统梯度下降算法,改进算法和随机梯度下降算法的权重向量的变化情况(只选取了权重向量的前三个分量作图),可以看出梯度下降算法和随机梯度下降算法的权重向量都收敛了,只是梯度下降算法的权重向量值抖动很大,说明训练集中有很多噪声点,随机梯度下降算法的权重向量的抖动就小的多,说明随机选取数据在减小噪声点对权重向量收敛的影响上的效果是显著的。做为对比,改进算法的效果就差得多,权重向量没有任何一个分量是收敛的。
开源
https://github.com/yhswjtuILMARE/LogisticRegression
2018年1月24日