训练深度学习模型时如何计算小批量内存影响?

数据挖掘 深度学习 张量流
2021-10-02 00:17:22

我正在尝试根据Andrej Karphaty的注释计算 GPU 训练我的模型所需的内存量。

我的网络有532,752 个激活19,072,984 个参数(权重和偏差)。这些都是 32 位浮点值,因此每个在内存中占用 4 个字节。

我的输入图像是 180x50x1(宽 x 高 x 深)= 9,000 float 32 值。我不使用图像增强,所以我认为杂项内存只与小批量大小有关。我正在使用 128 个图像的小批量大小。

根据 Andrej 的建议,我得到以下内存大小:

激活: 532,752*4/(1024^2) = 2.03 MB

参数: 19,072,984*4/(1024^2) * 3 = 218.27 MB

其他: 128 9,000 4/(1024^2) = 4.39 MB

所以训练这个网络的总内存是224,69 MB

我正在使用 TensorFlow,但我认为我遗漏了一些东西。我还没有进行培训,但我很确定(根据过去的经验)正在使用的内存将比我计算的要高得多。

如果对于 mini-batch 中的每张图像,TensorFlow 会保持它们的梯度,以便以后可以对它们进行归一化以进行单个权重/偏差更新步骤,那么我认为内存应该考虑另外 532,752 * 128 个值(每个图像的梯度小批量)。如果是这种情况,那么我需要更多的 260.13 MB 来用 128 个图像/小批量训练这个模型。

你能帮我理解训练我的深度学习模型的内存注意事项吗?以上考虑对吗?

3个回答

@StatsSorceress TL;博士:

我正在经历这个活动,看看我是否可以自己计算所需的内存:

激活: 532,752 * 2 * 4 / (1024^2) = 4.06 MB

参数: 19,072,984 * 4 / (1024^2) * 3 = 218.27 MB

其他: 128 * 9,000 * 4 / (1024^2) = 4.39 MB

总内存:(4.06 * 128)+ 218.27 + 4.39 = 742.34 MB

如果我错了,请有人纠正我。仅供参考,您已经将杂项乘以 128,这就是为什么我没有将其乘以上面的 128 的原因


我会向您指出这篇文章相应的视频他们帮助我更好地了解正在发生的事情。

注意: 使用网络进行预测所需的内存远低于训练所需的内存,原因有两个:

  • 预测时,我们只通过网络向前发送图像而不是向后发送(所以我们不将内存乘以 3;见下文)
  • 每张图像有一个预测(因此我们不需要将一张图像所需的内存乘以批量大小,因为我们在预测中不使用批量)。

过程(记忆训练)

  1. 计算在一张图像上训练所需的内存
  2. 将此数字乘以批次中的图像数量

记住: 小批量表示我们获取数据的一个子集,计算子集中每个图像的梯度和误差,然后对它们进行平均并朝着平均的方向前进。对于卷积网络,权重和偏差是共享的,但是激活的数量乘以批次中的图像数量。)。

第 1 步:存储 1 张图像

要训​​练一张图像,您必须为以下内容预留内存:

  • 型号参数:

    每层权重偏差,它们的梯度,以及它们的动量变量(如果使用 Adam、Adagrad、RMSProp 等,优化器)

    为了近似内存,计算存储权重和偏差所需的内存并将其乘以 3(即“乘以 3”,因为我们说存储权重和偏差所需的内存量(大致)等于梯度和动量变量所需的)

    方程式:

    卷积:

    权重(n) = 深度(n) * (kernel_width * kernel_height) * depth(n-1)

    偏差(n)=深度(n)

    全连接(密集)层:

    权重(n)=输出(n)*输入(n)

    偏差(n)=输出(n)

其中n是当前层,n-1是前一层,输出是FC层的输出数量,输入是FC层的输入数量(如果前一层不是全连接层,输入的数量等于展平层的大小)。

注意:仅权重和偏差的内存,加上一张图像的激活内存(见下文),就是预测所需的内存总量(不包括卷积和其他一些东西的内存开销)。

  • 激活(这些是 Caffe 中的“Blob”):

(我在这里松散地使用术语,请耐心等待)

卷积层中的每个卷积都会产生“图像中的像素数”激活(即,您通过单个卷积传递图像,您将获得由“ m ”激活组成的单个特征图,其中“ m ”是来自您的图像/输入)。

对于全连接层,您产生的激活数等于输出的大小。

卷积:

激活(n)= image_width * image_height * image_num_channels

全连接(密集)层:

激活(n)=输出(n)

请注意,您的输入实际上只是网络开始时的图像。在卷积之后,它变成了别的东西(特征图)。所以真正将“image_width”、“image_height”和“image_num_channels”替换为“input_width”、“input_height”和“layer_depth”更准确。(我更容易从图像的角度来思考这个概念。)

由于我们还需要存储每一层的激活误差(用于后向传递),我们将激活数乘以 2 以获得我们需要在存储空间中腾出空间的实体总数。激活的数量随着批次中图像的数量而增加,因此您将此数字乘以批次大小。

第 2 步:训练批次的内存

将权重和偏差的数量(乘以 3)和激活的数量(乘以批量大小的 2)相加。将其乘以 4,即可得到训练批次所需的字节数。您可以除以 1024^2 得到 GB 的答案。

我认为你在正确的轨道上。

是的,您需要存储激活的导数和反向传播的参数。

此外,您选择的优化可能很重要。您是使用 SGD、Adam 还是 Adagrad 进行训练?这些都有不同的内存要求。例如,您将不得不为基于动量的方法存储步长缓存,尽管与您提到的其他内存考虑因素相比,这应该是次要的。

所以总而言之,您似乎已经计算了前向传递的内存需求。Andrej Karpathy提到后向传播可能会占用前向传播的 3 倍内存,所以这可能就是您看到这种差异的原因(向下滚动到网站上的“案例研究”以查看 VGGNet 的示例)。

或者,我认为您可以使用任何分析器库来分析程序的内存和 CPU 使用情况。有许多 python 库可以为您提供特定线程或进程以毫秒为间隔的内存和 CPU 使用情况的快照。

您可以使用popen在不同的子进程中运行您想要监控的程序部分,并使用它的 PID 监控它的内存和 CPU 使用率。

psutil我觉得很适合这样的工作。虽然还有很多。

我希望这将有所帮助。