机器学习理论系列2——梯度下降法

什么是优化算法

优化算法要求解的,是一个问题的最优解或者近似最优解。在机器学习中,有很多问题都是优化问题,即我们要求出输入特征和标签之间的映射关系,并且我们一定是期望,通过这个映射关系得到的标签结果,和实际标签之间的误差最小。

机器学习中,优化算法有很多种,最基本的,也是应用非常广泛的,就是梯度上升/下降,此外还有遗传算法等。

什么是梯度下降

当我们得到一个目标函数之后,如何求解它的参数?直接求解?并不一定可解,线性回归算是一个特例(使用最小二乘法求解)。

梯度上升/下降,是机器学习中求解无约束优化问题时,最常用的方法之一。
它的套路是,喂给机器一堆数据,告诉它什么样的方式是对的(目标函数),然后让它自己朝着这个方向去做。

那么怎么知道【方向】呢?这个方向,就是我们说的梯度。在微积分里边,对多元函数的参数求∂偏导数,把求得的偏导数用向量表示出来,就是梯度。沿着梯度的方向,函数值的变化最快。

所以,如果我们要求损失函数的最大值,就采用梯度上升的方法,沿着梯度方向一步步迭代,得到最大值;反之,如果我们要求损失函数的最小值,就采用梯度下降,沿着梯度的反方向,一步步迭代,得到最小值。

所以,梯度下降法,就是一个用于【寻找最小化损失函数的参数值】的【最优化算法】。

梯度下降的视觉直观解释

我们把下面的图形,想象成两座山,假设我们站在【Initial Condition】这个山顶点,我们想下到山的最底部,但是不知道往哪个方向走最快。于是我们每走一步,都计算一下当前所在位置的梯度,即最陡峭、下山最快的方向,然后沿着这个方向走。这样走一步算一步,最终就走到了山底。这就是梯度下降法,它的要点是:【梯度】和【迭代】,即沿着函数值变化最快的方向,每走一步,计算一次,不断迭代,从而找到最优解。

梯度下降

当然按照梯度下降法走下去,我们不一定走到了两座山的最低点(图中的Global Minimum),而是走到了一个局部最低点(图中次低点)。所以梯度下降法得到的不一定是全局最优解,而是局部最优解。

不过机器学习中我们遇到的很多损失函数,都是凸函数,如线性回归对应的就是凸函数。如果只有一个特征变量,凸函数的形状类似于下面的图形,分别为下凸和上凸:

如果有两个特征变量,则类似于下面这个碗的造型。如果是凸函数,采用梯度上升/下降法,得到的一定是全局最优解。

最小二乘法和梯度下降法的关系

最小二乘法的目标是,求解最小误差平方和,有两种形式:线性和非线性;线性的解可以直接用数学方法推导出来,也可以用迭代的方法求解;非线性的解,需要用迭代的方法求解。
梯度下降法是一种迭代求解的方法,可以用来求解最小二乘问题(线性和非线性的都可以)。

梯度下降相关概念

1. 步长:在神经网络中也叫学习速率(Learning Rate),步长决定了在在梯度下降迭代过程中,每一步沿着梯度负方向前进的长度。

2. 损失函数:也叫成本函数;用来度量拟合的程度,损失函数极小化,意味着拟合程度最好,对应的模型参数即为最优解。损失函数的定义如下:

损失函数

其中m代表样本数量,1/m是为了平均化,因为如果m=100,右边的平方和所得结果,肯定比m=1000的时候小很多,但这并不能代表m=100的时候计算所得参数值要优于m=1000的时候的参数值。所以这里取平均值,消除这个影响。

特征数据归一化

样本的特征取值范围可能都不一样,这可能导致迭代速度很慢。但是不能因为想要加速下降速度,就把下降步伐调整过大,否则找到函数最小值的速度反而容易变慢,出现overshoot the minimum的现象,即如下图所示:

overshoot the minimum

为了加快迭代速度,我们可以采用Feature Scaling的方法,对特征数据进行归一化处理,来加速下降的执行速度。即将各个特征值标准化,使他们的取值范围都在(-1,1)之间。这样迭代速度会大大加快。

常用的方法是均值归一化(Mean Normalization),即:

均值归一化

或者求标准分(Standard Score),即:

标准化

算法流程

  1. 初始化:初始化θ参数,步长α,终止距离ε

  2. 循环操作:

    2.1 计算当前位置的损失函数对应的梯度,通过求损失函数的偏导获得:

    2.2 步长 * 损失函数的梯度 = 当前位置下降的距离;

    2.3 判断是否所有的θ参数,梯度下降的距离都小于ε,如果是,则算法终止;否则进入下一步;

    2.4 更新所有θ参数,更新表达式如下:
  3. 输出最优解

梯度下降的三种方式

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

批量梯度下降法,即在计算梯度时,使用所有的样本数据来计算。如果我们有m个样本,求梯度的时候,就用到m个样本的梯度数据。

这种方法容易得到最优解。但是每次都要考虑所有的样本数据,迭代速度非常慢。尤其是在样本数据量巨大的时候。

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

和批量梯度下降法的区别,仅仅在于,在求梯度时,没有用到所有样本的数据,而是随机选取其中一个样本来计算梯度。

这种方法的优点是迭代速度很快。但是每次只选择一个样本,所以不一定每次都是朝着全局最优的方向,不过大方向是朝着全局最优的。

小批量梯度下降法 MBGD(Mini-Batch Gradient Descent)

这是最实用的一种方法,综合了批量和随机的特点,每次选择一小批样本数据(32,64,128比较常见)来计算平均梯度的更新方向。

三种算法的Python实现和说明

假设我们有这样的样本数据:

x1 x2 y
1 4 19
2 5 26
5 1 19
4 2 29
3 5 29
8 1 28
7 2 29
1 2 11
6 2 24
9 3 39

我们需要一个函数来拟合上面的数据,这个函数为:

我们的目标是求出θ1和θ2,让h(θ)尽量逼近实际值y。

我们结合Python和实际案例,来说明一下三种算法。参考自:批量梯度下降(BGD)、随机梯度下降(SGD)、小批量随机梯度下降(MSGD)实现过程详解

批量梯度下降法

#BGD
input_x = [[1,4], [2,5], [5,1], [4,2],[3,5],[8,1],[7,2],[1,2],[6,2],[9,3]]  #输入
y = [19,26,19,20,28,25,27,13,28,37]   #输出
theta = [1,1]       #θ参数初始化
loss = 10           #loss先定义一个数,为了进入循环迭代
step_size = 0.01    #步长
eps =0.0001         #精度要求
max_iters = 10000   #最大迭代次数
error =0            #损失值
iter_count = 0      #当前迭代次数

err1=[0,0,0,0,0,0,0,0,0,0]      #求Θ1梯度的中间变量1
err2=[0,0,0,0,0,0,0,0,0,0]      #求Θ2梯度的中间变量2

while(loss > eps and iter_count < max_iters):
    loss = 0
    err1num = 0
    err2num = 0
    for i in range(10):    #我们共有10个样本,每次迭代所有样本计算梯度
        pred_y = theta[0]*input_x[i][0] + theta[1]*input_x[i][1]    #第i个样本对应的预测值
        err1[i] = (pred_y - y[i]) * input_x[i][0]               
        err2[i] = (pred_y - y[i]) * input_x[i][1]                 
        err1num = err1num + err1[i]                                 #Θ1对应的梯度    
        err2num = err2num + err2[i]                                 #Θ2对应的梯度 
        
    theta[0] = theta[0] - step_size * err1num/10                    #更新Θ1对应的梯度 
    theta[1] = theta[1] - step_size * err2num/10                    #更新Θ2对应的梯度
    
    for i in range(10):
        pred_y = theta[0]*input_x[i][0] + theta[1]*input_x[i][1]    #更新Θ1和Θ2之后重新计算预测值
        error = (1/(2*10))*(pred_y - y[i])**2                       #计算损失值
        loss = loss + error
    
    iter_count += 1

print('theta:', theta)
print('final loss:', loss)
print('iter counts:', iter_count)

得到结果为:
theta: [2.798690040454637, 4.119758556475943]
final loss: 0.9069318692609009
iter counts: 10000

随机梯度下降法

#SGD
input_x = [[1,4], [2,5], [5,1], [4,2],[3,5],[8,1],[7,2],[1,2],[6,2],[9,3]]  #输入
y = [19,26,19,20,28,25,27,13,28,37]   #输出
theta = [1,1]       #θ参数初始化
loss = 10           #loss先定义一个数,为了进入循环迭代
step_size = 0.01    #步长
eps =0.0001         #精度要求
max_iters = 1000000   #最大迭代次数
error =0            #损失值
iter_count = 0      #当前迭代次数

err1=[0,0,0,0,0,0,0,0,0,0]      #求Θ1梯度的中间变量1
err2=[0,0,0,0,0,0,0,0,0,0]      #求Θ2梯度的中间变量2

while(loss > eps and iter_count < max_iters):
    loss = 0
    err1num = 0
    err2num = 0
    i = random.randint(0,3)    #随机选取10个样本中的一个,由于样本数增加,导致迭代次数增多,这里缩减为从前4个样本中取数
    pred_y = theta[0]*input_x[i][0] + theta[1]*input_x[i][1]    #计算选取的样本对应的预测值
    err1num = (pred_y - y[i]) * input_x[i][0]                   #Θ1对应的梯度  
    err2num = (pred_y - y[i]) * input_x[i][1]                   #Θ2对应的梯度 
        
    theta[0] = theta[0] - step_size * err1num                   #更新Θ1对应的梯度 
    theta[1] = theta[1] - step_size * err2num                   #更新Θ2对应的梯度
    
    for i in range(4):
        pred_y = theta[0]*input_x[i][0] + theta[1]*input_x[i][1]#更新Θ1和Θ2之后重新计算预测值
        error = (1/2)*(pred_y - y[i])**2                        #计算损失值
        loss = loss + error
    
    iter_count += 1

print('theta:', theta)
print('final loss:', loss)
print('iter counts:', iter_count)

计算所得结果:
theta: [3.001936311335262, 3.9976736608405257]
final loss: 8.908461998310991e-05
iter counts: 122

小批量梯度下降法

#MBGD
input_x = [[1,4], [2,5], [5,1], [4,2],[3,5],[8,1],[7,2],[1,2],[6,2],[9,3]]  #输入
y = [19,26,19,20,28,25,27,13,28,37]   #输出
theta = [1,1]       #θ参数初始化
loss = 10           #loss先定义一个数,为了进入循环迭代
step_size = 0.01    #步长
eps =0.0001         #精度要求
max_iters = 100000   #最大迭代次数
error =0            #损失值
iter_count = 0      #当前迭代次数

err1=[0,0,0,0,0,0,0,0,0,0]      #求Θ1梯度的中间变量1
err2=[0,0,0,0,0,0,0,0,0,0]      #求Θ2梯度的中间变量2

while(loss > eps and iter_count < max_iters):
    loss = 0
    err1num = 0
    err2num = 0
    #这里总体样本取前4个,每批次选取其中两个样本
    i = random.randint(0,3)    #缩减为从前4个样本中随机抽取一个样本
    j = (i+1)%4                #选取与i相邻的样本为第二个样本
    pred_yi = theta[0]*input_x[i][0] + theta[1]*input_x[i][1]    #计算选取的样本i对应的预测值
    pred_yj = theta[0]*input_x[j][0] + theta[1]*input_x[j][1]    #计算选取的样本j对应的预测值
    err1num = (pred_yi - y[i]) * input_x[i][0] + (pred_yj - y[j]) * input_x[j][0]    #Θ1对应的梯度  
    err2num = (pred_yi - y[i]) * input_x[i][1] + (pred_yj - y[j]) * input_x[j][1]    #Θ1对应的梯度  
        
    theta[0] = theta[0] - step_size * (1/2) * err1num                   #更新Θ1对应的梯度 
    theta[1] = theta[1] - step_size * (1/2) * err2num                   #更新Θ2对应的梯度
    
    for i in range(4):
        pred_y = theta[0]*input_x[i][0] + theta[1]*input_x[i][1]#更新Θ1和Θ2之后重新计算预测值
        error = (1/2*2)*(pred_y - y[i])**2                        #计算损失值
        loss = loss + error
    
    iter_count += 1

print('theta:', theta)
print('final loss:', loss)
print('iter counts:', iter_count)

计算所得结果:
theta: [3.0015170025160156, 3.9983740403913126]
final loss: 9.42763188681316e-05
iter counts: 116

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容