对mini-batch梯度下降算法的理解以及代码实现

1.什么是mini-batch梯度下降

首先我们知道原始梯度下降算法是在整个训练集上进行的,把整个训练集作为数据传入,求解代价和梯度。但是我们在深度学习项目中很多时候数据集是非常庞大的,当我们把整个数据集传入矩阵进行运算的时候速度会很慢,并且对内存的要求也比较高。
mini-batch梯度下降(MBGD,Mini-Batch Gradient Descent)就是把数据集分为若干个子集,然后每次把每个子集作为我们的训练数据进行梯度下降。例如样本数为m,每一个batch的大小为64,那么我们就可以分为m/64个样本,如果m%64不等于0说明还有剩的样本,则第m/64+1个batch不足64,大小就等于m%64。

为什么可以这样分,进行梯度下降?其实很好理解,我们知道原始的梯度下降算法的过程就是求出代价函数对权重参数的偏导 ∂ J w \frac{∂J}{w} wJ,然后进行更新 w = w − α ∗ ∂ J w w = w - \alpha*\frac{∂J}{w} w=wαwJ.
其中代价函数 J = ∑ i = 1 m L ( y ( i ) , y ^ ( i ) ) J=\sum_{i=1}^{m}L(y^{(i)},\hat{y}^{(i)}) J=i=1mL(y(i),y^(i)) y ( i ) y^{(i)} y(i)是真实值, y ^ ( i ) \hat{y}^{(i)} y^(i)是预测值,L就是损失函数,代价函数J就是在所有样本上损失值的和。
现在使用mini-batch梯度下降算法,假设我们的batchsize=64,每个batch的代价为: J 1 = ∑ i = 1 64 L ( y ( i ) , y ^ ( i ) ) J_1=\sum_{i=1}^{64}L(y^{(i)},\hat{y}^{(i)}) J1=i=164L(y(i),y^(i)) J 2 = ∑ i = 1 64 L ( y ( i ) , y ^ ( i ) ) J_2=\sum_{i=1}^{64}L(y^{(i)},\hat{y}^{(i)}) J2=i=164L(y(i),y^(i)),… J m / 64 = ∑ i = 1 64 L ( y ( i ) , y ^ ( i ) ) J_{m/64}=\sum_{i=1}^{64}L(y^{(i)},\hat{y}^{(i)}) Jm/64=i=164L(y(i),y^(i))
根据求导数的性质我们可以知道,对和的求导可以分别对每部分求导然后再加起来,所以有: ∂ J w = ∂ J 1 w + ∂ J 2 w + . . . + ∂ J m / 64 w \frac{∂J}{w}=\frac{∂J_1}{w}+\frac{∂J_2}{w}+...+\frac{∂J_{m/64}}{w} wJ=wJ1+wJ2+...+wJm/64,然后我们在对每个batch进行梯度下降的时候,就是 w = w − α ∗ ∂ J i w w = w - \alpha*\frac{∂J_i}{w} w=wαwJi,然后遍历所有batch的过程就等价于: w = w − α ∗ ∂ J 1 w − α ∗ ∂ J 2 w − . . . − α ∗ ∂ J m / 64 w = w − α ∗ ( ∂ J 1 w + ∂ J 2 w + . . . + ∂ J m / 64 w ) = w − α ∗ ∂ J w w = w - \alpha*\frac{∂J_1}{w}-\alpha*\frac{∂J_2}{w}-...-\alpha*\frac{∂J_{m/64}}{w}=w-\alpha*(\frac{∂J_1}{w}+\frac{∂J_2}{w}+...+\frac{∂J_{m/64}}{w})=w-\alpha*\frac{∂J}{w} w=wαwJ1αwJ2...αwJm/64=wα(wJ1+wJ2+...+wJm/64)=wαwJ

这里顺便说一下,原始的梯度下降算法也就是把整个训练集输入的算法称为BGD(Batch Gradient Descent)
当batchsize=1的时候,也称为随机梯度下降,SGD(Stochastic Gradient Descent)

2.mini-batch梯度下降算法的伪代码

repeat num iterations{
	遍历每一个batch{
		1.前向传播:(1)计算Z=W*X+b
				    (2)计算激活项的值A=g(Z)
		2.计算代价J
		3.反向传播求解梯度
		4.更新权重
	}
}

3.为什么要使用mini-batch梯度下降算法

最近做了一些深度学习方面的小项目,发现无一例外几乎都会用到mini-batch梯度下降算法,即使是用tensorflow,keras框架封装好的函数,我们也会用指定batchsize的大小。
因为我们每次循环都是用的训练集的子集来进行梯度下降,和我们原始的梯度下降算法相比,当训练集很大时,mini-batch能加快我们的参数更新速度。
和SGD相比,因为SGD的batchsize=1,和我们mini-batch相比失去了矩阵运算速度快的优势,因为在numpy中的矩阵计算是进行了优化了的,和循环相比,速度提高了不少,可以通过下面的例子来看看。

import numpy
import time

a = np.random.rand(1000000)
b = np.random.rand(1000000)
start_time = time.clock()
c = np.dot(a, b)
end_time = time.clock()
print("点积运算,向量化所花费的时间:"+str(1000*(end_time - start_time))+"ms")

c = 0
start_time = time.clock()
for i in range(1000000):
    c += a[i] * b[i]
end_time = time.clock()
print("点积运算,循环所花费的时间:"+str(1000*(end_time - start_time))+"ms")

运行结果:(通过结果时间比较我们可以看出向量化的优势)
在这里插入图片描述

4.比较BGD,SGD,MBGD

(1)通过下面的图,我们可以看到mini-batch梯度下降并不是每一次迭代代价都会下降,,而是存在一些噪声,存在噪声的原因主要是因为我们可能有的batch比较好计算,计算得到的成本比较小,而有的batch计算得到的成本比较大,但是总的依旧是呈下降趋势。
在这里插入图片描述
(2)batch梯度下降单次迭代耗时比较长,当数据集太大的时候不是很理想。
随机梯度下降由于batchsize=1,每次只对一个样本进行梯度下降,所以没法利用向量化加速的优势。
mini-batch梯度下降更像是综合了上面两种,学习效率比较快。
在这里插入图片描述
上图中蓝色的是batch梯度下降,紫色的是随机梯度下降,绿色的是mini-batch梯度下降。

5.用python实现mini-batch梯度下降

首先我们要将我们的训练集随机分割为指定大小的batch:

def random_mini_batches(X,Y,mini_batch_size=64,seed=0):
'''
输入:X的维度是(n,m),m是样本数,n是每个样本的特征数
'''
    np.random.seed(seed)
    m = X.shape[1]
    mini_batches = []
    #step1:打乱训练集
	#生成0~m-1随机顺序的值,作为我们的下标
    permutation = list(np.random.permutation(m))
    #得到打乱后的训练集
    shuffled_X = X[:,permutation]
    shuffled_Y = Y[:,permutation].reshape((1,m))
	#step2:按照batchsize分割训练集
	#得到总的子集数目,math.floor表示向下取整
    num_complete_minibatches = math.floor(m / mini_batch_size)
    for k in range(0,num_complete_minibatches):
    	#冒号:表示取所有行,第二个参数a:b表示取第a列到b-1列,不包括b
        mini_batch_X = shuffled_X[:, k * mini_batch_size:(k+1) * mini_batch_size]
        mini_batch_Y = shuffled_Y[:, k * mini_batch_size:(k+1) * mini_batch_size]

        mini_batch = (mini_batch_X, mini_batch_Y)
        mini_batches.append(mini_batch)
	#m%mini_batch_size != 0表示还有剩余的不够一个batch大小,把剩下的作为一个batch
    if m % mini_batch_size != 0:
        mini_batch_X = shuffled_X[:,mini_batch_size * num_complete_minibatches:]
        mini_batch_Y = shuffled_Y[:,mini_batch_size * num_complete_minibatches:]

        mini_batch = (mini_batch_X, mini_batch_Y)
        mini_batches.append(mini_batch)

    return mini_batches

上面代码中不理解 np.random.seed(seed)可以看这里[numpy.random.seed()的使用]
进行mini-batch梯度下降:

    for i in range(num_epochs):
        seed = seed + 1
        minibatches = random_mini_batches(X,Y,mini_batch_size,seed)

        for minibatch in minibatches:
            (minibatch_X, minibatch_Y) = minibatch

            A3, cache = opt_utils.forward_propagation(minibatch_X, parameters)

            cost = opt_utils.compute_cost(A3, minibatch_Y)

            grads = opt_utils.backward_propagation(minibatch_X,minibatch_Y,cache)

            parameters = update_parameters_with_gd(parameters,grads,learning_rate)

通过上面的代码我们可以发现我们在每一次epoch迭代的时候,都会打乱数据,随机分割数据集。
这是因为神经网络参数多,学习能力强,如果不乱序的话,同一个组合的batch反复出现,模型有可能会“记住”这些样本的次序,从而影响泛化能力。(摘自网友的回答)

  • 20
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值