PyTorch 2.2 中文官方教程(十一)(3)

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: PyTorch 2.2 中文官方教程(十一)

PyTorch 2.2 中文官方教程(十一)(2)https://developer.aliyun.com/article/1482552

扩展 PyTorch

使用自定义函数进行双向传播

原文:pytorch.org/tutorials/intermediate/custom_function_double_backward_tutorial.html

译者:飞龙

协议:CC BY-NC-SA 4.0

有时候需要通过向后图两次运行反向传播,例如计算高阶梯度。然而,要支持双向传播需要对 autograd 有一定的理解和谨慎。支持单次向后传播的函数不一定能够支持双向传播。在本教程中,我们展示了如何编写支持双向传播的自定义 autograd 函数,并指出一些需要注意的事项。

当编写自定义 autograd 函数以进行两次向后传播时,重要的是要知道自定义函数中的操作何时被 autograd 记录,何时不被记录,以及最重要的是,save_for_backward 如何与所有这些操作一起使用。

自定义函数隐式影响梯度模式的两种方式:

  • 在向前传播期间,autograd 不会记录任何在前向函数内执行的操作的图形。当前向完成时,自定义函数的向后函数将成为每个前向输出的 grad_fn
  • 在向后传播期间,如果指定了 create_graph 参数,autograd 会记录用于计算向后传播的计算图

接下来,为了了解 save_for_backward 如何与上述交互,我们可以探索一些示例:

保存输入

考虑这个简单的平方函数。它保存一个输入张量以备向后传播使用。当 autograd 能够记录向后传播中的操作时,双向传播会自动工作,因此当我们保存一个输入以备向后传播时,通常不需要担心,因为如果输入是任何需要梯度的张量的函数,它应该有 grad_fn。这样可以正确传播梯度。

import torch
class Square(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x):
        # Because we are saving one of the inputs use `save_for_backward`
        # Save non-tensors and non-inputs/non-outputs directly on ctx
        ctx.save_for_backward(x)
        return x**2
    @staticmethod
    def backward(ctx, grad_out):
        # A function support double backward automatically if autograd
        # is able to record the computations performed in backward
        x, = ctx.saved_tensors
        return grad_out * 2 * x
# Use double precision because finite differencing method magnifies errors
x = torch.rand(3, 3, requires_grad=True, dtype=torch.double)
torch.autograd.gradcheck(Square.apply, x)
# Use gradcheck to verify second-order derivatives
torch.autograd.gradgradcheck(Square.apply, x) 

我们可以使用 torchviz 来可视化图形以查看为什么这样可以工作

import torchviz
x = torch.tensor(1., requires_grad=True).clone()
out = Square.apply(x)
grad_x, = torch.autograd.grad(out, x, create_graph=True)
torchviz.make_dot((grad_x, x, out), {"grad_x": grad_x, "x": x, "out": out}) 

我们可以看到对于 x 的梯度本身是 x 的函数(dout/dx = 2x),并且这个函数的图形已经正确构建

保存输出

在前一个示例的轻微变化是保存输出而不是输入。机制类似,因为输出也与 grad_fn 相关联。

class Exp(torch.autograd.Function):
    # Simple case where everything goes well
    @staticmethod
    def forward(ctx, x):
        # This time we save the output
        result = torch.exp(x)
        # Note that we should use `save_for_backward` here when
        # the tensor saved is an ouptut (or an input).
        ctx.save_for_backward(result)
        return result
    @staticmethod
    def backward(ctx, grad_out):
        result, = ctx.saved_tensors
        return result * grad_out
x = torch.tensor(1., requires_grad=True, dtype=torch.double).clone()
# Validate our gradients using gradcheck
torch.autograd.gradcheck(Exp.apply, x)
torch.autograd.gradgradcheck(Exp.apply, x) 

使用 torchviz 来可视化图形:

out = Exp.apply(x)
grad_x, = torch.autograd.grad(out, x, create_graph=True)
torchviz.make_dot((grad_x, x, out), {"grad_x": grad_x, "x": x, "out": out}) 

保存中间结果

更棘手的情况是当我们需要保存一个中间结果时。我们通过实现以下情况来演示这种情况:

s i n h ( x ) : = e x − e − x 2 sinh(x) := \frac{e^x - e^{-x}}{2}sinh(x):=2exex

由于 sinh 的导数是 cosh,因此在向后计算中重复使用 exp(x)和 exp(-x)这两个中间结果可能很有用。

尽管如此,中间结果不应直接保存并在向后传播中使用。因为前向是在无梯度模式下执行的,如果前向传递的中间结果用于计算向后传递中的梯度,则梯度的向后图将不包括计算中间结果的操作。这会导致梯度不正确。

class Sinh(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x):
        expx = torch.exp(x)
        expnegx = torch.exp(-x)
        ctx.save_for_backward(expx, expnegx)
        # In order to be able to save the intermediate results, a trick is to
        # include them as our outputs, so that the backward graph is constructed
        return (expx - expnegx) / 2, expx, expnegx
    @staticmethod
    def backward(ctx, grad_out, _grad_out_exp, _grad_out_negexp):
        expx, expnegx = ctx.saved_tensors
        grad_input = grad_out * (expx + expnegx) / 2
        # We cannot skip accumulating these even though we won't use the outputs
        # directly. They will be used later in the second backward.
        grad_input += _grad_out_exp * expx
        grad_input -= _grad_out_negexp * expnegx
        return grad_input
def sinh(x):
    # Create a wrapper that only returns the first output
    return Sinh.apply(x)[0]
x = torch.rand(3, 3, requires_grad=True, dtype=torch.double)
torch.autograd.gradcheck(sinh, x)
torch.autograd.gradgradcheck(sinh, x) 

使用 torchviz 来可视化图形:

out = sinh(x)
grad_x, = torch.autograd.grad(out.sum(), x, create_graph=True)
torchviz.make_dot((grad_x, x, out), params={"grad_x": grad_x, "x": x, "out": out}) 

保存中间结果:不要这样做

现在我们展示当我们不返回中间结果作为输出时会发生什么:grad_x 甚至不会有一个反向图,因为它纯粹是一个函数 exp 和 expnegx,它们不需要 grad。

class SinhBad(torch.autograd.Function):
    # This is an example of what NOT to do!
    @staticmethod
    def forward(ctx, x):
        expx = torch.exp(x)
        expnegx = torch.exp(-x)
        ctx.expx = expx
        ctx.expnegx = expnegx
        return (expx - expnegx) / 2
    @staticmethod
    def backward(ctx, grad_out):
        expx = ctx.expx
        expnegx = ctx.expnegx
        grad_input = grad_out * (expx + expnegx) / 2
        return grad_input 

使用 torchviz 来可视化图形。请注意,grad_x 不是图形的一部分!

out = SinhBad.apply(x)
grad_x, = torch.autograd.grad(out.sum(), x, create_graph=True)
torchviz.make_dot((grad_x, x, out), params={"grad_x": grad_x, "x": x, "out": out}) 

当不跟踪反向传播时

最后,让我们考虑一个例子,即 autograd 可能根本无法跟踪函数的反向梯度。我们可以想象 cube_backward 是一个可能需要非 PyTorch 库(如 SciPy 或 NumPy)或编写为 C++扩展的函数。这里演示的解决方法是创建另一个自定义函数 CubeBackward,在其中手动指定 cube_backward 的反向传播!

def cube_forward(x):
    return x**3
def cube_backward(grad_out, x):
    return grad_out * 3 * x**2
def cube_backward_backward(grad_out, sav_grad_out, x):
    return grad_out * sav_grad_out * 6 * x
def cube_backward_backward_grad_out(grad_out, x):
    return grad_out * 3 * x**2
class Cube(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x):
        ctx.save_for_backward(x)
        return cube_forward(x)
    @staticmethod
    def backward(ctx, grad_out):
        x, = ctx.saved_tensors
        return CubeBackward.apply(grad_out, x)
class CubeBackward(torch.autograd.Function):
    @staticmethod
    def forward(ctx, grad_out, x):
        ctx.save_for_backward(x, grad_out)
        return cube_backward(grad_out, x)
    @staticmethod
    def backward(ctx, grad_out):
        x, sav_grad_out = ctx.saved_tensors
        dx = cube_backward_backward(grad_out, sav_grad_out, x)
        dgrad_out = cube_backward_backward_grad_out(grad_out, x)
        return dgrad_out, dx
x = torch.tensor(2., requires_grad=True, dtype=torch.double)
torch.autograd.gradcheck(Cube.apply, x)
torch.autograd.gradgradcheck(Cube.apply, x) 

使用 torchviz 来可视化图形:

out = Cube.apply(x)
grad_x, = torch.autograd.grad(out, x, create_graph=True)
torchviz.make_dot((grad_x, x, out), params={"grad_x": grad_x, "x": x, "out": out}) 

总之,双向传播是否适用于您的自定义函数取决于反向传播是否可以被 autograd 跟踪。通过前两个示例,我们展示了双向传播可以直接使用的情况。通过第三和第四个示例,我们展示了使反向函数可以被跟踪的技术,否则它们将无法被跟踪。

使用自定义函数融合卷积和批量归一化

原文:pytorch.org/tutorials/intermediate/custom_function_conv_bn_tutorial.html

译者:飞龙

协议:CC BY-NC-SA 4.0

注意

点击这里下载完整示例代码

将相邻的卷积和批量归一化层融合在一起通常是一种推理时间的优化,以提高运行时性能。通常通过完全消除批量归一化层并更新前面卷积的权重和偏置来实现[0]。然而,这种技术不适用于训练模型。

在本教程中,我们将展示一种不同的技术来融合这两个层,可以在训练期间应用。与改进运行时性能不同,这种优化的目标是减少内存使用。

这种优化的理念是看到卷积和批量归一化(以及许多其他操作)都需要在前向传播期间保存其输入的副本以供反向传播使用。对于大批量大小,这些保存的输入占用了大部分内存,因此能够避免为每个卷积批量归一化对分配另一个输入张量可以显著减少内存使用量。

在本教程中,我们通过将卷积和批量归一化合并为单个层(作为自定义函数)来避免这种额外的分配。在这个组合层的前向传播中,我们执行正常的卷积和批量归一化,唯一的区别是我们只保存卷积的输入。为了获得批量归一化的输入,这对于反向传播是必要的,我们在反向传播期间再次重新计算卷积的前向传播。

重要的是要注意,这种优化的使用是情境性的。虽然(通过避免保存一个缓冲区)我们总是在前向传播结束时减少分配的内存,但在某些情况下,峰值内存分配实际上可能并未减少。请查看最后一节以获取更多详细信息。

为简单起见,在本教程中,我们将 Conv2D 的 bias=False,stride=1,padding=0,dilation=1 和 groups=1 硬编码。对于 BatchNorm2D,我们将 eps=1e-3,momentum=0.1,affine=False 和 track_running_statistics=False 硬编码。另一个小的区别是在计算批量归一化时,在平方根的分母外部添加了 epsilon。

[0] nenadmarkus.com/p/fusing-batchnorm-and-conv/

卷积的反向传播公式实现

实现自定义函数需要我们自己实现反向传播。在这种情况下,我们需要为 Conv2D 和 BatchNorm2D 分别实现反向传播公式。最终,我们会将它们链接在一起形成统一的反向传播函数,但在下面,我们首先将它们实现为各自的自定义函数,以便验证它们的正确性

import torch
from torch.autograd.function import once_differentiable
import torch.nn.functional as F
def convolution_backward(grad_out, X, weight):
    grad_input = F.conv2d(X.transpose(0, 1), grad_out.transpose(0, 1)).transpose(0, 1)
    grad_X = F.conv_transpose2d(grad_out, weight)
    return grad_X, grad_input
class Conv2D(torch.autograd.Function):
    @staticmethod
    def forward(ctx, X, weight):
        ctx.save_for_backward(X, weight)
        return F.conv2d(X, weight)
    # Use @once_differentiable by default unless we intend to double backward
    @staticmethod
    @once_differentiable
    def backward(ctx, grad_out):
        X, weight = ctx.saved_tensors
        return convolution_backward(grad_out, X, weight) 

在使用gradcheck进行测试时,重要的是使用双精度

weight = torch.rand(5, 3, 3, 3, requires_grad=True, dtype=torch.double)
X = torch.rand(10, 3, 7, 7, requires_grad=True, dtype=torch.double)
torch.autograd.gradcheck(Conv2D.apply, (X, weight)) 
True 

PyTorch 2.2 中文官方教程(十一)(4)https://developer.aliyun.com/article/1482555#slide-3

相关文章
|
存储 物联网 PyTorch
基于PyTorch的大语言模型微调指南:Torchtune完整教程与代码示例
**Torchtune**是由PyTorch团队开发的一个专门用于LLM微调的库。它旨在简化LLM的微调流程,提供了一系列高级API和预置的最佳实践
885 59
基于PyTorch的大语言模型微调指南:Torchtune完整教程与代码示例
|
并行计算 监控 搜索推荐
使用 PyTorch-BigGraph 构建和部署大规模图嵌入的完整教程
当处理大规模图数据时,复杂性难以避免。PyTorch-BigGraph (PBG) 是一款专为此设计的工具,能够高效处理数十亿节点和边的图数据。PBG通过多GPU或节点无缝扩展,利用高效的分区技术,生成准确的嵌入表示,适用于社交网络、推荐系统和知识图谱等领域。本文详细介绍PBG的设置、训练和优化方法,涵盖环境配置、数据准备、模型训练、性能优化和实际应用案例,帮助读者高效处理大规模图数据。
466 5
|
并行计算 Ubuntu PyTorch
Ubuntu下CUDA、Conda、Pytorch联合教程
本文是一份Ubuntu系统下安装和配置CUDA、Conda和Pytorch的教程,涵盖了查看显卡驱动、下载安装CUDA、添加环境变量、卸载CUDA、Anaconda的下载安装、环境管理以及Pytorch的安装和验证等步骤。
5790 1
Ubuntu下CUDA、Conda、Pytorch联合教程
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(十九)(1)
PyTorch 2.2 中文官方教程(十九)
398 1
PyTorch 2.2 中文官方教程(十九)(1)
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(十八)(4)
PyTorch 2.2 中文官方教程(十八)
317 1
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(二十)(4)
PyTorch 2.2 中文官方教程(二十)
463 0
PyTorch 2.2 中文官方教程(二十)(4)
|
Android开发 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(二十)(2)
PyTorch 2.2 中文官方教程(二十)
493 0
PyTorch 2.2 中文官方教程(二十)(2)
|
iOS开发 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(二十)(1)
PyTorch 2.2 中文官方教程(二十)
359 0
PyTorch 2.2 中文官方教程(二十)(1)
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(十九)(3)
PyTorch 2.2 中文官方教程(十九)
421 0
PyTorch 2.2 中文官方教程(十九)(3)
|
异构计算 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(十九)(2)
PyTorch 2.2 中文官方教程(十九)
372 0
PyTorch 2.2 中文官方教程(十九)(2)

热门文章

最新文章

推荐镜像

更多