机器学习--梯度下降法(BGD、SGD、MBGD)

来源:互联网 发布:国家信安全与网络 编辑:程序博客网 时间:2024/05/18 01:24

1. 梯度

    在微积分里面,对多元函数的参数求偏导数,把求得的各个参数的偏导数以向量的形式写出来,就是梯度。比如函数f(x,y), 分别对x,y求偏导数,求得的梯度向量就是(xf,yf)T,简称gradf(x,y)或者f(x,y)。对于在点(x0,y0)的具体梯度向量就是(x0f,y0f)T.或者f(x0,y0),如果是3个参数的向量梯度,就是(xf,yf,zf,)T,以此类推。

    那么这个梯度向量求出来有什么意义呢?他的意义从几何意义上讲,就是函数变化增加最快的地方。具体来说,对于函数f(x,y),在点(x0,y0),沿着梯度向量的方向就是(x0f,y0f)T的方向是f(x,y)增加最快的地方。或者说,沿着梯度向量的方向,更加容易找到函数的最大值。反过来说,沿着梯度向量相反的方向,也就是 (x0f,y0f)T的方向,梯度减少最快,也就是更加容易找到函数的最小值。

2. 梯度下降与梯度上升

    在机器学习算法中,在最小化损失函数时,可以通过梯度下降法来一步步的迭代求解,得到最小化的损失函数,和模型参数值。反过来,如果我们需要求解损失函数的最大值,这时就需要用梯度上升法来迭代了。

    梯度下降法和梯度上升法是可以互相转化的。比如我们需要求解损失函数f(θ)的最小值,这时我们需要用梯度下降法来迭代求解。但是实际上,我们可以反过来求解损失函数f(θ)的最大值,这时梯度上升法就派上用场了。

    下面来详细总结下梯度下降法。

3. 梯度下降法算法详解

3.1 梯度下降的直观解释

    首先来看看梯度下降的一个直观的解释。比如我们在一座大山上的某处位置,由于我们不知道怎么下山,于是决定走一步算一步,也就是在每走到一个位置的时候,求解当前位置的梯度,沿着梯度的负方向,也就是当前最陡峭的位置向下走一步,然后继续求解当前位置梯度,向这一步所在位置沿着最陡峭最易下山的位置走一步。这样一步步的走下去,一直走到觉得我们已经到了山脚。当然这样走下去,有可能我们不能走到山脚,而是到了某一个局部的山峰低处。

    从上面的解释可以看出,梯度下降不一定能够找到全局的最优解,有可能是一个局部最优解。当然,如果损失函数是凸函数,梯度下降法得到的解就一定是全局最优解。

这里写图片描述

3.2 梯度下降的相关概念

    在详细了解梯度下降的算法之前,我们先看看相关的一些概念。

    1. 步长(Learning rate):步长决定了在梯度下降迭代的过程中,每一步沿梯度负方向前进的长度。用上面下山的例子,步长就是在当前这一步所在位置沿着最陡峭最易下山的位置走的那一步的长度。

    2.特征(feature):指的是样本中输入部分,比如样本x0,y0,x1,y1,则样本特征为x,样本输出为y。

    3. 假设函数(hypothesis function):在监督学习中,为了拟合输入样本,而使用的假设函数,记为hθ(x)。比如对于样本xi,yi(i=1,2,...n),可以采用拟合函数如下:hθ(x)=θ0+θ1x

    4. 损失函数(loss function):为了评估模型拟合的好坏,通常用损失函数来度量拟合的程度。损失函数极小化,意味着拟合程度最好,对应的模型参数即为最优参数。在线性回归中,损失函数通常为样本输出和假设函数的差取平方。比如对于样本xi,yi(i=1,2,...n),采用线性回归,损失函数为:
    J(θ0,θ1)=i=1m(hθ(xi)yi)2

    其中xi表示样本特征x的第i个元素,yi表示样本输出y的第i个元素,hθ(xi)为假设函数。

3.3 梯度下降的详细算法

    梯度下降法的算法可以有代数法和矩阵法(也称向量法)两种表示,如果对矩阵分析不熟悉,则代数法更加容易理解。不过矩阵法更加的简洁,且由于使用了矩阵,实现逻辑更加的一目了然。这里先介绍代数法,后介绍矩阵法。

3.3.1 梯度下降法的代数方式描述

    1. 先决条件: 确认优化模型的假设函数和损失函数。

    比如对于线性回归,假设函数表示为 hθ(x1,x2,...xn)=θ0+θ1x1+...+θnxn, 其中θi(i=0,1,2...n)为模型参数,xi(i=0,1,2...n)为每个样本的n个特征值。这个表示可以简化,我们增加一个特征x0=1 ,这样hθ(x0,x1,...xn)=i=0nθixi

    同样是线性回归,对应于上面的假设函数,损失函数为:
    J(θ0,θ1...,θn)=12mi=0m(hθ(x0,x1,...xn)yi)2
    2. 算法相关参数初始化:主要是初始化θ0,θ1...,θn,算法终止距离ε以及步长α。在没有任何先验知识的时候,我喜欢将所有的θ初始化为0, 将步长初始化为1。在调优的时候再 优化。

    3. 算法过程:

      1)确定当前位置的损失函数的梯度,对于θi,其梯度表达式如下:
      θiJ(θ0,θ1...,θn)
      2)用步长乘以损失函数的梯度,得到当前位置下降的距离,即αθiJ(θ0,θ1...,θn)对应于前面登山例子中的某一步。

      3)确定是否所有的θi,梯度下降的距离都小于ε,如果小于ε则算法终止,当前所有的θi(i=0,1,...n)即为最终结果。否则进入步骤4.

      4)更新所有的θ,对于θi,其更新表达式如下。更新完毕后继续转入步骤1.

    下面用线性回归的例子来具体描述梯度下降。假设我们的样本是
    (x(0)1,x(0)2,...x(0)n,y0),(x(1)1,x(1)2,...x(1)n,y1),...(x(m)1,x(m)2,...x(m)n,yn),
    损失函数如前面先决条件所述:

   J(θ0,θ1...,θn)=12mi=0m(hθ(x0,x1,...xn)yi)2

    则在算法过程步骤1中对于θi 的偏导数计算如下:   

    θiJ(θ0,θ1...,θn)=1mj=0m(hθ(xj0,xj1,...xjn)yj)xji
    由于样本中没有x0上式中令所有的xj0为1.

    步骤4中θi的更新表达式如下:
    θi=θiα1mj=0m(hθ(xj0,xj1,...xjn)yj)xji
    从这个例子可以看出当前点的梯度方向是由所有的样本决定的,加1m 是为了好理解。由于步长也为常数,他们的乘机也为常数,所以这里α1m可以用一个常数表示。

    在下面第4节会详细讲到的梯度下降法的变种,他们主要的区别就是对样本的采用方法不同。这里我们采用的是用所有样本。

3.4 梯度下降的算法调优

    在使用梯度下降时,需要进行调优。哪些地方需要调优呢?

    1. 算法的步长选择。在前面的算法描述中,我提到取步长为1,但是实际上取值取决于数据样本,可以多取一些值,从大到小,分别运行算法,看看迭代效果,如果损失函数在变小,说明取值有效,否则要增大步长。前面说了。步长太大,会导致迭代过快,甚至有可能错过最优解。步长太小,迭代速度太慢,很长时间算法都不能结束。所以算法的步长需要多次运行后才能得到一个较为优的值。

    2. 算法参数的初始值选择。 初始值不同,获得的最小值也有可能不同,因此梯度下降求得的只是局部最小值;当然如果损失函数是凸函数则一定是最优解。由于有局部最优解的风险,需要多次用不同初始值运行算法,关键损失函数的最小值,选择损失函数最小化的初值。

    3.归一化。由于样本不同特征的取值范围不一样,可能导致迭代很慢,为了减少特征取值的影响,可以对特征数据归一化,也就是对于每个特征x,求出它的期望x¯和标准差std(x),然后转化为:

     xx¯std(x)
    这样特征的新期望为0,新方差为1,迭代次数可以大大加快。

4. 梯度下降法大家族(BGD,SGD,MBGD)

4.1 批量梯度下降法(Batch Gradient Descent)

    批量梯度下降法,是梯度下降法最常用的形式,具体做法也就是在更新参数时使用所有的样本来进行更新,这个方法对应于前面3.3.1的线性回归的梯度下降算法,也就是说3.3.1的梯度下降算法就是批量梯度下降法。  

    θi=θiαj=0m(hθ(xj0,xj1,...xjn)yj)xji
    由于我们有m个样本,这里求梯度的时候就用了所有m个样本的梯度数据。

4.2 随机梯度下降法(Stochastic Gradient Descent)

    随机梯度下降法,其实和批量梯度下降法原理类似,区别在与求梯度时没有用所有的m个样本的数据,而是仅仅选取一个样本j来求梯度。对应的更新公式是:

    θi=θiα(hθ(xj0,xj1,...xjn)yj)xji
    随机梯度下降法,和4.1的批量梯度下降法是两个极端,一个采用所有数据来梯度下降,一个用一个样本来梯度下降。自然各自的优缺点都非常突出。对于训练速度来说,随机梯度下降法由于每次仅仅采用一个样本来迭代,训练速度很快,而批量梯度下降法在样本量很大的时候,训练速度不能让人满意。对于准确度来说,随机梯度下降法用于仅仅用一个样本决定梯度方向,导致解很有可能不是最优。对于收敛速度来说,由于随机梯度下降法一次迭代一个样本,导致迭代方向变化很大,不能很快的收敛到局部最优解。

    那么,有没有一个中庸的办法能够结合两种方法的优点呢?有!这就是4.3的小批量梯度下降法。

4.3 小批量梯度下降法(Mini-batch Gradient Descent)

  小批量梯度下降法是批量梯度下降法和随机梯度下降法的折衷,也就是对于m个样本,我们采用x个样子来迭代,1<x<m。一般可以取x=10,当然根据样本的数据,可以调整这个x的值。对应的更新公式是:

    θi=θiαj=tt+x1(hθ(xj0,xj1,...xjn)yj)xji

代码实现
假定有这样一个对衣服估价的训练样本,代码中matrix表示,第一列表示色彩的评分,第二列表示对料子质地的评分,例如第一个样本1,4表示这件衣服色彩打1分,料子打4分。我们需要训练的是theta,其表示在衣服的估价中,色彩和料子的权重,这个权重是未知量,是需要训练的,训练的依据是这四个样本的真实价格已知,分别为19元,…20元。
通过批量梯度下降和随机梯度下降的方法均可得到theta_C={3,4}T

/*
Matrix_A
1 4
2 5
5 1
4 2
theta_C
?
?
Matrix_A*theta_C
19
26
19
20
*/

1BGD

hθ(x1,x2,...xn)=θ0+θ1x1+...+θnxn
J(θ0,θ1...,θn)=12mi=0m(hθ(x0,x1,...xn)yi)2

θi=θiαj=0m(hθ(xj0,xj1,...xjn)yj)xji

#include<bits/stdc++.h>using namespace std;#define eps 1e-10//给定的数据double x[100][100]={{1,4},{2,5},{5,1},{4,2}};double y[100]={19,26,19,20};int m=4;//初始化数据int n=2;double theta[100]={0,0};double learn_rate=0.1;double error=100.0;//hx表达式//y=O1*x1+O2*x2+...+On*xndouble get_hx(int index){    double ans=0.0;    for(int i=0;i<n;i++) ans+=x[index][i]*theta[i];    return ans;}int main(){   double error1=0.0,error2=0.0;    while(true){        for(int i=0;i<m;i++){            double h_x=get_hx(i);            for(int j=0;j<n;j++){                theta[j]+=(y[i]-h_x)*x[i][j]*learn_rate/m;            }        }        for(int i=0;i<n;i++) cout<<theta[i]<<" ";        cout<<endl;        //找出最小的值,即与上一次的结果相差小于eps        error1=0.0;        for(int i=0;i<m;i++){            error1+=(y[i]-get_hx(i))*(y[i]-get_hx(i))/2/m;        }        if(abs(error1-error2)<eps) break;        else error2=error1;    }    return 0;}

SGD

hθ(x1,x2,...xn)=θ0+θ1x1+...+θnxn
J(θ0,θ1...,θn)=12mi=0m(hθ(x0,x1,...xn)yi)2

θi=θiα(hθ(xj0,xj1,...xjn)yj)xji

#include<bits/stdc++.h>using namespace std;#define eps 1e-10//给定的数据double x[100][100]={{1,4},{2,5},{5,1},{4,2}};double y[100]={19,26,19,20};int m=4;//初始化数据int n=2;double theta[100]={0,0};double learn_rate=0.1;double error=100.0;//hx表达式//y=O1*x1+O2*x2+...+On*xndouble get_hx(int index){    double ans=0.0;    for(int i=0;i<n;i++) ans+=x[index][i]*theta[i];    return ans;}int main(){    double error1=0.0,error2=0.0;    int index=0;    while(true){        int j=index%m;//随机采用一个样本(实际采用顺序)        for(int i=0;i<n;i++){            double h_x=get_hx(j);            theta[i]+=(y[j]-h_x)*x[j][i]*learn_rate/m;        }        index++;        for(int i=0;i<n;i++) cout<<theta[i]<<" ";        cout<<endl;        //找出最小的值,与上一次的记过相差小于eps        error1=0.0;        for(int i=0;i<m;i++){            error1+=(y[i]-get_hx(i))*(y[i]-get_hx(i))/2/m;        }        if(abs(error1-error2)<eps) break;        else error2=error1;    }    return 0;}

MBGD

hθ(x1,x2,...xn)=θ0+θ1x1+...+θnxn
J(θ0,θ1...,θn)=12mi=0m(hθ(x0,x1,...xn)yi)2

θi=θiαj=tt+x1(hθ(xj0,xj1,...xjn)yj)xji

#include<bits/stdc++.h>using namespace std;#define eps 1e-10//给定的数据double x[100][100]={{1,4},{2,5},{5,1},{4,2}};double y[100]={19,26,19,20};int m=4;//初始化数据int n=2;double theta[100]={0,0};double learn_rate=0.1;double error=100.0;//hx表达式//y=O1*x1+O2*x2+...+On*xndouble get_hx(int index){    double ans=0.0;    for(int i=0;i<n;i++) ans+=x[index][i]*theta[i];    return ans;}int main(){    double error1=0.0,error2=0.0;    int index=0,t=10;    while(true){        for(int i=0;i<t;i++){   //并不采用m个数据,而是采用t个            index=(index+i)%m;            double h_x=get_hx(index);            for(int j=0;j<n;j++){                theta[j]+=(y[index]-h_x)*x[index][j]*learn_rate/m;            }        }        for(int i=0;i<n;i++) cout<<theta[i]<<" ";        cout<<endl;        //找出最小的值,与上一次的记过相差小于eps        error1=0.0;        for(int i=0;i<m;i++){            error1+=(y[i]-get_hx(i))*(y[i]-get_hx(i))/2/m;        }        if(abs(error1-error2)<eps) break;        else error2=error1;    }    return 0;}
阅读全文
0 0