入门pytorch似乎不慢,写好dataloader和model就可以跑起来了,然而把模型搭好用起来时,却往往发觉自己的程序运行效率并不高,GPU使用率宛如舞动的妖精...忽高忽低,影响模型迭代不说,占着显存还浪费人家的计算资源hh 我最近就是遇到这个困难,花了一些精力给模型提速,这里总结一下(有些描述可能并不准确,但至少这些point可以借鉴hh,不妥之处恳请大家指正/补充啦)
dataloader方面:
- DataLoader的参数中,
- num_workers与batch_size调到合适值,并非越大越快(注意后者也影响模型性能)
- eval/test时shuffle=False
- 内存够大的情况下,dataloader的pin_memory设为True,并用data_prefetcher技巧(方法)
- 把不怎么会更改的预处理提前做好 保存到硬盘上,不要放dataloader里做,尤其对图像视频的操作
- 减少IO操作,服务器如果是hdd根本架不住多人对磁盘的折磨,曾经几个小伙伴把服务器弄得卡到无法login...可先多线程把数据放到内存,不太会的话可以先用个dataloader专门把数据先读到list中(即内存),然后把这个list作为每次取数据的池。还可以用tmpfs把内存当硬盘,不过需要权限
- prefetch_generator(方法)让读数据的worker能在运算时预读数据,而默认是数据清空时才读
model方面:
- 用float16代替默认的float32运算(方法参考,搜索"fp16"可以看到需要修改之处,包括model、optimizer、backward、learning rate)
- 优化器以及对应参数的选择,如learning rate,不过它对性能的影响似乎更重要【占坑】
- 少用循环,多用向量化操作
- 经典操作尽量用别人优化好的库,别自己写(想自己实现锻炼能力除外)
- 数据很多时少用append,虽然使用很方便,不过它每次都会重新分配空间?所以数据很大的话,光一次append就要几秒(测过),可以先分配好整个容器大小,每次用索引去修改内容,这样一步只要0.0x秒
- 固定对模型影响不大的部分参数,还能节约显存。如果固定的是中间层,用required_grad=False 因为前面层的参数仍需要后面传来的梯度来更新,中间层的误差还要算;如果想固定网络最前面的参数,可直接用detach()切断反向传播(不用计算误差)
- eval/test的时候,加上model.eval()和torch.no_grad(),前者固定batch-normalization和dropout 但是会影响性能,后者关闭autograd
- 提高程序并行度,例如 我想train时对每个epoch都能test一下以追踪模型性能变化,但是test时间成本太高要一个小时,所以写了个socket,设一个127.0.0.1的端口,每次train完一个epoch就发个UDP过去,那个进程就可以自己test,同时原进程可以继续train下一个epoch(对 这是自己想的诡异方法hhh)
其他:
- torch.backends.cudnn.benchmark设为True,可以让cudnn根据当前训练各项config寻找优化算法,但这本身需要时间,所以input size在训练时会频繁变化的话,建议设为False
参考:
- 使用pytorch时,训练集数据太多达到上千万张,Dataloader加载很慢怎么办?
- MrTian:给训练踩踩油门 —— Pytorch 加速数据读取
- Pytorch IO提速 - 三年一梦 - 博客园
- https://discuss.pytorch.org/search?q=speed%20up
- https://github.com/huggingface/transformers/blob/dad3c7a485b7ffc6fd2766f349e6ee845ecc2eee/examples/run_classifier.py