原文:7 tips for squeezing maximum performance from pytorch

原文:7个提升PyTorch性能的技巧

出处:AI公园 - 微信公众号

作者:William Falcon

编译:ronghuaiyang

为了帮助训练得更快,这里有几个技巧,应该知道它们可能会减慢训练代码.

1. 在DataLoader中使用workers

data_loader = DataLoader(
    dataset,
    collate_fn=collate_fn,
    batch_sampler=sampler,
    num_workers=NUM_WORKERS, #多进程
    pin_memory=True,
)

PyTorch允许同时在多个进程上加载数据.

PyTorch 可以通过处理 NUM_WORKERS 个批次绕过GIL锁,每个批次在一个单独的进程上。

应该使用多少workers?一个好的经验法则是:

num_worker = 4 * num_GPU

可以参考:Guidelines for assigning num_workers to DataLoader

缺点是,内存使用也会增加.

2. 设置 Pin memory

有时候GPU显存显示是满的,但模型并没有使用那么多,这种开销称为 pinned memory. 这个内存被保留为一种 “working allocation” 类型.

当在一个DataLoader中启用pinned_memory时,它自动将获取的数据张量放在 pinned memory 中,并使数据更快地传输到CUDA-enabled的gpu.

图:How to Optimize Data Transfers in CUDA C/C++

这意味着不需要不必要的去调用:

torch.cuda.empty_cache()

3. 避免CPU与GPU间的传输

# bad.cpu()
.item()
.numpy()

大量使用 .item().cpu().numpy() 调用,对于性能来说是非常糟糕的,因为每个调用都将数据从GPU 传输到 CPU,从而极大地降低了性能.

如果试图清除附加的计算图,请使用 .detach() .

# good.detach()

其不会将内存转移到GPU,会删除任何附加到该变量的计算图.

4. 直接在GPUs上构建张量

大多数人都是这样在GPUs上创建张量的:

t = tensor.rand(2,2).cuda()

然而,这首先创建CPU张量,然后将其转移到GPU……这真的很慢.

相反,直接在想要的设备上创建张量:

t = tensor.rand(2,2, device=torch.device('cuda:0'))

5. 使用DistributedDataParallel

使用DistributedDataParallel不要使用DataParallel.

PyTorch有两个主要的模式用于在多 GPUs训练:

第一种是DataParallel,它将一批数据分割到多个GPUs上. 但这也意味着模型必须复制到每个GPU上,一旦在GPU 0 上计算出梯度,它们必须同步到其他GPU. 这需要大量昂贵的GPU传输.

第二种是,DistributedDataParallel在每个GPU(在它自己的进程中)上创建模型副本,并且只让数据的一部分对该GPU可用. 这就像是让N个独立的模型进行训练,除了一旦每个模型都计算出梯度,它们就会在模型之间同步梯度. 这意味着在每批处理中只在GPUs之间传输一次数据.

6. 使用16-bit精度

这是另一种加快训练速度的方法,实际上现在还没有很多人使用这种方法.

模型训练采用 16bit,数据从32位变到到16位,其有几个优点:

[1] - 使用了一半的内存(这意味着可以将batch大小翻倍,并将训练时间减半)

[2] - 某些GPU(V100, 2080Ti)可以自动加速(3 -8倍),因为它们针对16位计算进行了优化.

注:在PyTorch 1.6之前,还必须安装Nvidia Apex,现在16位是PyTorch的原生版本.

7. 对代码进行Profile

如果没有PyTorch-Lightning,最后一条建议可能很难实现,但可以使用cprofiler这样的工具来实现.

然而,在 PyTorch-Lightning 中,可以通过两种方式获得所有在训练期间所做的调用的总结:

第一种是,内置的basic profiler:

Trainer(profile=True)

可以给出这样的输出:

第二种是,高级的profiler:

profiler = AdvancedProfiler()
trainer = Trainer(profiler=profiler)

得到更细粒度的结果:

Last modification:May 8th, 2021 at 08:59 pm