一句话:多GPU训练模型,一句代码不能解决模型训练、eval、保存、加载的。单GPU跟多GPU的在这几个步骤中使用方式不太一样。(前提:使用huggingface.co去训练语言模型)


在pytorch上使用多个GPU(在同一台设备上,并非分布式)进行训练是件非常容易的事情,只要在源代码中添加(修改)两行代码即可。下面贴上官方教程给的示例代码。并在文末总结一些自己在使用多GPU实验时发现的问题。

官方tutorial——单GPU

把模型放在GPU上:

device = torch.device("cuda:0")
model.to(device)

将tensor复制到GPU上

mytensor = my_tensor.to(device)

请注意,调用my_tensor.to(device)会在GPU上返回一个新的my_tensor副本,而不是重写my_tensor。你需要给它分配一个新的张量,然后在GPU上使用这个张量。

Pytorch默认情况下只使用一个GPU。

官方tutorial——多GPU

想用多GPU同时训练,只需下面一步简单的操作:使用模块DataParallel,你就可以很容易地在多个gpu上运行你的操作:

if torch.cuda.device_count() > 1:
     print("Let's use", torch.cuda.device_count(), "GPUs!")
     model = torch.nn.DataParallel(model)

多GPU环境下LOSS的计算

先来看下loss的反向传播代码:

        for step, batch in enumerate(train_dataloader):
            batch[0] = torch.LongTensor(batch[0]).to(config.device)
            batch[1] = torch.LongTensor(batch[1]).to(config.device)
            loss, logits= model(batch[0],labels=batch[1])   # Forward pass
            loss_avg.backward()  # Backward pass

报错如下,意思就是梯度只能为标量(即一个数)输出隐式地创建。

RuntimeError: grad can be implicitly created only for scalar outputs

这里面4gpu打印输出的是一个list,即每一个gpu上都会有一个loss返回:

---loss_avg : tensor([0.5866, 0.6941, 0.6176, 0.6226], device='cuda:0',
       grad_fn=<DivBackward0>)
---loss_avg : tensor([0.5866, 0.6941, 0.6176, 0.6226], device='cuda:0',
       grad_fn=<DivBackward0>)

平时我们单卡的loss形式:

---loss_avg : tensor(0.4542, device='cuda:0', grad_fn=<DivBackward0>)
---loss_avg : tensor(0.3607, device='cuda:0', grad_fn=<DivBackward0>)

解决方法:

将多卡得到的loss进行mean,求平均:

loss_avg.backward()  -----> loss_avg.mean().backward()

多GPU环境下对模型的保存:

出现的问题:

最开始保存模型时,直接torch.save(),这样保存后的模型,去做预测,同一个样本,预测得分不一致,很是奇怪。

解决:

用model.module模块去保存模型。

要先判断下是否为数据并行的形式,然后再去将模型的state_dict保存到给定路径中。特别注意:多GPU情况下,需要调用model.module模块,再去保存模型参数state_dict。我理解model.module就是将多卡上的参数以及网络机构通过某种机制将其汇总成:只有一个网络结构,只有一套参数的模型结构。

代码如下:

#万能的保存方法,如果你的预测函数不会依赖你的模型类定义。
if isinstance(model, torch.nn.DataParallel):
   torch.save(model.module.state_dict(), config.save_path)
#依赖你的模型定义,这么保存只保存了你模型的参数,模型的结构没有保存,所以尽量用上面的保存方法。
if isinstance(model, torch.nn.DataParallel):
   torch.save(model.state_dict(), config.save_path)

多GPU环境下对模型的加载去预测:

在 pytorch 多GPU训练下,存储 整个模型 ( 而不是model.state_dict() )后再调用模型可能会遇到下面的情况:

AttributeError: ‘DataParallel’ object has no attribute ‘xxxx’

解决的方法是:

model = torch.load('path/to/model')
if isinstance(model,torch.nn.DataParallel):
		model = model.module

#下面就可以正常使用了
model.eval()

后传

如果你训练的是使用huggingface.co库中的方法去训练语言模型,你想在cpu上使用gpu上训练好的模型,进行预测,那么,加载模型时,需要先将模型由gpu的序列化形式转为cpu序列化形式,然后再使用huggingface.co库中的模型加载方式得到模型,例子:

# 将gpu序列化转换为cpu序列化
model_state_dict = torch.load(model_path, map_location=torch.device('cpu'))
#将模型的state_dict作为预训练模型加载的参数,相当于拿到了模型 
model = BertForSequenceClassification.from_pretrained(config.bert_name, state_dict=model_state_dict)
# 可以进行预测了
#pre = model(pre_data)

 

参考:

1.多gpu训练https://programtip.com/zh/art-45711

2.保存与加载https://www.cnblogs.com/blog4ljy/p/11711173.html

3.模型加载去预测:https://blog.csdn.net/Chris_zhangrx/article/details/86619834

4.反向传播只能是标量:https://www.cnblogs.com/wanghui-garcia/p/10616344.html

5.多GPU训练:https://oldpan.me/archives/pytorch-to-use-multiple-gpus

 

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐