【问题标题】:Back-Propagation of y = x / sum(x, dim=0) where size of tensor x is (H,W)y = x / sum(x, dim=0) 的反向传播,其中张量 x 的大小为 (H,W)
【发布时间】:2021-05-11 07:58:50
【问题描述】:

第一季度。

我正在尝试使用 pytorch 制作我的自定义 autograd 函数。

但是我在使用 y = x / sum(x, dim=0) 进行分析反向传播时遇到了问题

其中张量 x 的大小是(高度,宽度)(x 是二维的)。

这是我的代码

class MyFunc(torch.autograd.Function):
@staticmethod
def forward(ctx, input):
  ctx.save_for_backward(input)
  input = input / torch.sum(input, dim=0)

  return input

@staticmethod
def backward(ctx, grad_output):
  input = ctx.saved_tensors[0]
  H, W = input.size()
  sum = torch.sum(input, dim=0)
  grad_input = grad_output * (1/sum - input*1/sum**2)

  return grad_input

我用 (torch.autograd import) gradcheck 来比较雅可比矩阵,

from torch.autograd import gradcheck
func = MyFunc.apply
input = (torch.randn(3,3,dtype=torch.double,requires_grad=True))
test = gradcheck(func, input)

结果是

请有人帮我得到正确的反向传播结果

谢谢!


第二季度。

感谢您的回答!

由于您的帮助,我可以在 (H,W) 张量的情况下实现反向传播。

但是,当我在 (N,H,W) 张量的情况下实现反向传播时,我遇到了一个问题。 我认为问题在于初始化新张量。

这是我的新代码

import torch
import torch.nn as nn
import torch.nn.functional as F

class MyFunc(torch.autograd.Function):
  @staticmethod
  def forward(ctx, input):
    ctx.save_for_backward(input)
    
    N = input.size(0)
    for n in range(N):
      input[n] /= torch.sum(input[n], dim=0)

    return input

  @staticmethod
  def backward(ctx, grad_output):
    input = ctx.saved_tensors[0]
    N, H, W = input.size()
    I = torch.eye(H).unsqueeze(-1)
    sum = input.sum(1)

    grad_input = torch.zeros((N,H,W), dtype = torch.double, requires_grad=True)
    for n in range(N):
      grad_input[n] = ((sum[n] * I - input[n]) * grad_output[n] / sum[n]**2).sum(1)

    return grad_input

Gradcheck 代码是

from torch.autograd import gradcheck
func = MyFunc.apply
input = (torch.rand(2,2,2,dtype=torch.double,requires_grad=True))
test = gradcheck(func, input)
print(test)

结果是 enter image description here

不知道为什么会出现错误...

您的帮助将对我实现自己的卷积网络非常有帮助。

谢谢!祝你有美好的一天。

【问题讨论】:

  • 为什么要自己实现backward pass?
  • @Ivan 因为我想做自己的卷积网络

标签: python pytorch autograd


【解决方案1】:

您的雅可比行列不准确:它是 4d 张量,您只计算了它的 2D 切片。

你忽略了雅可比的第二行:

【讨论】:

    【解决方案2】:

    我们来看一个单列示例,例如:[[x1], [x2], [x3]]

    sum 成为x1 + x2 + x3,然后规范化x 将得到y = [[y1], [y2], [y3]] = [[x1/sum], [x2/sum], [x3/sum]]。您正在寻找 dL/dx1dL/x2dL/x3 - 我们将它们写为:dx1dx2dx3。所有dL/dyi 都一样。

    所以dx1 等于dL/dy1*dy1/dx1 + dL/dy2*dy2/dx1 + dL/dy3*dy3/dx1。这是因为x1 对相应列上的所有输出元素都有贡献:y1y2y3

    我们有:

    • dy1/dx1 = d(x1/sum)/dx1 = (sum - x1)/sum²

    • dy2/dx1 = d(x2/sum)/dx1 = -x2/sum²

    • 同样,dy3/dx1 = d(x3/sum)/dx1 = -x3/sum²

    因此dx1 = (sum - x1)/sum²*dy1 - x2/sum²*dy2 - x3/sum²*dy3dx2dx3 相同。因此,雅可比矩阵是 [dxi]_i = (sum - xi)/sum²[dxi]_j = -xj/sum²(对于所有与 i 不同的 j)。

    在您的实现中,您似乎缺少所有非对角线组件。

    保持相同的单列示例,x1=2x2=3x3=5

    >>> x = torch.tensor([[2.], [3.], [5.]])
    
    >>> sum = input.sum(0)
    tensor([10])
    

    雅可比将是:

    >>> J = (sum*torch.eye(input.size(0)) - input)/sum**2
    tensor([[ 0.0800, -0.0200, -0.0200],
            [-0.0300,  0.0700, -0.0300],
            [-0.0500, -0.0500,  0.0500]])
    

    对于多列的实现,它有点棘手,更具体地说是对角矩阵的形状。将 column 轴保持在最后更容易,因此我们不必为广播而烦恼:

    >>> x = torch.tensor([[2., 1], [3., 3], [5., 5]])
    >>> sum = x.sum(0)
    tensor([10.,  9.])
    
    >>> diag = sum*torch.eye(3).unsqueeze(-1).repeat(1, 1, len(sum))
    tensor([[[10.,  9.],
             [ 0.,  0.],
             [ 0.,  0.]],
    
            [[ 0.,  0.],
             [10.,  9.],
             [ 0.,  0.]],
    
            [[ 0.,  0.],
             [ 0.,  0.],
             [10.,  9.]]])
    

    diag 上方的形状为 (3, 3, 2),其中两个 位于最后一个轴上。注意我们不需要广播sum

    不会做的是:torch.eye(3).unsqueeze(0).repeat(len(sum), 1, 1)。由于使用这种形状 - (2, 3, 3) - 你将不得不使用 sum[:, None, None],并且需要进一步广播......

    雅可比就是:

    >>> J = (diag - x)/sum**2
    tensor([[[ 0.0800,  0.0988],
             [-0.0300, -0.0370],
             [-0.0500, -0.0617]],
    
            [[-0.0200, -0.0123],
             [ 0.0700,  0.0741],
             [-0.0500, -0.0617]],
    
            [[-0.0200, -0.0123],
             [-0.0300, -0.0370],
             [ 0.0500,  0.0494]]])
    

    您可以使用任意的dy 向量通过操作反向传播来检查结果(但不使用torch.ones,您将得到0s,因为J!)。反向传播后,x.grad 应该等于 torch.einsum('abc,bc->ac', J, dy)

    【讨论】:

    • 我看到你做了练习 ;) +1
    • 感谢您的回答!我可以在 (H,W) 张量的情况下实现反向传播
    • 但是,在 (N,H,W) 张量的情况下我遇到了问题...虽然我对批处理中的每个张量 (N) 使用了 for 循环,但发生了错误。我将上传我的代码和图像。你能帮我得到 (N,H,W) 张量情况的正确答案吗?谢谢!
    【解决方案3】:

    第二季度的答案。

    我自己为许多批处理案例实施了反向传播。 我使用了 unsqueeze 功能,它起作用了。

    输入大小:(N,H,W)(N是批量大小)

    forward:
      out = input / torch.sum(input, dim=1).unsqueeze(1)
    
    backward:
      diag = torch.eye(input.size(1),  dtype=torch.double, requires_grad=True).unsqueeze(-1)
      sum = input.sum(1)
      grad_input = ((sum.unsqueeze(1).unsqueeze(1) * diag - input.unsqueeze(1)) * grad_out.unsqueeze(1) / (sum**2).unsqueeze(1).unsqueeze(1)).sum(2)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-18
      • 2020-04-01
      • 2020-10-03
      • 1970-01-01
      • 2023-01-27
      • 2019-07-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多