当前位置: 首页 > 后端技术 > Python

计算Python代码内存消耗和模型内存消耗的小技巧

时间:2023-03-25 22:34:30 Python

了解Python代码的内存消耗是每个开发者必须要解决的问题。这个问题不仅在我们使用pandas读取和处理CSV文件时非常重要,在我们使用GPU训练时,我们也需要规划GPU内存使用。尤其是当我们免费使用kaggle和colab的时候。在本文中,我们将介绍两个Python库memory_profiler和Pytorch-Memory-Utils,可以帮助我们了解内存和显存的消耗情况。memory_profilerpipinstallmemory_profiler#加载它的魔法函数%load_extmemory_profilerfrommemory_profilerimportprofilememory_profiler可以完成以下任务:1.求一行的内存消耗,我们只需要加上魔法函数%memit%memitx=10+5#Outputpeakmemory:54.01MiB,increment:0.2在这里,峰值内存是运行此代码的进程消耗的内存。增量只是由于添加这行代码而需要/消耗的内存。同样的逻辑适用于下面的其他显示。2.查找函数的内存消耗在调用函数的行的开头添加magicfunction。defaddition():a=[1]*(10**1)b=[2]*(3*10**2)sum=a+breturnsum%memitaddition()#Outputpeakmemory:36.36MiB,increment:0.01MiB3,逐行查找函数的内存消耗如果需要记录函数中每一行的内存使用情况,我们可以使用@profile装饰器。但是@profile仅适用于在单独模块中定义的函数,因此我们将首先创建一个名为demo.py的简单模块,其中%%file包含我们的函数%%filedemo.pyfrommemory_profileimportprofile@profiledefaddition():a=[1]*(10**1)b=[2]*(3*10**2)sum=a+breturnssum现在,我们可以调用函数fromdemoimportaddition%memitaddition()#OutputLine#MemusageIncrementLineContents=====================================================236.4MiB36.4MiB@profile3defaddition():436.4MiB0.0MiBa=[1]*(10**1)53851.1MiB3814.7MiBb=[2]*(3*10**2)67665.9MiB3814.8MiBsum=a+b77665.9MiB0.0MiBreturnsumpeakmemory:7665.88MiB,increment:7629.52MiB4,完整python脚本的内存消耗这个方法不能在notebook中使用。我们必须创建python脚本并通过命令行运行它。#createscript.pyimporttime@profiledeffunction1():n=100000a=[1]*ntime.sleep(1)return@profiledefffunction2():n=200000b=[1]*ntime.sleep(1)returnbif__name__=="__main__":function1()function2()之后,运行脚本,查看#在命令行mprofrunscript.py#生成plotmprofplot我们可以看到内存消耗和时间的关系@profile装饰器不需要放在在函数前面,如果我们不保留它,我们看不到函数级别的内存消耗,但是我们看到整个脚本的内存消耗Pytorch-Memory-Utils使用Pytorch-Memory-Utils工具,我们插入在使用显存的代码中间检测函数,这样可以输出当前代码行占用的显存。这对于我们计算模型的GPU内存使用来说是非常方便的。通过计算内存使用量,我们可以最大化训练的batchsize,保证最优的训练速度。importtorchimportinspectfromtorchvisionimportmodelsfromgpu_mem_trackimportMemTracker#参考内存跟踪代码device=torch.device('cuda:0')frame=inspect.currentframe()gpu_tracker=MemTracker(frame)#创建内存检测对象gpu_tracker.track()#开始检测cnn=9models.vgg1pretrained=True).to(device)#导入VGG19模型并将数据传输到显存gpu_tracker.track()然后可以看到程序运行过程中显存的变化(第一行是加载前的显存,最后一行是输入后的加载内存):At__main__:line13TotalUsedMemory:472.2Mb+|1*尺寸:(128,64,3,3)|内存:0.2949M|+|1*尺寸:(256,128,3,3)|内存:1.1796M|+|1*尺寸:(64,64,3,3)|内存:0.1474M|+|2*尺寸:(4096,)|内存:0.0327M|+|1*尺寸:(512,256,3,3)|内存:4.7185M|+|2*尺寸:(128,)|内存:0.0010M|+|1*尺寸:(1000,4096)|内存:16.384M|+|6*尺寸:(512,)|内存:0.0122M|+|1*尺寸:(64,3,3,3)|内存:0.0069M|+|1*尺寸:(4096,25088)|内存:411.04M|+|1*尺寸:(4096,4096)|内存:67.108M|+|5*尺寸:(512,512,3,3)|内存:47.185M|+|2*尺寸:(64,)|内存:0.0005M|+|3*尺寸:(256,)|内存:0.0030M|+|1*尺寸:(128,128,3,3)|内存:0.5898M|<类'torch.nn.parameter.Parameter'>+|2*尺寸:(256,256,3,3)|内存:4.7185M|+|1*尺寸:(1000,)|内存:0.004M|At__main__:line15TotalUsedMemory:1387.5Mb通过上面的报告,很容易发现问题所在。首先,我们知道VGG19所有层的权重加上大约是548M(这个值来自Pytorch官方提供的VGG19权重文件大小),我们把上面报告打印的Tensor-Memory加起来差不多是551.8兆然而,我们计算了两次打印的实际内存使用量:1387.5–472.2=915.3MB。Pytorch在开始运行程序时需要额外的显存开销。这种额外的显存开销与我们实际使用的模型权重显存大小无关。这个额外显存的开发者Pytorch也对此进行了解释。这部分释放出来的显存是可以使用的,但是在Nvidia-smi中是没有显示的,所以不用关注。