随CUDA 11发布PyTorch 1.7,FFT新API,赢得分布式培训

2020-10-28 03:09:17

PyTorch 1.7版本包括许多新的API,包括支持与NumPy兼容的FFT操作、评测工具以及对基于分布式数据并行(DDP)和远程过程调用(RPC)的分布式培训的重大更新。此外,几个功能已经稳定下来,包括自定义C++类、内存分析器、创建自定义类张量对象、RPC中的用户异步函数以及Torch.Distributed中的许多其他功能,如每RPC超时、DDP动态分组和RRef帮助器。

对Autograd分析器中的RPC、TorchScript和堆栈跟踪的分析和性能进行了更新和添加。

再次重申,从PyTorch1.6开始,特性现在分为稳定、测试版和原型。你可以在这里看到详细的公告。请注意,此博客中列出的原型功能已作为此版本的一部分提供。

与FFT相关的功能通常用于各种科学领域,如信号处理。虽然PyTorch过去一直支持一些与FFT相关的函数,但1.7版本添加了一个新的torch.fft模块,该模块使用与NumPy相同的API实现与FFT相关的函数。

这个新模块必须导入才能在1.7版本中使用,因为它的名称与历史悠久(现已弃用)的torch.fft函数冲突。

>;>;>;导入手电筒。FFT>;>;>;t=火炬。Arange(4)>;>;>;t张量([0,1,2,3])>;>;火炬。快速傅立叶变换。FFT(T)张量([6.+0.j,-2.+2.j,-2.+0.j,-2.-2.j])>;>;>;t=张量([0.。+1.j,2.+3.j,4.+5.j,6.+7.j])>;>;>;火炬。快速傅立叶变换。FFT(T)张量([12.+16.j,-8.+0.j,-4.-4.j,0.。-8.j])。

从PyTorch1.5开始,我们继续保持Python和C++前端API之间的一致性。此更新允许开发人员使用C++前端的nn.former模块抽象。此外,开发人员不再需要将模块从python/JIT保存并加载到C++中,因为它现在可以在C++中直接使用。

再现性(逐位确定性)可能有助于在调试或测试程序时识别错误。为了便于重现性,PyTorch 1.7添加了torch.set_defiristic(Bool)函数,该函数可以指导PyTorch操作符选择确定性算法(如果可用),并在操作可能导致不确定性行为时抛出运行时错误。默认情况下,此函数控制的标志为false,并且行为没有变化,这意味着默认情况下PyTorch可能不确定地实现其操作。

具有确定性变体的操作使用这些变体(与非确定性版本相比,通常会降低性能);以及。

请注意,对于PyTorch程序的单次运行中的确定性而言,这是必要的,但不是充分的。其他随机性来源,如随机数生成器、未知操作、异步或分布式计算,仍可能导致不确定行为。

用户现在不仅可以看到分析器输出表中的操作员名称/输入,还可以看到操作员在代码中的位置。工作流程只需极少的更改即可利用此功能。用户像以前一样使用自动评分探查器,但带有可选的新参数:WITH_STACK和GROUP_BY_STACK_n。注意:常规分析运行不应使用此功能,因为它会增加大量开销。

Torchelastic提供了当前Torch.Distributed的严格超集。启动CLI时增加了容错和弹性功能。如果用户对容错不感兴趣,他们可以通过设置max_restarts=0获得精确的功能/行为奇偶校验,同时增加了自动分配RANK和MASTER_ADDR|PORT的便利性(而不是在torch.Distributed.Launch中手动指定)。

通过将Torchelastic捆绑在与PyTorch相同的底座图像中,用户可以立即开始尝试Torchelastic,而不必单独安装Torchelastic。除了方便之外,当在现有Kubeflow的分布式PyTorch操作符中添加对弹性参数的支持时,这项工作也是非常有用的。

PyTorch1.7引入了一个新的上下文管理器,该管理器将与使用torch.nn.parallel.DistributedDataParallel训练的模型结合使用,以支持跨不同进程使用不均匀的数据集进行训练。此功能在使用DDP时提供了更大的灵活性,使用户不必手动确保不同流程中的数据集大小相同。使用此上下文管理器,DDP将自动处理不均匀的数据集大小,这可以防止培训结束时出现错误或挂起。

在过去,NCCL的训练运行会因为集体卡住而无限期地挂起,给用户带来了非常不愉快的体验。如果检测到潜在的挂起,此功能将中止挂起的集合,并抛出异常/使进程崩溃。当与torchelastic(它可以从最后一个检查点恢复训练过程)之类的东西一起使用时,用户可以获得更高的分布式训练可靠性。此功能完全是选择加入的,并且位于需要显式设置才能启用此功能的环境变量之后(否则用户将看到与以前相同的行为)。

在早期版本中,Torch.Distributed.rpc.rpc_async已在TorchScript中提供。对于PyTorch1.7,此功能将扩展到剩下的两个核心RPCAPI:torch.Distributed.rpc.rpc_sync和torch.Distributed.rpc.remote。这将完成计划在TorchScript中支持的主要RPC API,它允许用户在TorchScript中使用现有的python RPC API(在脚本函数或脚本方法中,这将释放python全局解释器锁),并可能提高多线程环境中的应用程序性能。

PyTorch提供了一系列用于训练算法的优化器,这些优化器已作为python API的一部分重复使用。然而,用户通常希望使用多线程训练而不是多进程训练,因为它在大规模分布式训练(例如,分布式模型并行)或任何基于RPC的训练应用程序的上下文中提供了更好的资源利用率和效率。用户以前不能使用分布式优化器做到这一点,因为我们需要摆脱python全局解释器锁(GIL)的限制才能实现这一点。

在PyTorch1.7中,我们启用了分布式优化器中的TorchScript支持来删除GIL,并使优化器能够在多线程应用程序中运行。新的分布式优化器具有与以前完全相同的接口,但是它会自动将每个Worker中的优化器转换为TorchScript,从而使每个GIL空闲。这是通过利用功能优化器概念并允许分布式优化器将优化器的计算部分转换为TorchScript来实现的。这将有助于使用分布式模型并行训练等用例,并使用多线程提高性能。

目前,唯一支持使用TorchScript进行自动转换的优化器是Adagrad,所有其他优化器在没有TorchScript支持的情况下仍然可以像以前一样工作。我们正在努力将覆盖范围扩大到所有PyTorch优化器,并期待在未来的版本中提供更多内容。启用TorchScript支持的用法是自动的,与现有的python API完全相同,下面是一个如何使用的示例:

导入手电筒。分布式的。自动分级为dist_autograd导入手电筒。分布式的。RPC作为来自TORCH的RPC从TORCH导入Optim。分布式的。Optim import DistributedOptimizer with dist_autograd。Context()as context_id:#转发。Rref1=RPC。远程(";工人1";,手电筒。ADD,args=(手电筒。1(2),3))rref2=RPC。远程(";工人1";,手电筒。ADD,args=(手电筒。1(2),1))损耗=rref1。To_here()+rref2。To_here()#向后传递。Dist_autograd。向后(context_id,[丢失。Sum()])#Optimizer,传入Optim.Adagrad,DistributedOptimizer会#自动将其转换/编译为TorchScript(GIL-free)dist_optim=DistributedOptimizer(Optim.。Adagrad,[rref1,rref2],LR=0.05,)dist_optim。步骤(Context_Id)。

PyTorch 1.6首次引入了对结合使用PyTorch分析器和RPC框架的支持。在PyTorch 1.7中,进行了以下增强:

用户现在可以使用熟悉的性能分析工具,比如torch.autograd.profiler.profile()和torch.autograd.profiler.record_function,这可以透明地与RPC框架配合使用,这些RPC框架具有全功能支持、配置文件异步函数和TorchScript函数。

PyTorch1.7为Windows平台上的DistributedDataParallel和集合通信提供了原型支持。在此版本中,仅支持基于Gloo的ProcessGroup和FileStore。要跨多台计算机使用此功能,请在init_process_group中提供来自共享文件系统的文件。

#初始化进程组dist。INIT_PROCESS_GROUP(";GLOO";,#多机示例:#共享文件需要六个";/";#INIT_METHOD=`";file:/{machine}/{share_folder}/file";`#本地文件需要三个";/";INIT_METHOD=";file:///{your本地文件路径}";,RANK=RANK,WORLD_SIZE=WORLD_SIZE)model=DistributedDataParallel(LOCAL_MODEL,DEVICE_ID=[RANK])

PyTorch Mobile支持iOS和Android,CocoaPods和J Center分别提供二进制软件包。您可以在这里了解有关PyTorch-Mobile的更多信息。

在一些移动平台上,比如Pixel,我们观察到内存被更积极地归还给系统。这导致频繁的页面错误,因为作为功能框架的PyTorch不维护操作符的状态。因此,对于大多数操作,每次执行操作时都会动态分配输出。为了改善由此造成的性能损失,PyTorch1.7为CPU提供了一个简单的缓存分配器。分配器按张量大小缓存分配,目前只能通过PyTorch C++API使用。缓存分配器本身归客户端所有,因此分配器的生命周期也由客户端代码维护。这样的客户端拥有的缓存分配器然后可以与作用域保护c10::WithCPUCachingAllocatorGuard一起使用,以允许在该作用域内使用缓存分配。

#include<;c10/mobile/CPUCachingAllocator.h>;.c10::CPUCachingAllocator CACHING_ALLOCATOR;//客户端代码拥有。可以是某个客户端类的成员,以便//将缓存分配器的生存期与类的生存期捆绑在一起.{c10::optional<;c10::WithCPUCachingAllocatorGuard>;CACHING_ALLOCATOR_GROUDE;IF(FLAGS_USE_CACHING_ALLOCATOR){CACHING_ALLOCATOR_GROUTE。Emplace(&;cachingallocator);}....。模特。前进(……);}……。

注意:缓存分配器仅在移动版本上可用,因此在移动版本之外使用缓存分配器将不会有效。

以前,torch.conj和Tensor.conj正在为real dtype的张量创建克隆。它现在按原样返回张量以提高性能。可以通过为实张量添加.clone()来恢复原始行为。注意,此行为与numpy不同,在numpy中,np.conj返回新的ndarray,而ndarray.conj按原样返回ndarray。

Torch.tensor、torch.as_tensor和torch.parse_coo_tensor现在使用未指定的输入张量设备(#41984)。

这将更改在其上创建张量的设备,因此用户可以开始看到设备不匹配错误。这也意味着对于稀疏张量,如果未指定设备,则提供的两个张量必须位于同一设备上。您可以通过传递设备参数来恢复原始行为。

>;>;>;t设备设备(type=‘cuda:0’)>;>;>;#张量构造函数>;>;火炬。张量(t,dtype=火炬。浮动32)。设备设备(type=‘cpu’)>;>;>;#稀疏构造函数>;>;火炬。稀疏Coo张量(手电筒。张量(([0],[2]),设备=";CPU";),手电筒。张量(([1.],),设备=";Cuda";),大小=(3,3,1))。设备设备(type=';cuda';,index=0)

>;>;>;t设备设备(type=‘cuda:0’)>;>;>;#张量构造函数>;>;火炬。张量(t,dtype=火炬。浮动32)。Device device(type=‘cuda:0’)>;>;>;#指定设备以获得与1.6>;>;>;Torch相同的行为。张量(t,dtype=火炬。Flat32,device=';cpu';)。设备设备(type=‘cpu’)>;>;>;#稀疏构造函数>;>;火炬。稀疏Coo张量(手电筒。张量(([0],[2]),设备=";CPU";),手电筒。张量(([1.],),设备=";Cuda";),大小=(3,3,1))。设备运行错误:索引后端(CPU)必须与值后端(CUDA)>;>;>;#指定设备以获得与1.6>;>;>;Torch相同的行为。稀疏Coo张量(手电筒。张量(([0],[2]),设备=";CPU";),手电筒。张量(([1.],),Device=";Cuda";),大小=(3,3,1),Device=";Cuda:0";)。设备设备(type=';cuda';,index=0)。

在进行此更改之前,当使用Keepdim=True和p=';fro';或p=number调用torch.norm时,将所有其他可选参数保留为其默认值时,Keepdim参数将被忽略。它现在受到了适当的尊重。此外,任何时候使用p=#39;nuc&39;和Keepdim=True调用torch.norm时,结果都会比输入少一个维度,并且维度可能会乱序,具体取决于要减少的维度。它现在正确地保留了所有的维度。您可以通过设置Keepdim=False来恢复原始行为。注意:该函数现在已弃用(见下文),我们建议您使用torch.linalg.norm,它遵循NumPy的约定。

>;>;>;T.size()手电筒。Size([4,4])>;>;>;t.norm(p=‘fro’,Keepdim=True)。大小()手电筒。Size([])>;>;>;t.norm(p=3,Keepdim=True)。大小()手电筒。Size([])>;>;>;t.norm(p=‘nuc’,Keepdim=True)。大小()手电筒。大小([1])。

>;>;>;T.size()手电筒。Size([4,4])>;>;>;t.norm(p=‘fro’,Keepdim=True)。大小()手电筒。Size([1,1])>;>;>;t.Norm(p=3,Keepdim=True)。大小()手电筒。Size([1,1])>;>;>;t.norm(p=‘nuc’,Keepdim=True)。大小()手电筒。大小([1,1])。

自动分级系统能够通过显式跟踪已知的视图操作来正确地处理通过张量的视图的修改。在以前的版本中,torch.plit和torch.chunk没有被标记为已知的视图操作,这可能会导致静默错误的渐变。

请注意,从V1.5开始,不建议对返回多个视图的函数创建的视图进行就地修改。自动分级器没有正确处理这种情况,可能会导致内部错误或错误的渐变。因此,作为此视图修复的副作用,就地修改torch.plit和torch.chunk的输出现在将引发警告,并可能导致内部错误或错误的渐变,而它们以前是静默地计算错误的渐变。如果您看到这样的警告,您应该将就地操作替换为不适当的操作。您可以使用新的torch.unsafe_plit和torch.unsafe_chunk恢复原始行为。请注意,此处的这些函数只是为了简化过渡,在将来的版本中也将删除。

Torch.argmin(torch.argmax)现在总是返回第一个最小(最大)元素的索引。此选择与NumPy一致。以前,如果存在多个最小值(最大值),则返回的索引可能是其中任何一个的索引。您无法恢复原始行为,因为它依赖于平台且不能保证。如果您的代码依赖于特定平台的特定索引,则应将其更新为使用第一个索引,此新代码将在所有平台上运行。

如果未指定尺寸,则执行完全缩减,并且渐变现在将均匀地向实现输出值的所有输入回流。旧的行为是只为任意选择的一个这样的输入传播梯度。这应该会通过梯度下降来提高训练的稳定性。要恢复之前的行为,可以使用dim=参数执行缩减。它将确保渐变仅针对其索引被返回的输入回流。

这是该OP的弃用周期的结束,以确保它与PyTorch代码库中所有其他地方使用的numpy的广播语义相比没有不同的广播语义。您需要确保所有输入的大小相同,以避免错误。

>;>;>;bceloss=nn.。BCELoss()>;>;>;a=手电筒。兰德(25)>;>;b=火炬。Rand(25,1)>;>;bceloss(a,b)UserWarning:使用目标大小(手电筒。尺寸([25,1]))与输入尺寸(手电筒)不同。大小([25]))已弃用。请确保它们的尺寸是一样的。张量(1.0604)。

>;>;>;bceloss=nn.。BCELoss()>;>;>;a=手电筒。兰德(25)>;>;b=火炬。Rand(25,1)>;>;>;bceloss(a,b)值错误:使用目标大小(手电筒。尺寸([25,1]))与输入尺寸(手电筒)不同。大小([25]))已弃用。请确保它们的尺寸是一样的。>;>;>;b=B.重塑(25)>;>;>;bceloss(a,b)张量(1.0604)。

为了提高性能,当输入是可微的,但用户的Backup函数没有为其返回None时,定制的autograd.Function将不会创建一个满是零的张量。这意味着.backward()或autograd.grad()最终结果现在将为None,而它过去是一个满是零的张量。您可以通过自定义自动分级来恢复以前的行为。函数使用torch.zeros_like(输入)实体化零张量,以替换向后方法的None输出。

Import torch#为渐变类GetTwos(Torch.。自动升级。函数):@staticmethod def ward(ctx,inp):返回inp。克隆()。Fill_(2)@staticmethod def back(ctx,grad_out):#要恢复1.6行为,请将下面一行替换为`Return torch.zeros_like(Grad_Out)`return NONE a=torch。Rand(10,Requires_grad=True)b=GetTwos。申请(A)B.sum()。Backward()print(a.grad)#在PyTorch 1.6中,这将打印#tensor([0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.])#在PyTorch 1.7中,这将打印#NONE。

我们修复了就地检测代码中的一个错误,该错误阻止检测一些不可微的输出就地操作(如整型张量)。这可能会导致过去运行良好的代码抛出错误“在原地操作中修改了向后所需的张量”。这种故障是真实的,必须修复用户代码才能计算正确的梯度。通常,这涉及在就地修改张量之前克隆张量,以确保可以安全地进行后向传递。

导入手电筒a=手电筒。带手电筒的RAND(10,Requires_grad=True)。No_grad():a[2]=10b,ind=A。max(dim=0)#ind在手电筒中为2。No_grad():t=手电筒。兰德(10)t[4]=10 RES=手电筒。MAX(t,Dim=0,Out=(手电筒。Tensor(),ind)#ind在这里变成4#这个向后运行在1.6中运行,但在1.7B.sum()中会失败。Backward()print(a.grad)#tensor([0.,0.,0.,0.,1.,0.,0.,0.,0.,0.])#错误的值在索引4处,而它应该在索引2处#通过将上面的行#替换为:#res=torch.max(t,dim=0,out=(torch.张量器(),ind.clone()可以避免这个问题。

可以恢复“子类上的任何操作都会产生火炬。张量器而不是子类”的旧行为

.