当前位置:   article > 正文

随机梯度下降(SGD)、批量梯度下降(BGD)、小批量梯度下降(MSGD)_bp.msgd

bp.msgd


转自:https://www.2cto.com/net/201610/557111.html

接触过神经网络的人都知道,网络的训练是其核心,本人在读书时接触的是BP神经网络,那时写的代码训练样本量不大,没有注意到题目所列出的这些训练方式,偶尔也曾看到了 “批量梯度下降”的名词,却并没有深入研究它的实现过程。

样本是深度学习的主要学习来源,其样本量动则百十万个,再加上其结构和BP网络的不同,虽然在大理论上没变化,但是如果再用老一套方式去训练,其效率和结果都不会理想,这时,一些trick就显现出它的关键了。

研究过深度学习的人士可能对BGD、SGD、MSGD这几种训练方式并不陌生,在Caffe、Touch、TensorFlow等框架工具上,这些就是你用来训练网络所选的方式,你只需要用特定的参数设置就可以实现其对应的训练过程,可是对于其实现的详细方式却未必真正了解,笔者也曾对这几种训练方式有过困惑。其实关于这几个概念的博文有很多,可是总感觉有些地方存在问题:要么公式和代码对不上,要么各种复制粘贴整理得没有头绪(这里不是贬低他人的意思,也可能自己水平不够,还没能达到看了别人的博文就能通七窍的高度,毕竟最后这篇博文的结果还是在别人的博文基础上理解后总结归纳出来的),直到最近看了多篇博文,再结合自己所写的Lenet-5 C语言代码中的训练方式,然后自认为有所参悟了,将理论和代码进行整理后放上来。

二、问题的提出:

 

假设我们提供了这样的数据样本(样本值取自于y=3*x1+4*x2):

 
x1x2y
1419
2526
5119
4229

x1和x2是样本值,y是预测目标,我们需要以一条直线来拟合上面的数据,待拟合的函数如下:

\——1式

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

这是一个线性回归问题,若对线性回归有所了解的同学就知道:利用最小二乘法则和梯度下降法可以求出两个参数,而深度学习也同样可以利用这两种方法求得所有的网络参数,因此,在这里用这个数学模型来解释BGD、SGD、MSGD这几个概念。

三、先熟悉一下梯度下降法:

我们首先确定损失函数:

 

\——2式

其中,J(θ)是损失函数,m代表每次取多少样本进行训练,如果采用SGD进行训练,那每次随机取一组样本,m=1;如果是批处理,则m等于每次抽取作为训练样本的数量。θ是参数,对应(1式)的θ1和θ2。求出了θ1和θ2,h(x)的表达式就出来了:

\——3式

我们的目标是让损失函数J(θ)的值最小,根据梯度下降法,首先要用J(θ)θ求偏导:

\——4式

由于是要最小化损失函数(2式),所以参数θ按其负梯度方向来更新:

\——5式

 

四、现在结合代码来说明(python):

BGD(Batch gradient descent)批量梯度下降法:每次迭代使用所有的样本

每次迭代都需要把所有样本都送入,这样的好处是每次迭代都顾及了全部的样本,做的是全局最优化。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#-*- coding: utf- 8 -*-
import random
#用y = Θ 1 *x1 + Θ 2 *x2来拟合下面的输入和输出
#input1  1   2   5   4
#input2  4   5   1   2
#output  19  26  19  20
input_x = [[ 1 , 4 ], [ 2 , 5 ], [ 5 , 1 ], [ 4 , 2 ]]  #输入
y = [ 19 , 26 , 19 , 20 ]   #输出
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 ]      #求Θ 1 梯度的中间变量 1
err2=[ 0 , 0 , 0 , 0 ]      #求Θ 2 梯度的中间变量 2
 
while ( loss > eps and iter_count < max_iters):   #迭代条件
     loss = 0
     err1sum = 0
     err2sum = 0
     for i in range ( 4 ):     #每次迭代所有的样本都进行训练
         pred_y = theta[ 0 ]*input_x[i][ 0 ]+theta[ 1 ]*input_x[i][ 1 ]  #预测值
         err1[i]=(pred_y-y[i])*input_x[i][ 0 ]
         err1sum=err1sum+err1[i]
         err2[i]=(pred_y-y[i])*input_x[i][ 1 ]
         err2sum=err2sum+err2[i]
     theta[ 0 ] = theta[ 0 ] - step_size * err1sum/ 4  #对应 5
     theta[ 1 ] = theta[ 1 ] - step_size * err2sum/ 4  #对应 5
     for i in range ( 4 ):
         pred_y = theta[ 0 ]*input_x[i][ 0 ]+theta[ 1 ]*input_x[i][ 1 ]   #预测值
         error = ( 1 /( 2 * 4 ))*(pred_y - y[i])** 2  #损失值
         loss = loss + error  #总损失值
     iter_count += 1
     print ( "iters_count" , iter_count)
print ( 'theta: ' ,theta )
print ( 'final loss: ' , loss)
print ( 'iters: ' , iter_count)

 

执行结果:

\

 

这里我们只有4个样本,所以训练的时间不长。但是,如果面对数量巨大的样本量(如40万个),采取这种训练方式,所耗费的时间会非常长。

 

SGD(Stochastic gradientdescent)随机梯度下降法:每次迭代使用一组样本

针对BGD算法训练速度过慢的缺点,提出了SGD算法,普通的BGD算法是每次迭代把所有样本都过一遍,每训练一组样本就把梯度更新一次。而SGD算法是从样本中随机抽出一组,训练后按梯度更新一次,然后再抽取一组,再更新一次,在样本量及其大的情况下,可能不用训练完所有的样本就可以获得一个损失值在可接受范围之内的模型了。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#-*- coding: utf- 8 -*-
import random
#用y = Θ 1 *x1 + Θ 2 *x2来拟合下面的输入和输出
#input1  1   2   5   4
#input2  4   5   1   2
#output  19  26  19  20
input_x = [[ 1 , 4 ], [ 2 , 5 ], [ 5 , 1 ], [ 4 , 2 ]]  #输入
y = [ 19 , 26 , 19 , 20 ]   #输出
theta = [ 1 , 1 ]       #θ参数初始化
loss = 10           #loss先定义一个数,为了进入循环迭代
step_size = 0.01    #步长
eps = 0.0001         #精度要求
max_iters = 10000   #最大迭代次数
error = 0            #损失值
iter_count = 0      #当前迭代次数
 
while ( loss > eps and iter_count < max_iters):    #迭代条件
     loss = 0
     i = random.randint( 0 , 3 )  #每次迭代在input_x中随机选取一组样本进行权重的更新
     pred_y = theta[ 0 ]*input_x[i][ 0 ]+theta[ 1 ]*input_x[i][ 1 ] #预测值
     theta[ 0 ] = theta[ 0 ] - step_size * (pred_y - y[i]) * input_x[i][ 0 ]
     theta[ 1 ] = theta[ 1 ] - step_size * (pred_y - y[i]) * input_x[i][ 1 ]
     for i in range ( 3 ):
         pred_y = theta[ 0 ]*input_x[i][ 0 ]+theta[ 1 ]*input_x[i][ 1 ] #预测值
         error = 0.5 *(pred_y - y[i])** 2
         loss = loss + error
     iter_count += 1
     print ( 'iters_count' , iter_count)
print ( 'theta: ' ,theta )
print ( 'final loss: ' , loss)
print ( 'iters: ' , iter_count)

 

执行结果:

\

 

MBGD(Mini-batch gradient descent)小批量梯度下降:每次迭代使用b组样本

SGD相对来说要快很多,但是也有存在问题,由于单个样本的训练可能会带来很多噪声,使得SGD并不是每次迭代都向着整体最优化方向,因此在刚开始训练时可能收敛得很快,但是训练一段时间后就会变得很慢。在此基础上又提出了小批量梯度下降法,它是每次从样本中随机抽取一小批进行训练,而不是一组。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#-*- coding: utf- 8 -*-
import random
#用y = Θ 1 *x1 + Θ 2 *x2来拟合下面的输入和输出
#input1  1   2   5   4
#input2  4   5   1   2
#output  19  26  19  20
input_x = [[ 1 , 4 ], [ 2 , 5 ], [ 5 , 1 ], [ 4 , 2 ]]  #输入
y = [ 19 , 26 , 19 , 20 ]       #输出
theta = [ 1 , 1 ]           #θ参数初始化
loss = 10               #loss先定义一个数,为了进入循环迭代
step_size = 0.01        #步长
eps = 0.0001             #精度要求
max_iters = 10000       #最大迭代次数
error = 0                #损失值
iter_count = 0          #当前迭代次数
 
 
while ( loss > eps and iter_count < max_iters):  #迭代条件
     loss = 0
     #这里每次批量选取的是 2 组样本进行更新,另一个点是随机点+ 1 的相邻点
     i = random.randint( 0 , 3 )     #随机抽取一组样本
     j = (i+ 1 )% 4                 #抽取另一组样本,j=i+ 1
     pred_y0 = theta[ 0 ]*input_x[i][ 0 ]+theta[ 1 ]*input_x[i][ 1 ]  #预测值 1
     pred_y1 = theta[ 0 ]*input_x[j][ 0 ]+theta[ 1 ]*input_x[j][ 1 ]  #预测值 2
     theta[ 0 ] = theta[ 0 ] - step_size * ( 1 / 2 ) * ((pred_y0 - y[i]) * input_x[i][ 0 ]+(pred_y1 - y[j]) * input_x[j][ 0 ])  #对应 5
     theta[ 1 ] = theta[ 1 ] - step_size * ( 1 / 2 ) * ((pred_y0 - y[i]) * input_x[i][ 1 ]+(pred_y1 - y[j]) * input_x[j][ 1 ])  #对应 5
     for i in range ( 3 ):
         pred_y = theta[ 0 ]*input_x[i][ 0 ]+theta[ 1 ]*input_x[i][ 1 ]     #总预测值
         error = ( 1 /( 2 * 2 ))*(pred_y - y[i])** 2                    #损失值
         loss = loss + error       #总损失值
     iter_count += 1
     print ( 'iters_count' , iter_count)
 
print ( 'theta: ' ,theta )
print ( 'final loss: ' , loss)
print ( 'iters: ' , iter_count)

执行结果:

\

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/319153
推荐阅读
相关标签
  

闽ICP备14008679号