【pytorch】自定义autograd中的Function

前言

自定义pytorch中动态图的算子(operator),也就是动态图的“边”,需要继承torch.autograd.Function类,并实现forwardbackward方法。在使用自定义的算子时,需要使用apply方法。下面结合官网资料的两个例子加以说明。
 

实例一

 class MyExp(torch.autograd.Function):
     """ 前向:y = exp(x), 微分:dydx = exp(x) """

     @staticmethod
     def forward(ctx, inputs):
         """ 实现前向传播逻辑 """
         outputs = inputs.exp()
         ctx.save_for_backward(outputs)
         return outputs

     @staticmethod
     def backward(ctx, grad_outputs):
         """ 实现反向传播, grad_outputs为损失对前向传播中输出的梯度,但需要计算的是损失对输入的梯度 """
         outputs, = ctx.saved_tensors
         return grad_outputs * outputs


 inputs = torch.tensor([1.0, 2.0, 3.0], device='cpu', requires_grad=True)
 outputs = MyExp.apply(inputs)
 outputs.backward(torch.ones(outputs.shape))  # torch.ones(outputs.shape)为对自己的梯度
 grad1 = inputs.grad
 inputs.grad = None # 梯度清零
 print(grad1)

 print("***********测试内置算子的梯度***********")
 outputs = inputs.exp()
 outputs.sum().backward()
 grad2 = inputs.grad
 print(grad2)

结果如下:
在这里插入图片描述

注意事项:

  • forward与backward方法的第一个参数为ctx,该对象为上下文管理器,ctx对象、forward与backward方法的详细信息参考补充资料。
  • 自定义Function类调用时需要使用apply方法,apply方法的详细说明信息暂时未知。
     

实例二

class LegendrePolynomial3(torch.autograd.Function):
    @staticmethod
    def forward(ctx, inputs):
        ctx.save_for_backward(inputs)
        return 0.5 * (5 * inputs ** 3 - 3 * inputs)

    @staticmethod
    def backward(ctx, grad_outputs):
        inputs, = ctx.saved_tensors
        return grad_outputs * 1.5 * (5 * inputs ** 2 - 1)


start = time.time()
dtype = torch.float
device = torch.device('cpu')
# device = torch.device('cuda:0') # uncommet this line to run on GPU

x = torch.linspace(-math.pi, math.pi, 2000, dtype=dtype, device=device)
y = torch.sin(x)

a = torch.full((), 0.0,  dtype=dtype, device=device, requires_grad=True)
b = torch.full((), -1.0, dtype=dtype, device=device, requires_grad=True)
c = torch.full((), 0.0, dtype=dtype, device=device, requires_grad=True)
d = torch.full((), 0.3, dtype=dtype, device=device, requires_grad=True)
learning_rate = 5e-6

P3 = LegendrePolynomial3.apply
for i in range(2000):
    y_pred = a + b * P3(c + d * x)
    loss = (y_pred - y).pow(2).sum()

    loss.backward()
    with torch.no_grad():
        a -= a.grad * learning_rate
        b -= b.grad * learning_rate
        c -= c.grad * learning_rate
        d -= d.grad * learning_rate

        a.grad, b.grad, c.grad, d.grad = None, None, None, None

    if i % 100 == 0:
        print("id: %s, loss: %s" % (i, loss.item()))

print(f"{a.item()} + {b.item()} * P3({c.item()}  + {d.item()} * x )")
print(f" totally costs {time.time() - start} seconds.")

结果如下:
实例二结果
 

补充材料:

ctx.save_for_backward()
ctx.save_for_backward(*tensors)

ctx是context上下文管理对象的缩写,该方法主要是用于存储反向传播计算所需的数据。反向传播计算梯度时,需要什么就存储什么。例如实例一,y = exp(x),在定义forward方法时需要存储forward方法的输出——因为dydx= exp(x),梯度正好是forward的结果。 而实例二中, y = 0.5 * (5 * x ** 3 - 3 * x),存储的是x——因为dydx = 1.5 * (5 * x ** 2 - 1),计算梯度需要的是x

save_for_backward方法可以存储任意多个张量,在反向传播时,可通过上下文对象的saved_tensors属性获取,该属性值为元组。 注意ctx是定义forward方法必须且第一个参数,一般情况下在farward定义中调用ctx.save_for_backward()也是必须的,且存储的张量最多只能获取一次
 

Function类的farward方法
STATIC forward(ctx: Any, *args: Any, **kwargs: Any) → Any

第一个参数必须为context上下文管理对象,该对象的作用是存储反向传播计算梯度所需的数据。
 

Function类的backward方法
STATIC backward(ctx: Any, *grad_outputs: Any) → Any

第一个参数必须为ctx对象,该对象在方向传播过程中,主要用于取出存储在其saved_tensors属性中的数据。后接任意个参数,这些输入参数是对前向传播中输出的梯度值,backward方法的返回值为对前向传播中输入的梯度。注意反向传播得到的对输入的梯度,需要与对前向传播中输出的梯度相匹配。

 

参考资料

  1. torch.autograd.Function
  2. PyTorch: Defining new autograd functions
  3. pytorch的自定义拓展之(一)
  4. pytorch的自定义拓展之(二)
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyTorch,您可以通过编写自定义的backward函数来实现自定义的梯度计算。这可以用于自定义损失函数、自定义层或其他需要自定义梯度计算的情况。 要自定义backward函数,您需要定义一个函数,它接受输入张量的梯度和其他参数,并返回相对于输入张量的梯度。然后,您可以将这个函数作为一个属性附加到您定义的自定义函数上。 下面是一个简单的示例,展示了如何实现一个自定义的梯度计算函数: ```python import torch class MyFunction(torch.autograd.Function): @staticmethod def forward(ctx, input): # 在forward函数,您可以保存任何需要在backward函数使用的间结果 ctx.save_for_backward(input) return input @staticmethod def backward(ctx, grad_output): # 在backward函数,您可以根据需要计算相对于输入的梯度 input, = ctx.saved_tensors grad_input = grad_output * 2 * input # 这里只是一个示例,您可以根据自己的需求编写梯度计算逻辑 return grad_input # 使用自定义函数创建输入张量 x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) # 使用自定义函数进行前向传播 output = MyFunction.apply(x) # 计算损失 loss = output.sum() # 执行反向传播 loss.backward() # 打印输入张量的梯度 print(x.grad) ``` 在这个示例,我们定义了一个名为`MyFunction`的自定义函数,它将输入张量作为输出返回,并且在backward函数计算相对于输入张量的梯度。我们使用`MyFunction.apply`方法应用自定义函数,并且可以通过调用`backward`方法来计算梯度。 请注意,自定义函数需要继承自`torch.autograd.Function`类,并且前向传播和反向传播函数都需要用`@staticmethod`修饰。 这只是一个简单的示例,您可以根据自己的需求编写更复杂的自定义backward函数。希望对您有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值