在谈论梯度下降算法之前,先来看看中学时候怎么求一个二次函数的最小值。
例如,现在定义如下函数:
f=x^2-x
在中学时,我们会先对其求其导数:f'=2x-1
令f'=0即可求出该函数在x=1/2时取得最小值,然而,在程序中,该怎么求呢?
首先,可以定义x以如下的方式来迭代x的值:
x=x-rate×k
在这里,rate是x值下降的速率,也可以是讲是x值减少或增加的大小,该值是人为定义的,k值是代表该点的导数,之所以rate前的符号是减号,是因为当k值为正时,x的值会减少,在这时函数是递增的,所以函数值会随着x的减少而减少,反之则刚好相反,这就保证了函数值是一直随着x值的改变而减少,从而达到收敛,可以取得最小值,而要注意的是,如果f是多维函数,那么可能是取到一个极小值而不是最小值。
而关于为什么里面用到该点的导数,是因为导数代表着该点的变化率,确保了x下降的最大值,事实上,导数已经确定了该点是怎么变化的,也就是说导数已经确定了x在某点的变化大小了,而在导数前乘以一个rate是人为改变了x的变化,当然,rate的值不能大于1的,因为当rate的值大于1的时候,引发的后果就是函数不能达到收敛,其实,当rate小于1且不够小的时候,也有可能引发这个后果。所以在这个式子中,x的变化程度是始终小于该点的导数的,也就是说x下降的最大值是该点的导数,所以才说确保了x下降的最大值。
当函数值无限接近与某个值或者函数值处于不变的时候,便认为该函数处于收敛,已求得最小值。
下面给出求该式子最小值的c代码:
#include <iostream>
int main() {
double rate=0.1;
double x=4;
double f=0;
while(true)
{
std::cout<<"x="<<x<<" f(x)="<<x*x-x<<std::endl;
x=x-rate*(2*x-1);
if((f-(x*x-x))==0)
{
break;
}
f=x*x-x;
}
return 0;
}
运算结果为:
有了上述基础后,再来讨论梯度下降算法:
梯度下降算法(Gradient descent algorithm)
梯度下降算法事实上是求多维函数的在某一点收敛的极小值,可以用这个算法迭代出在哪个点收敛,也是求最小二乘问题的一种方法。先在脑海中想象一下,你站在一座山上,怎么找到最快下山的方法,这时你当然会朝着最陡峭的方向前进,到达一个点后,再次朝着陡峭的方向下山,从而循环这些步骤,到达山脚。事实上,这也是梯度下降算法名字的由来,如图所示。
先来熟悉下有关梯度下降算法的几个术语:
1)特征值(feature):是样本的输入部分,比如目标函数:,其中x1与x2是特征值,特征值的值是属于数据集的,而且是已知的,θ1与θ2是参数,也是要求的几个值。
2)学习速率(learning rate):是决定在梯度下降中下降的大小程度,或者长度。
3)目标函数(hypothesis function):又称假设函数,是在监督学习中,为了拟合输入样本而产生的目标函数。格式如下:
倘若有两个特征变量,那么表达式则为:。
4)样本(sample):样本是一些数据,代表着样本中第i个输入样本,
则对应着样本中第i个输出样本。假如:一组数据中有房子的大小,房间的数量和房子的价钱。其中房子的价钱受前两者的影响,那么一组房子大小和房间数量即可称为一个输入样本,一个房子价钱称为输出样本,其中房子大小与房间数量为其中两个特征值。
5)损失函数(loss function):损失函数是用来评估模型好坏的,其中函数值越小代表着拟合的越好。一般用假设函数减去输出样本的方差来评估,一般表达式如下:,其中m是样本的数量,乘以二分之一是便于计算。
上面说了,求得损失函数达到最小的时候,亦或者收敛的时候,目标函数与样本拟合的很好,也便能确定其中各个θ的值。
所以只要改变各个θ的值,来达到损失函数收敛即可,但是如何改变各个θ的值来让损失函数收敛呢?而且怎么保证下降的方向与大小是正确的呢?
借鉴于上文二次函数求最小值,同理,也可以参照这么做,只不过在多维函数中是求各个θ在那点的偏导数,这样便得到了下降的方向与大小:
推导得:
而判断损失函数是否收敛,可以根据函数图像来观察,亦或者判断损失函数减少时忽略不计时即可判断它收敛。
下面给出具有一个特征值的求解代码,matlab实现:
theta0=1;
theta1=1;
r=0.001;
t=0;
while(true)
t=t+1;
sum1=0;
sum2=0;
sum=0;
for i=1:m
sum1=sum1+theta0+theta1*x(i)-y(i);
sum2=sum2+(theta0+theta1*x(i)-y(i))*x(i);
end
theta0=theta0-r*sum1/m;
theta1=theta1-r*sum2/m;
for j=1:m
sum=sum+(theta0+theta1*x(j)-y(j))^2;
end
J=sum/(2*m);
if(J<=4.5)
break;
end
end
运算结果为:theta0= -3.3929 theta1=1.1425
给出原始数据图:
验证:
因为取值的问题,虽然有些偏差,但是可以看出,拟合的已经比较好了。
正规方程组(Normal equation)
正规方程组是梯度下降算法的矩阵形式,其表达式为:
具体推导就不推了,谈谈其中的元素,其中θ代表着θ0,θ1.....θn组成的列向量,X代表着由输入样本组成的矩阵,y指输出样本组成的列矩阵。
根据此式子可求出各参数的值,下面给出代码:
z=ones(m,1);
x=[z,x];
theta=pinv(x'*x)*x'*y;
disp(theta);
再次利用上次使用的数据集求出:
θ0=-3.8598 θ1=1.1930
与代数法求出的结果相比相差不大,是正确的。
数据集与源代码地址:https://pan.baidu.com/s/12WMLAqY2YJH-mv58B9p22g