在过去的10个月里,在从事PyTorchLightning工作时,我和团队接触了多种结构PyTorch代码的风格,我们发现了一些人们无意中引入瓶颈的关键地方。我们非常小心地确保PyTorchLightning不会在我们自动为您编写的代码中犯任何这些错误,我们甚至会在检测到这些错误时为用户更正这些错误。但是,由于Lightning只是结构化的PyTorch,而你仍然控制着PyTorch的所有部分,因此在很多情况下我们不能为用户做太多事情。此外,如果没有Lightning,您可能会不经意地将这些问题引入您的代码中。为了帮助您更快地训练,这里有8个您应该注意的技巧,它们可能会降低您的代码速度。在DataLoaders中使用worker的第一个错误很容易纠正。PyTorch允许同时在多个进程上加载数据。在这种情况下,PyTorch可以通过处理8个批次来绕过GIL锁,每个批次在一个单独的进程上。你应该使用多少工人?一个好的经验法则是:num_worker=4*num_GPUhttps://discuss.pytorch.org/t/guidelines-for-assigning-num-workers-to-dataloader/813/7这里有一个很好的讨论。警告:缺点是您的内存使用量也会增加Pin内存您知道有时您的GPU内存显示它已满但您确定您的模型没有使用那么多吗?这种开销称为固定内存。此内存保留为“工作分配”类型。当您在DataLoader中启用pinned_memory时,它“自动将获取的数据张量放入固定内存中,并能够更快地将数据传输到支持CUDA的gpus”,这意味着您不应不必要地调用:torch.cuda.empty_cache()避免CPU到GPU的传输反之亦然#bad.cpu().item().numpy()我看到大量使用.item()或.cpu()或.numpy()transfer。这对性能非常不利,因为每次调用都会将数据从GPU传输到CPU,从而大大降低性能。如果您尝试清除附加的计算图,请使用.detach()。#good.detach()这不会将内存传输到GPU,它会删除附加到此变量的任何计算图。直接在GPU上构建张量大多数人在GPU上创建张量,如t=tensor.rand(2,2).cuda()但是,这首先创建CPU张量,然后将其传输到GPU......这真的是慢的。相反,直接在所需设备上创建张量。t=tensor.rand(2,2,device=torch.device('cuda:0'))如果您使用的是Lightning,我们会自动将您的模型和批处理放在正确的GPU上。但是,如果您在代码中的某处创建了一个新的张量(例如:为VAE采样随机噪声,或类似的东西),那么您必须自己放置张量。t=tensor.rand(2,2,device=self.device)每个LightningModule都有一个方便的self.device调用,无论你是在一个CPU,多个GPU,还是TPU上,Lightning都会创建一个选择正确的设备。使用DistributedDataParallel而不是DataParallelPyTorch有两种主要模式用于在多个GPU上进行训练。第一个是DataParallel,它将一批数据拆分到多个GPU。但这也意味着必须在每个GPU上复制模型,并且一旦在GPU0上计算了梯度,就必须将它们同步到其他GPU。这需要大量昂贵的GPU传输!相反,DistributedDataParallel在每个GPU上创建模型的副本(在其自己的进程中),并且仅使一部分数据可用于该GPU。这就像有N个单独的模型进行训练,除了一旦为每个模型计算了梯度,它们就会在模型之间同步梯度……这意味着我们只在每批数据的GPU之间传输一次。在Lightning中,您可以轻松地在Trainer(distributed_backend='ddp',gpus=8)和Trainer(distributed_backend='dp',gpus=8)之间切换。请注意,PyTorch和Lightning都不鼓励使用DP。使用16位精度是另一种加速训练的方法,我们没有看到很多人使用它。在您的模型在16位上训练的部分,数据从32位更改为16位。这有几个优点:您使用一半的内存(意味着您可以将批量大小加倍并将训练时间缩短一半)。某些GPU(V100、2080Ti)可以自动加速(3-8倍),因为它们针对16位计算进行了优化。在Lightning中,这很简单:Trainer(precision=16)注意:在PyTorch1.6之前,您还必须安装NvidiaApex,现在16位是PyTorch的原生版本。但如果您使用的是Lightning,它会同时支持这两种版本,并会根据检测到的PyTorch版本自动切换。剖析你的代码如果没有Lightning,最后一条建议可能很难实施,但你可以使用像cprofiler这样的工具来实现。然而,在Lightning中,您可以通过两种方式获得训练期间所有调用的摘要:首先,内置的基本profilerTrainer(profile=True)可以提供如下输出:或高级profiler:profiler=AdvancedProfiler()trainer=Trainer(profilerprofiler=profiler)以获得更小粒度的结果:
