RuntimeError: CUDA out of memory. Tried to allocate 50.00 MiB (GPU 0; 4.00 GiB total capacity; 682.9

1. 问题

训练模型时报错:
RuntimeError: CUDA error: out of memory CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below might be incorrect. For debugging consider passing CUDA_LAUNCH_BLOCKING=1.
或任务管理器手动释放进程后:
再次训练模型时报错:
RuntimeError: CUDA out of memory. Tried to allocate 50.00 MiB (GPU 0; 4.00 GiB total capacity; 682.90 MiB already allocated; 1.62 GiB free; 768.00 MiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation. See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF

2. 分析

这种问题,是GPU内存不够引起的

3. 解决

方法一:通过修改代码减少GPU内存使用量

(1)不计算梯度

部分代码:

for x, y in dataload:
    step += 1
    inputs = x.to(device)
    labels = y.to(device)
    # zero the parameter gradients
    optimizer.zero_grad()
    # forward
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss.backward()#梯度下降,计算出梯度
    optimizer.step()

在 outputs = model(inputs)那里加上一行代码:with torch.no_grad():

    optimizer.zero_grad()
    # forward
    with torch.no_grad():#添加这行代码
        outputs = model(inputs)#再缩进这行
    loss = criterion(outputs, labels)

说明:对于tensor的计算操作,默认是要计算梯度和进行反向传播的,而torch.no_grad()用来禁止梯度的计算和反向传播,可以减少GPU的内存使用量。在验证和测试阶段不需要计算梯度反向传播,可以添加with torch.no_grad():。在作用域范围内的操作不会计算梯度,也不会进行反向传播。

如果添加上述代码后还不行,那是因为我们前面虽然禁止了计算梯度,但是却没有计算梯度的Variable,因为这里的inputs本身默认就是不求梯度的。所以还会报错:
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

这时需要构建Variable,并传入参数:requires_grad=True,这个参数表示对这个变量求梯度,默认是False。

inputs = x.to(device)
labels = y.to(device)
#添加下面两行代码
inputs = Variable(inputs,requires_grad=True)
labels = Variable(labels,requires_grad=True)

记得还需要在代码最前面添加 from torch.autograd import Variable才能导入Variable

(2)调整Batch Size

还有一种方法就是调整Batch Size(即一次训练所抓取的数据样本数量)的大小,Batch size大小的选择也至关重要。为了在内存效率和内存容量之间寻求最佳平衡,batch size应该精心设置,从而最优化网络模型的性能及速度。
在卷积神经网络中,大的batch size通常可使网络更快收敛,但由于内存资源的限制,batch size过大可能会导致内存不够用,如果GPU内存不够用出现以上情况,可以将Batch Size适当的设置小一点。

BatchSize是非常重要的超参数,它不仅影响训练速度,也影响模型精度。本篇来讨论如何选择BatchSize。

BatchSize是否越大越好?

BatchSize一般指同时代入训练模型的实例个数,最直接的经验是如果GPU显存不大,又设置较大的BatchSize时直接报错“cuda runtime error(2): out of memory”。

是不是只要机器性能允许,就要设置尽可能大的Batch size呢?同时训练多个实例比逐个代入单个实例训练速度又能提高多少?
使用较大的batchSize的确可以提速,但是batchSize大到一定程度后,效果并不十分明显。

如何选择batch大小?

两种极端的情况是BatchSize大小为1,每次只训练一个实例,或者BatchSize大小等于所有训练集数据大小,即每次训练所有数据。但更多的时候BatchSize设置在二者之间。

batchSize较小时,抖动大,训练过程有很大运气的成份,可能某个实例将模型带偏了,防止被模型被带偏的方法是使用较小的学习率,这样即非并行处理,又使用较小的学习率,使得收敛变慢。

batchSize较大时,任务并行执行,训练速度快, 且大Batch正负样本更均衡可以有效更新BN层参数精度更高。代价函数也能相对比较稳定,平滑地下降。但是如果代入了所有数据后再调参,可能会使很多梯度相互抵消,调参也比较粗糙。如果不进行特殊处理,过大的BatchSize一将会损失模型精度。另外,较大的batchSize会使模型的泛化能力下降(generalization gap)。

如果遇到较大的数据集,一般需要切分成batch分批训练,对于较少的数据,也可以使用全部数据训练。当有足够算力时,选取BatchSize为32,64,128或更小一些的batch_size。算力不够时,在效率和泛化性之间做权衡,选择更小的batchSize。

在训练过程中,可以将batch_size作为超参数做多次尝试。另一方面,也可以在前期使用较大的学习率和较越BatchSize粗调,后期(比如论文实验/比赛最后)将BatchSize变小精调,并使用SGD优化方法,慢慢把Error磨低。

BatchSize是否只与速度有关?

BatchSize不仅与速度相关,如果模型中使用了Batch Normalization(BN)归一化方法,那么太小的BatchSize会影响模型效果,如果受算法或性能限制只能使用小的BatchSize,则建议在fine-tune时锁住BN归一化层,或者使用其它归一化方法(如Group Normalization)。

Batch Normalization works well for large enough batch sizes but not well for small batch sizes, because it normalizes over the batch. Training large models with large batch sizes is not possible due to the memory capacity of the devices.

另外,BN归一化时的统计值针对每一Batch计算,而非对整个训练集计算,从而引入了噪声,当噪声在合理范围以内时,它能实现类似Dropout的效果,使模型更加健壮;BatchSize太小噪声太大,则使模型效果变差;如果BatchSize太大,统计值能有效地表示整体训练数据的统计数据,则无法产生类似Dropout的正则化效果。

BatchSize与Learning rate的关系

Krizhevsky提出,如果BatchSize加大,可以加大学习率,当BatchSize加大k倍,则学习率也加大k的开根号位数,后来发现使用线性变换调节二者关系效果更好。用这种方法将AlexNet的BatchSize从128加大到1024精度只损失1%。但是太大的学习率又会在一开始使模型训练不稳定,解决该问题的方法是学习率warm-up。

方法二:更换更高性能更高显存的显卡

这样所有问题都解决了。

你可能感兴趣的:(深度学习,深度学习,人工智能)