转载自:Huggingface Accelerator踩坑指南 | 广哥拉斯的树洞
因为是个人网页,怕今后什么时候丢了,所以特意复制。
前言
最近因为做实验的数据集较大(百万条中文语句对),传统的单机单卡的速度实在不可恭维,所以笔者转向尝试单机多卡训练。最开始使用的是torch自带的torch.distributed包,引入了很多配置相关的代码,导致原来的代码结构混乱,再加上本身分布式并行计算就难以调试,笔者不得不花费大量时间一点点纠错才跑起来(一把辛酸泪)
然后机缘巧合下看到了hugginface开发的accelerate。在阅读完其简介和例子后,心中不经暗想:这也太方便了,事出反常必有妖!不过笔者还是决定付诸实践,看看这库是否如其宣传的那般好用。
目前的体验感想,仅一家之言
- 和torch一样,分布式环境难以调试,但比torch容易
- 网上的教程和huggingface自带的论坛里的提问很少有回答(活跃度太低,遇到bug可能得自己解决…)
- 非常多的配置选项,虽然“好像”能提升训练效率但是笔者(一枚菜鸟)看不懂啊
- 代码改动少很多,且模型训练和推理过程几乎和单卡一致
- accelerate支持单卡和多卡,几乎可以无缝切换
- 无需指定模型和数据分配的设备,accelerate会自动将两者分配到正确的设备!(可以极大提高显存使用效率)
- 解决了日志打印混乱的问题,accelerate自带的logger默认只在主进程打印日志,所以就不会再被眼花缭乱、上蹿下跳的不知道哪路进程打印的log迷惑了双眼
- more than what I mentioned aboved…
总结:值得一试
下面主要讲讲accelerate的简单使用和踩坑记录,代码和介绍主要参考Accelerate (huggingface.co)
入门实践
添加accelerator
首先导入和创建一个Accelerator实例,后面的所有几乎操作都离不开它
from accelerate import Acceleratoraccelerator = Accelerator()
设置device
如果你要用到device的话需要如下配置,accelerator会将pytorch object放入正确的device
device = accelerator.device |
|
# 笔者实践中,发现只需要将模型显示放入device,其他代码再见不到.to(xxx)和.cuda()了 |
|
model.to(device) |
调用prepare()处理对象
prepare()为传递的所有对象做好用于进行分布式训练和混合精度(能加速训练)的准备,返回相同顺序的对象
- 只接受继承自
torch.nn.Module的对象 - inference阶段无需使用
- 返回的对象可以正常使用,而
torch.distributed则需要model.module才能访问到真正的model
model, optimizer, training_dataloader, scheduler = accelerator.prepare( |
|
model, optimizer, training_dataloader, scheduler |
|
) |
替换backward()
使用accelerator.backward()函数进行反向传播,后续很多针对梯度和误差的操作都依赖此接口
loss.backward() -> accelerator.backward(loss)
初步完成代码
将上面描述的内容组合起来就可以跑了,但是如果想要实现分布式,我们必须将上述代码放入函数里边,即不能写在脚本里直接用
所以我们初步完成的代码应该如下所示
from accelerate import Accelerator |
|
+ def main(): |
|
accelerator = Accelerator() |
|
model, optimizer, training_dataloader, scheduler = accelerator.prepare( |
|
model, optimizer, training_dataloader, scheduler |
|
) |
|
for batch in training_dataloader: |
|
optimizer.zero_grad() |
|
# 注意到,这里无需将数据导入到指定设备,accelerator会自动帮你完成 |
|
# 传统来说应该先将inputs和targets传入gpu,cuda()或to(device) |
|
inputs, targets = batch |
|
outputs = model(inputs) |
|
loss = loss_function(outputs, targets) |
|
# 这里不再使用loss.backward()!!! |
|
accelerator.backward(loss) |
|
optimizer.step() |
|
scheduler.step() |
|
+ if __name__ == "__main__": |
|
+ main() |
如何运行
官方推荐运行前最好先调用accelerate config配置训练方式和相关参数,但笔者懒得搞(其实是不会),所以只讲最简单的运行方式
类似torch.distributed launch,我们这里用accelerate launch代替,紧跟着是运行的py脚本和你自己想传入的参数
# 默认是单机单卡训练,非混合精度(这样运行会有很多warning) |
|
accelerate launch {script_name.py} --arg1 --arg2 ... |
|
# 限制使用的GPU |
|
CUDA_VISIBLE_DEVICES="0" accelerate launch {script_name.py} --arg1 --arg2 ... |
|
# 单机多卡训练,非混合精度 |
|
accelerate launch --multi_gpu {script_name.py} {--arg1} {--arg2} ... |
|
# 单机多卡训练,混合精度 |
|
accelerate launch --multi_gpu --mixed_precision=fp16 {script_name.py} {--arg1} {--arg2} ... |
|
# 指定进程数 |
|
accelerate launch --multi_gpu --mixed_precision=fp16 --num_processes=2 {script_name.py} {--arg1} {--arg2} ... |
|
# 指定机器数量,笔者的配置,这样就没有warning了 |
|
CUDA_VISIBLE_DEVICES="0,1" accelerate launch --multi_gpu --mixed_precision=fp16 --num_processes=2 --num_machines=1 train.py |
具体还可以设置的参数使用accelerate launch -h获取
seed设置
如果想要重复实验,seed设置必不可少,accelerate提供的方法非常容易使用,一行代码解决
from accelerate import set_seed |
|
set_seed(42) |
实际上做的事

最低0.47元/天 解锁文章
1069

被折叠的 条评论
为什么被折叠?



