点击上方“视学算法”,选择加"星标"或“置顶

重磅干货,第一时间送达

作者丨陈志远@知乎(已授权)

来源丨https://zhuanlan.zhihu.com/p/83626029

编辑丨极市平台

导读

深度学习中batch size的大小对训练过程的影响是什么样的?训练过程中超大batch有什么优缺点,如何尽可能地避免超大batch带来的负面影响?本文对这两个问题进行了详细的分析和解释。

之前面试过程中被问到过两个问题:

(1)深度学习中batch size的大小对训练过程的影响是什么样的?

(2)有些时候不可避免地要用超大batch,比如人脸识别,可能每个batch要有几万甚至几十万张人脸图像,训练过程中超大batch有什么优缺点,如何尽可能地避免超大batch带来的负面影响?

面试版回答

不考虑Batch Normalization的情况下(这种情况我们之后会在bn的文章里专门探讨),先给个自己当时回答的答案吧(相对来说学究一点):

(1) 不考虑bn的情况下,batch size的大小决定了深度学习训练过程中的完成每个epoch所需的时间每次迭代(iteration)之间梯度的平滑程度。(感谢评论区的韩飞同学提醒,batchsize只能说影响完成每个epoch所需要的时间,决定也算不上吧。根本原因还是CPU,GPU算力吧。瓶颈如果在CPU,例如随机数据增强,batch size越大有时候计算的越慢。

对于一个大小为N的训练集,如果每个epoch中mini-batch的采样方法采用最常规的N个样本每个都采样一次,设mini-batch大小为b,那么每个epoch所需的迭代次数(正向+反向), 因此完成每个epoch所需的时间大致也随着迭代次数的增加而增加。

由于目前主流深度学习框架处理mini-batch的反向传播时,默认都是先将每个mini-batch中每个instance得到的loss平均化之后再反求梯度,也就是说每次反向传播的梯度是对mini-batch中每个instance的梯度平均之后的结果,所以b的大小决定了相邻迭代之间的梯度平滑程度,b太小,相邻mini-batch间的差异相对过大,那么相邻两次迭代的梯度震荡情况会比较严重,不利于收敛;b越大,相邻mini-batch间的差异相对越小,虽然梯度震荡情况会比较小,一定程度上利于模型收敛,但如果b极端大,相邻mini-batch间的差异过小,相邻两个mini-batch的梯度没有区别了,整个训练过程就是沿着一个方向蹭蹭蹭往下走,很容易陷入到局部最小值出不来。

总结下来:batch size过小,花费时间多,同时梯度震荡严重,不利于收敛;batch size过大,不同batch的梯度方向没有任何变化,容易陷入局部极小值。

(2)(存疑,只是突发奇想)如果硬件资源允许,想要追求训练速度使用超大batch,可以采用一次正向+多次反向的方法,避免模型陷入局部最小值。即使用超大epoch做正向传播,在反向传播的时候,分批次做多次反向转播,比如将一个batch size为64的batch,一次正向传播得到结果,instance级别求loss(先不平均),得到64个loss结果;反向传播的过程中,分四次进行反向传播,每次取16个instance的loss求平均,然后进行反向传播,这样可以做到在节约一定的训练时间,利用起硬件资源的优势的情况下,避免模型训练陷入局部最小值。

通俗版回答

那么我们可以把第一个问题简化为一个小时候经常玩的游戏:

深度学习训练过程:贴鼻子

训练样本:负责指挥的小朋友们(观察角度各不一样)

模型:负责贴的小朋友

模型衡量指标:最终贴的位置和真实位置之间的距离大小

由于每个小朋友站的位置各不一样,所以他们对鼻子位置的观察也各不一样。(训练样本的差异性),这时候假设小明是负责贴鼻子的小朋友,小朋友A、B、C、D、E是负责指挥的同学(A, B站在图的右边,C,D, E站在左边),这时候小明如果采用:

  1. 每次随机询问一个同学,那么很容易出现,先询问到了A,A说向左2cm,再问C,C说向右5cm,然后B,B说向左4cm,D说向右3cm,这样每次指挥的差异都比较大,结果调过来调过去,没什么进步。

  2. 每次随机询问两个同学,每次取询问的意见的平均,比如先问到了(A, C),A说向左2cm,C说向右5cm,那就取个均值,向右1.5cm。然后再问(B, D),这样的话减少了极端情况(前后两次迭代差异巨大)这种情况的发生,能更好更快的完成游戏。

  3. 每次全问一遍,然后取均值,这样每次移动的方向都是所有人决定的均值,这样的话,最后就是哪边的小朋友多最终结果就被很快的拉向哪边了。(梯度方向不变,限于极小值)

科学版回答

就用MINST做一下实验吧(代码主要转自cnblogs.com/jiangnanyan):

  • cnblogs.com/jiangnanyan

  • 链接:https://www.cnblogs.com/jiangnanyanyuchen/p/9782223.html

实验环境:

1080ti * 1

Pytorch 0.4.1

超参数:SGD(lr = 0.02, momentum=0.5) 偷懒没有根据batch size细调

我们先创建一个简单的模型:

from torch.nn import *
import torch.nn.functional as F
class SimpleModel(Module):def __init__(self):super(SimpleModel, self).__init__()self.conv1 = Conv2d(in_channels=1, out_channels=10, kernel_size=5)self.conv2 = Conv2d(10, 20, 5)self.conv3 = Conv2d(20, 40, 3)self.mp = MaxPool2d(2)self.fc = Linear(40, 10)def forward(self, x):in_size = x.size(0)x = F.relu(self.mp(self.conv1(x)))x = F.relu(self.mp(self.conv2(x)))x = F.relu(self.mp(self.conv3(x)))x = x.view(in_size, -1)x = self.fc(x)print(x.size())return F.log_softmax(x, dim=1)

然后把MINST加载出来训练一下:

import time
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import os
from torchvision import datasets, transforms
from simple_model import SimpleModelos.environ['CUDA_VISIBLE_DEVICES'] = "0"
use_cuda = torch.cuda.is_available()
batch_size = 6
lr = 1e-2
# MNIST Datasettrain_dataset = datasets.MNIST(root='./data/',train=True,transform=transforms.ToTensor(),download=True)test_dataset = datasets.MNIST(root='./data/',train=False,transform=transforms.ToTensor())# Data Loader (Input Pipeline)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True)test_loader = torch.utils.data.DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=False)model = SimpleModel()
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.5)if use_cuda:model = nn.DataParallel(model).cuda()
iter_losses = []
def train(epoch):model.train()total_loss = 0compution_time = 0e_sp = time.time()for batch_idx, (data, target) in enumerate(train_loader):if use_cuda:data = data.cuda()target = target.cuda()b_sp = time.time()output = model(data)loss = F.nll_loss(output, target)optimizer.zero_grad()loss.backward()optimizer.step()compution_time += time.time() - b_sp# optimizer.step()epoch_time = time.time() - e_spprint('Train Epoch: {} \t Loss: {:.6f}\t epoch time: {:.6f} s\t epoch compution time: {:.6f} s'.format(epoch, total_loss / len(train_loader), epoch_time, compution_time))return total_loss / len(train_loader)
def test():model.eval()with torch.no_grad():test_loss = 0correct = 0for data, target in test_loader:if use_cuda:data = data.cuda()target = target.cuda()output = model(data)# sum up batch losstest_loss += F.nll_loss(output, target).item()# get the index of the max log-probabilitypred = output.data.max(1, keepdim=True)[1]correct += pred.eq(target.data.view_as(pred)).cpu().sum()test_loss /= len(test_loader)print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(test_loss, correct, len(test_loader.dataset),100. * correct / len(test_loader.dataset)))return test_loss, 100. * correct.item() / len(test_loader.dataset)if __name__ == "__main__":for epoch in range(1, 10000):train_l = train(epoch)val_l, val_a = test()

我们从以下指标来看一下不同batch size之间的区别:

  • 迭代速度

感觉之前做的实验有点不太科学,重新捋了一下思路,把时间计算的代码也放到了前面的代码之中,有兴趣的同学也可以自己做一下看看。

(表中 Epoch Time是在此batch size下完成一个epoch所需的所有时间,包括加载数据和计算的时间,Epoch Computation Time抛去了加载数据所需的时间。)

(时间确实是有偏量,上面的数据可以大体做个参考,要做科学考究的话,还是要多做几次实验求均值减少偏差。)

其实纯粹cuda计算的角度来看,完成每个iter的时间大batch和小batch区别并不大,这可能是因为本次实验中,反向传播的时间消耗要比正向传播大得多,所以batch size的大小对每个iter所需的时间影响不明显,未来将在大一点的数据库和更复杂的模型上做一下实验。(因为反向的过程取决于模型的复杂度,与batchsize的大小关系不大,而正向则同时取决于模型的复杂度和batch size的大小。而本次实验中反向的过程要比正向的过程时间消耗大得多,所以batch size的大小对完成每个iter所需的耗时影响不大。)

完成每个epoch运算的所需的全部时间主要卡在:1. load数据的时间,2. 每个epoch的iter数量。因此对于每个epoch,不管是纯计算时间还是全部时间,大体上还是大batch能够更节约时间一点,但随着batch增大,iter次数减小,完成每个epoch的时间更取决于加载数据所需的时间,此时也不见得大batch能带来多少的速度增益了。

  • 梯度平滑程度

我们再来看一下不同batch size下的梯度的平滑程度,我们选取了每个batch size下前1000个iter的loss,来看一下loss的震荡情况,结果如下图所示:

如果感觉这张图不太好看,可以看一下这张图:

由于现在绝大多数的框架在进行mini-batch的反向传播的时候,默认都是将batch中每个instance的loss平均化之后在进行反向传播,所以相对大一点的batch size能够防止loss震荡的情况发生。从这两张图中可以看出batch size越小,相邻iter之间的loss震荡就越厉害,相应的,反传回去的梯度的变化也就越大,也就越不利于收敛。同时很有意思的一个现象,batch size为1的时候,loss到后期会发生爆炸,这主要是lr=0.02设置太大,所以某个异常值的出现会严重扰动到训练过程。这也是为什么对于较小的batchsize,要设置小lr的原因之一,避免异常值对结果造成的扰巨大扰动。而对于较大的batchsize,要设置大一点的lr的原因则是大batch每次迭代的梯度方向相对固定,大lr可以加速其收敛过程。

  • 收敛速度

在衡量不同batch size的优劣这一点上,我选用衡量不同batch size在同样参数下的收敛速度快慢的方法。

下表中可以看出,在minst数据集上,从整体时间消耗上来看(考虑了加载数据所需的时间),同样的参数策略下 (lr = 0.02, momentum=0.5 ),要模型收敛到accuracy在98左右,batchsize在 6 - 60 这个量级能够花费最少的时间,而batchsize为1的时候,收敛不到98;batchsize过大的时候,因为模型收敛快慢取决于梯度方向和更新次数,所以大batch尽管梯度方向更为稳定,但要达到98的accuracy所需的更新次数并没有量级上的减少,所以也就需要花费更多的时间,当然这种情况下可以配合一些调参策略比如warmup LR,衰减LR等等之类的在一定程度上进行解决(这个先暂且按下不表),但也不会有本质上的改善。

不过单纯从计算时间上来看,大batch还是可以很明显地节约所需的计算时间的,原因前面讲过了,主要因为本次实验中纯计算时间中,反向占的时间比重远大于正向。

(我一直觉得直接比较不同batch size下的绝对收敛精度来衡量batch size的好坏是没有太大意义的,因为不同的batch size要配合不同的调参策略用才能达到其最佳效果,而要想在每个batch size下都找到合适的调参策略那可太难了,所以用这种方法来决定batch size未免有点武断。)

一次正向,多次反向

这部分在pytorch上进行了实验,但发现pytorch在backward中加入retain_graph进行多次反向传播的时候,时间消耗特别大,所以尽管采用一次正向,多次反向的方法,减少了超大batch size收敛到98的accuracy所需的iteration,但由于每个iteration时间消耗增加,所以并没有带来时间节省,我后续还要探究一下原因,再重新做一下实验,然后再贴结果,给结论。

做了几次实验,基本失败,一个大batch分成10份反向传播,基本等同于lr放大10倍。大batch还是要配合着更复杂的lr策略来做,比如warmup,循环lr,lr衰减等等。

实验的漏洞

为了保证独立变量,我在实验中不同batch设置了同样的lr,然后比较收敛速度,这样是不公平的,毕竟大batch还是要配合更大的初始lr,所以后续还要做一下实验,固定每个batch size, 看lr的变化对不同batch size收敛素的的影响。

如果觉得有用,就请分享到朋友圈吧!

点个在看 paper不断!

浅析深度学习中Batch Size大小对训练过程的影响相关推荐

  1. 深入剖析深度学习中Batch Size大小对训练过程的影响

    点击上方"AI算法与图像处理",选择加"星标"或"置顶" 重磅干货,第一时间送达 推荐文章[点击下面可直接跳转]: 来源:https://z ...

  2. batch size 训练时间_深度学习 | Batch Size大小对训练过程的影响

    转自:面试中问你 Batch Size大小对训练过程的影响​mp.weixin.qq.com 先看两个问题: (1)深度学习中batch size的大小对训练过程的影响是什么样的? (2)有些时候不可 ...

  3. 深度学习中 Batch Size 对训练过程的影响

    作者 | 陈志远 编辑丨极市平台 之前面试过程中被问到过两个问题: (1)深度学习中batch size的大小对训练过程的影响是什么样的? (2)有些时候不可避免地要用超大batch,比如人脸识别,可 ...

  4. 深度学习中Batch、Iteration、Epoch的概念与区别

    在神经网络训练中,一般采用小批量梯度下降的方式. Batch Epoch Iteration 就是其中的重要的概念.我们要理解懂得它们都是什么以及它们之间的区别. 1.Batch 每次迭代时使用的一批 ...

  5. 浅析深度学习中的mask操作

    mask(掩码.掩膜)是深度学习中的常见操作.简单而言,其相当于在原始张量上盖上一层掩膜,从而屏蔽或选择一些特定元素,因此常用于构建张量的过滤器(见下图). 按照上述定义,非线性激活函数Relu(根据 ...

  6. 深度学习中学习率和batchsize对模型准确率的影响

    本内容来自其他的人解析,参考链接在最后的注释. 1. 前言 目前深度学习模型多采用批量随机梯度下降算法进行优化,随机梯度下降算法的原理如下: n是批量大小(batchsize),η是学习率(learn ...

  7. 五个角度解释深度学习中 Batch Normalization为什么效果好?

    https://www.toutiao.com/a6699953853724361220/ 深度学习模型中使用Batch Normalization通常会让模型得到更好表现,其中原因到底有哪些呢?本篇 ...

  8. 浅析深度学习中优化方法

    目前而言,深度学习是机器学习的发展前沿,一般针对大数据量的学习目标.其优化方法来源于基本的机器学习的优化方法,但也有所不同. 下面,小结一下,其基础是随机梯度下降的方法,但是为了学习的自适应性,做了如 ...

  9. 任奎:人工智能算法安全浅析——深度学习中的对抗攻击与防御

    2020-05-19 19:52:46 任奎 随着计算机产业发展带来的计算性能与处理能力的大幅提高,人工智能在音视频识别.自然语言处理和博弈论等领域得到了广泛应用.在此背景下,确保人工智能的核心--深 ...

最新文章

  1. jenkins android sdk,Jenkins为什么找不到Android SDK?
  2. 【学习笔记】37、用正则表达式解析和提取数据
  3. 新思路保障网络安全 基于平台的网络安全架构体系
  4. 微信小程序动态点赞php,在微信小程序中如何实现点赞功能
  5. pymysql连接mysql数据库try_pymysql 连接数据库和基本使用
  6. OLED电视出现烧屏问题 LG电子被判赔偿消费者16万澳元
  7. USB转串口 TTL RS-232 RS-485 COM口 UART区别
  8. 开源在线视频播放器flowplayer
  9. Oxygen XML Editor(XML编辑器)v21.0专业破解版
  10. ts 报错:‘new‘ expression, whose target lacks a construct signature, implicitly has an ‘any‘ type.
  11. workman+thinkPHP 即时通讯
  12. White自动化测试培训大纲
  13. Maven实战(四)--坐标
  14. 地方征信平台第2讲:河北省征信
  15. 阿里云HaaS100物联网开发板学习笔记(二)硬件控制初步--让小灯闪烁起来
  16. Java实现之普利姆(Prim)算法
  17. 十字路口倒计时交通灯
  18. 2.5万字详细讲解个人网站的开发过程和项目的部署
  19. 转播小助手开启微信语音多群同步直播转播之路
  20. java构建n阶魔方方阵

热门文章

  1. ibatis的there is no statement named xxx in this SqlMap
  2. js实现页面跳转的几种方式
  3. C#实现HttpPost提交文件
  4. 数据结构与算法:16 Leetcode同步练习(六)
  5. Matlab与线性代数 -- 逆矩阵
  6. Strategy_Requirement2
  7. 【PAT (Basic Level) 】1015 德才论 (25 分)
  8. 这个机器狗引起网友争议,「持枪机器狗」射程达1200米
  9. 达摩院实现自动驾驶核心技术突破,达摩院首次实现3D物体检测精度与速度的兼得
  10. 聊聊抖音、奈飞、Twitch、大疆、快手、B站的多媒体关键技术