前言

跑程序的时候,将GPU利用率拉到100%是每个深度学习er的目标,而有些时候这个目标却总是不那么容易实现,看着只有70%左右甚至还在上下反复横跳的GPU利用率,有些人直接放弃了,勉强可以用就行,有些人则一直在追求提升之道,我也一样。

普通生成效率vs模型运行时生成效率

我做了一个实验,不跑模型,单纯的生成数据,可以达到10 iter/s的效率,然而在跑模型的时候,却只有3 iter/s,且GPU利用率在50-70之间反复横跳,我一直错误的认为单纯生成数据能够达到10 iter/s,因此数据生成应该不是GPU利用率的瓶颈。但是仔细思考一下,可以发现这是错误的,当单纯生成数据的时候,10 iter/s意味着没生成一批数据,需要花费0.1s,因此在模型运行时,每跑一轮都会花0.1s的时候去等待数据生成,这种非连续性的生成当然导致了模型和GPU无法达到他的最大利用率。

解决方法

知道这个限制之后,我们自然可以想到解决方法,那就是用多进程去生成数据,避免模型等待数据的开销,事实上torch的DataLoader给了我们num_workers的参数,因此仅仅增加了num_workers=8这个参数,就将模型的运行效率提升了1.5倍,GPU利用率直接稳定在了90%,之所以一直回避这个参数,是因为我喜欢用windows系统,而windows种对于多进程的支持是有限的,因此在windows上num_workers参数必须设为0

大模型与变长输入

这里的变长输入指每一个batch内的数据填充到一样的长度,但是每个batch的最大长度不同。当我在训练roberta-base的时候,我发现模型可以稳定在90%左右的GPU利用,然而在换成roberta-large之后,利用率却莫名其妙只有60%左右的,在一顿苦思冥想之后,得出了3个可能的问题所在:

  • 输入长度 输入长度一直是可能左右模型GPU利用率的一个因素,有时候更大的模型需要更长的序列才能利用满
  • batch size base模型我可以使用的最大batchsize为30,而large模型的最大batchsize只能设置为6
  • 模型深度 base模型深度为12,而large模型深度为24

在尝试第一个的时候发现,将所有batch的最大长度固定到一样的时候,利用率稳定在了90%以上。其实仔细一想可以发现,large相比base拥有更多的参数,因此对于长度的敏感性更高,尤其是大部分序列长度都在最大长度一半的情况下,利用率自然而然的就下来了。因此要想提高大模型的利用率,batch序列长度最好保持一致