【发布时间】:2021-06-25 11:29:23
【问题描述】:
我想知道在使用 DDP 时记录指标的正确方法是什么。我注意到如果我想在validation_epoch_end 中打印一些东西,使用 2 个 GPU 时会打印两次。我期待 validation_epoch_end 仅在等级 0 上被调用并接收来自所有 GPU 的输出,但我不确定这是否正确。因此我有几个问题:
-
validation_epoch_end(self, outputs)- 使用 DDP 时,每个子进程是否接收从当前 GPU 处理的数据或从所有 GPU 处理的数据,即输入参数outputs是否包含来自所有 GPU 的整个验证集的输出? - 如果
outputs是 GPU/进程特定的,那么在使用 DDP 时,计算validation_epoch_end中整个验证集的任何指标的正确方法是什么?
我知道我可以通过检查 self.global_rank == 0 并仅在这种情况下打印/记录来解决打印问题,但是我正在尝试更深入地了解在这种情况下我正在打印/记录的内容。
这是我用例中的代码 sn-p。我希望能够报告整个验证数据集的 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}'))
【问题讨论】:
标签: python-3.x pytorch pytorch-lightning