mxnet gluon GPU 训练速度问题?

squeeze net模型训练imagenet分类,以前用caffe训练,一块卡(GTX Titan X Maxwell)能到每秒约384图片:batch size是96,solver.prototxt里iter_size是4,迭代100次大概需要100秒,因此是每秒约384张图片。

现在用mxnet gluon接口4块卡并行训练,也才到每秒400多张图片。4块卡的GPU利用率看着挺高,都是100%左右,但实际每块卡功耗都是在100瓦上下浮动,很少能飙到200瓦以上(满功率是250瓦)。如果只用一块卡,速度就只有每秒100张图片左右了,在使用的这块卡功耗仍然在100瓦左右。而用caffe训练时,单卡的功耗通常能跑到200瓦左右,单卡就能跑每秒400张图片。

ps我已经从最新的源码重新编译了,但速度与pip安装的版本没有差异。最初以为是pip安装的mxnet没有用cudnn的问题,但从历史帖子了解到,mxnet静态连接的cudnn,所以ldd看不到。

目前用的是gluon(新上手mxnet,就直接上最新的接口了),还没有试过symbol接口会怎样。感觉gluon现在还有不少问题,比如如果一直在训练中间没有用asnumpy进行同步,就会吃越来越多GPU内存,直到GPU内存被吃完。如果GPU内存被吃完后后再来个asnumpy同步,就很容易cudaMalloc fail崩溃掉。

2赞

改回用symbol api,单卡训练速度超过了此前用caffe的速度,每秒550张图片左右,GPU功耗也持续在200W左右甚至更高。使用gluon api和使用symbol api的实验中,数据预处理完全相同,那么就剩下这样几个可能性:

  1. 我用gluon定义的模型有问题,所以速度慢。
  2. gluon有某些参数或环境变量我没有正确设置,所以速度慢。
  3. 现在版本的gluon就是不如symbol快。

后续我会继续研究这个问题,但如果已经有人测试比较过gluon与symbol的速度,能排除3的可能性,那就太好了!毕竟gluon接口在易用性上比symbol好很多,我也倾向于以后使用gluon接口。但如果速度问题不解决,就不好办了。

另外我发现gluon里的Dataset + DataLoader的方式做数据加载比用io.DataIter好用很多,速度比后者快很多。我的理解是前者使用了python的multiprocessing,直接开多个python进程并行搞;而后者是在libmxnet内部对计算图进行了一些并行化处理,会利用MXNET_CPU_WORKER_NTHREADS这个环境变量,但终究不如直接多个python进程好。

每一个iter里都要用total += loss.asscalar()同步一下

进一步进行了测试,表明gluon.nn.PReLU是导致速度变慢的主要问题。另一个问题是gluon在GPU内存占用较多时似乎在内部试图进行某种内存优化,造成频繁地释放和分配GPU内存,使速度严重下降。下面是测试的详细信息。

在这个测试中,我比较了四种模型训练的方式:

  1. 用Gluon API定义模型和训练,使用Hybridize。
  2. 用Gluon API定义模型和训练,不用Hybridize。
  3. 用Gluon API定义模型,然后转换成Symbol,用Symbol API进行训练。
  4. 用Symbol API定义模型和训练。

测试的其他信息如下:

  • 网络模型为SqueezeNet模型(我自己编的),测试了batch size=128和batch size=256两种情况。
  • 用固定的data和label进行200次迭代,由于每次迭代都使用同样的数据,因此排除了数据加载对速度的影响。
  • 测试中使用1块GTX Titan X(Maxwell)。

测试结果如下,表中记录了每种情况下迭代100次用的时间和GPU内存占用量。

模型 relu, batch=128 prelu, batch=128 relu, batch=256 prelu,batch=256
gluon hybrid 39.30sec/6.13GB 211.06sec/6.13GB 147.44sec/1GB-12GB波动 484.97sec/1GB-12GB波动
gluon no hybrid 40.56sec/9.58GB 213.46sec/9.58GB 191.78sec/6GB-12GB波动 521.35sec/7GB-12GB波动
gluon -> symbol 40.27sec/5.76GB 211.14sec/5.76GB 80.71sec/11.00GB 419.01sec/11.00GB
symbol 39.81sec/5.76GB 40.66sec/5.76GB 80.04sec/11.00GB 86.40sec/11.00GB

从这个表里可以看出:

  1. GPU内存充足时(batch=128),使用relu激活,四种方式速度没有明显差异,但gluon no hybrid占用内存明显更多,gluon hybrid占用内存仍高于symbol。
  2. GPU内存充足时(batch=128),gluon使用prelu激活会使速度明显减慢,即使把gluon模型转化为symbol后进行训练也仍然速度很慢,表明gluon里的prelu实现存在问题。
  3. GPU内存不足时(batch=256),使用gluon训练会显著降低速度,GPU内存占用量在一个大范围内浮动,可能是gluon内部存在的某种内存回收机制频繁在GPU上释放和分配内存导致的。

因此现在gluon存在如下两个问题:

  1. 使用PReLU激活时速度会显著变慢。由于model zoo里的模型大多使用relu而非prelu激活,因此这个问题可能mxnet的开发者没有注意到。
  2. GPU内存占用较多时速度会显著变慢。
2赞

又试了一下gluoncv里的train_imagenet.py,双卡Titan X(Maxwell),结果如下:

批量大小128:
python train_imagenet.py --data-dir ‘/home/bst/.met/datasets/imagenet’ --model resnet18_v1 --num-gpus 2 -j 8 --batch-size 128
|INFO:root:Epoch[0] Batch [49] Speed: 446.828648 samples/sec top1-err=0.997969 top5-err=0.990547
|INFO:root:Epoch[0] Batch [99] Speed: 686.303929 samples/sec top1-err=0.997578 top5-err=0.988867
|INFO:root:Epoch[0] Batch [149] Speed: 687.303935 samples/sec top1-err=0.997135 top5-err=0.986016
两个卡内存占用为9584MB/8648MB,内存占用大小稳定不动。

批量大小192:
python train_imagenet.py --data-dir ‘/home/bst/.mxnet/datasets/imagenet’ --model resnet18_v1 --num-gpus 2 -j 8 --batch-size 192
|INFO:root:Epoch[0] Batch [49] Speed: 193.592433 samples/sec top1-err=0.998229 top5-err=0.991667
|INFO:root:Epoch[0] Batch [99] Speed: 222.164344 samples/sec top1-err=0.997135 top5-err=0.989089
两个卡的GPU内存占用量在5GB到11GB之间大幅度波动。

这个测试结果也表明不是我自己写的网络里的问题,而是gluon在gpu内存占用过高时采取的某种策略使训练速度大幅度下降。

靠近内存上限的时候确实会导致频繁释放和分配内存。正在搞gluon的静态内存分配

2赞

又跑了一下gluoncv里的train_imagenet.py,resnet18_v1,batch-size=128,把gluon/model_zoo/vision/resnet.py里BasicBlockV1中的relu改成了prelu:

  • self.body.add(nn.Activation(‘relu’)改成self.body.add(nn.PReLU())
  • 在__init__最后添加一行self.prelu = nn.PReLU()
  • 在hybrid_forward中x = F.Activation(residual+x, act_type=‘relu’)改成x = self.prelu(residual + x)

测试结果如下:
原先使用relu的结果:

  • INFO:root:Epoch[0] Batch [49] Speed: 389.351966 samples/sec top1-err=0.997891 top5-err=0.992344
  • INFO:root:Epoch[0] Batch [99] Speed: 613.825026 samples/sec top1-err=0.997617 top5-err=0.989961
  • INFO:root:Epoch[0] Batch [149] Speed: 607.031833 samples/sec top1-err=0.996901 top5-err=0.986458

改成了prelu的结果:

  • INFO:root:Epoch[0] Batch [49] Speed: 202.304977 samples/sec top1-err=0.998281 top5-err=0.991563
  • INFO:root:Epoch[0] Batch [99] Speed: 229.141266 samples/sec top1-err=0.996914 top5-err=0.988047
  • INFO:root:Epoch[0] Batch [149] Speed: 214.704882 samples/sec top1-err=0.995964 top5-err=0.984453

这也证实了gluon中prelu的确会导致训练速度显著变慢。

1赞

我最近发现用gluon训练alexnet mnist,速度比keras还慢

这个是cpu还是gpu?

请问有人测试过不同的任务情况下mxnet和其他平台在cpu上infer 的速度对比吗?