Python 3.x 使用Pytork Lightning DDP时记录事情的正确方法
我想知道当使用DDP时,记录度量的正确方法是什么。我注意到如果我想在Python 3.x 使用Pytork Lightning DDP时记录事情的正确方法,python-3.x,pytorch,pytorch-lightning,Python 3.x,Pytorch,Pytorch Lightning,我想知道当使用DDP时,记录度量的正确方法是什么。我注意到如果我想在validation\u epoch\u end内打印某些内容,则使用2个GPU时会打印两次。我希望validation\u epoch\u end只在秩0上调用,并从所有GPU接收输出,但我不确定这是否正确。因此,我有几个问题: validation\u epoch\u end(self,outputs)-使用DDP时,每个子流程是否接收到从当前GPU处理的数据或从所有GPU处理的数据,即输入参数outputs是否包含来自所有
validation\u epoch\u end
内打印某些内容,则使用2个GPU时会打印两次。我希望validation\u epoch\u end
只在秩0上调用,并从所有GPU接收输出,但我不确定这是否正确。因此,我有几个问题:
validation\u epoch\u end(self,outputs)
-使用DDP时,每个子流程是否接收到从当前GPU处理的数据或从所有GPU处理的数据,即输入参数outputs
是否包含来自所有GPU的整个验证集的输出outputs
是GPU/过程特定的,那么在使用DDP时,计算validation\u epoch\u end
中整个验证集的任何度量的正确方法是什么self.global_rank==0
并仅在这种情况下打印/记录来解决打印问题,但是我正在尝试更深入地了解在这种情况下打印/记录的内容
下面是我的用例中的代码片段。我希望能够报告整个验证数据集的f1、精度和召回率,我想知道使用DDP时的正确方法是什么
def _process_epoch_outputs(self,
outputs: List[Dict[str, Any]]
) -> Tuple[torch.Tensor, torch.Tensor]:
"""Creates and returns tensors containing all labels and predictions
Goes over the outputs accumulated from every batch, detaches the
necessary tensors and stacks them together.
Args:
outputs (List[Dict])
"""
all_labels = []
all_predictions = []
for output in outputs:
for labels in output['labels'].detach():
all_labels.append(labels)
for predictions in output['predictions'].detach():
all_predictions.append(predictions)
all_labels = torch.stack(all_labels).long().cpu()
all_predictions = torch.stack(all_predictions).cpu()
return all_predictions, all_labels
def validation_epoch_end(self, outputs: List[Dict[str, Any]]) -> None:
"""Logs f1, precision and recall on the validation set."""
if self.global_rank == 0:
print(f'Validation Epoch: {self.current_epoch}')
predictions, labels = self._process_epoch_outputs(outputs)
for i, name in enumerate(self.label_columns):
f1, prec, recall, t = metrics.get_f1_prec_recall(predictions[:, i],
labels[:, i],
threshold=None)
self.logger.experiment.add_scalar(f'{name}_f1/Val',
f1,
self.current_epoch)
self.logger.experiment.add_scalar(f'{name}_Precision/Val',
prec,
self.current_epoch)
self.logger.experiment.add_scalar(f'{name}_Recall/Val',
recall,
self.current_epoch)
if self.global_rank == 0:
print((f'F1: {f1}, Precision: {prec}, '
f'Recall: {recall}, Threshold {t}'))
问题
验证\u历元\u结束(自身,输出)-使用DDP时
子流程接收从当前GPU或数据处理器处理的数据
从所有GPU处理,即输入参数是否输出
包含来自所有GPU的整个验证集的输出
仅从当前GPU处理数据,输出不同步,只有向后
同步(梯度在训练期间同步,并分发到驻留在每个GPU上的模型副本)
想象一下,所有的输出都是从1000
GPU传递给这个可怜的主机,它可以很容易地给它一个OOM
如果输出是GPU/进程特定的,那么正确的计算方法是什么
validation_epoch_中整个验证集的任何度量在
使用顺铂
根据(我的重点):
使用从每个批次分割数据的加速器进行验证时
在整个GPU中,有时您可能需要在主机上聚合它们
用于处理的GPU(dp或ddp2)
下面是附带的代码(validation\u epoch\u end
将在本例中从单步跨多个GPU接收累积数据,也请参见注释):
提示
关注每台设备的计算和尽可能少的GPU间传输
- 在
(或validation\u步骤
,如果这是您想要的,这是一般性的)内,按批次计算training\u步骤
、f1
、精度
以及任何其他召回
- 返回这些值(例如,作为dict)。现在,您将从每个设备返回
编号,而不是3
(可能会大得多)(批次、输出)
- 在
中,获取那些validation\u step\u end
值(如果您有2个GPU,则实际上是3
),对它们求和/取平均值,并返回(2,3)
值3
- 现在
将获得验证\u epoch\u end
值,您可以使用这些值进行累加(步骤3)
validation\u epoch\u end
期间不在值列表上操作,而是将它们累积到另一个3
值中(假设您有很多验证步骤,列表可能会变得太大),那就更好了,但这应该足够了
AFAIK Pytork Lightning不会这样做(例如,与其添加到
列表中,不如直接应用一些累加器),但我可能弄错了,所以任何更正都会很好。谢谢您的帮助!不幸的是,您的建议显然只对文档中提到的DP和DDP2有效。我对此进行了测试并确认,对于DDP,即使我使用了validation\u step\u end
输出始终是特定于GPU的…@JovanAndonov,如果是这样的话,您可能会使用PyTorch“低级”原语(可能会很头疼)。
# Done per-process (GPU)
def validation_step(self, batch, batch_idx):
x, y = batch
y_hat = self.model(x)
loss = F.cross_entropy(y_hat, y)
pred = ...
return {'loss': loss, 'pred': pred}
# Gathered data from all processes (per single step)
# Allows for accumulation so the whole data at the end of epoch
# takes less memory
def validation_step_end(self, batch_parts):
gpu_0_prediction = batch_parts.pred[0]['pred']
gpu_1_prediction = batch_parts.pred[1]['pred']
# do something with both outputs
return (batch_parts[0]['loss'] + batch_parts[1]['loss']) / 2
def validation_epoch_end(self, validation_step_outputs):
for out in validation_step_outputs:
# do something with preds