【问题标题】:Gradient is none in pytorch when it shouldn'tpytorch中不应该有梯度
【发布时间】:2019-04-29 15:31:12
【问题描述】:

我正在尝试使用 pytorch 获取/跟踪变量的梯度,其中我有该变量,将其传递给第一个函数,该函数查找某个其他变量的某个最小值,然后输入第一个函数的输出到第二个功能,整个事情重复多次。

这是我的代码:

import torch

def myFirstFunction(parameter_current_here):
    optimalValue = 100000000000000
    Optimal = 100000000000000
    for j in range(2, 10):
        i = torch.ones(1, requires_grad=True)*j
        with torch.enable_grad():
            optimalValueNow = i*parameter_current_here.sum()
        if (optimalValueNow < optimalValue):
            optimalValue = optimalValueNow
            Optimal = i
    return optimalValue, Optimal

def mySecondFunction(Current):
    with torch.enable_grad():
        y = (20*Current)/2 + (Current**2)/10
    return y

counter = 0
while counter < 5:
    parameter_current = torch.randn(2, 2, requires_grad=True)

    outputMyFirstFunction = myFirstFunction(parameter_current)
    outputmySecondFunction = mySecondFunction(outputMyFirstFunction[1])
    outputmySecondFunction.backward()

    print("outputMyFirstFunction after backward:",
               outputMyFirstFunction)
    print("outputmySecondFunction after backward:",
               outputmySecondFunction)
    print("parameter_current Gradient after backward:",
               parameter_current.grad)

    counter = counter + 1

parameter_current.grad 对于所有迭代来说都是 none,而它显然不应该是 none。我究竟做错了什么?我该如何解决?

非常感谢您对此提供的帮助。非常感谢!

阿里

【问题讨论】:

    标签: python python-3.x pytorch gradient


    【解决方案1】:

    我也有类似的经历。参考:https://pytorch.org/docs/stable/tensors.html

    • 对于 requires_grad 为 True 的张量,如果它们是由用户创建的,它们将是叶张量。这意味着它们不是操作的结果,因此 grad_fn 为 None。
    • 只有叶张量会在调用backward() 期间填充其梯度。要为非叶张量填充 grad,可以使用 retain_grad()。 示例:
        >>> a = torch.tensor([[1,1],[2,2]], dtype=torch.float, requires_grad=True)
        >>> a.is_leaf
        True
        >>> b = a * a
        >>> b.is_leaf
        False
        >>> c = b.mean()
        >>> c.backward()
        >>> print(c.grad)
        None
        >>> print(b.grad)
        None
        >>> print(a.grad)
        tensor([[0.5000, 0.5000],
                [1.0000, 1.0000]])
        >>> b = a * a
        >>> c = b.mean()
        >>> b.retain_grad()
        >>> c.retain_grad()
        >>> c.backward()
        >>> print(a.grad)
        tensor([[1., 1.],
                [2., 2.]])
        >>> print(b.grad)
        tensor([[0.2500, 0.2500],
                [0.2500, 0.2500]])
        >>> print(c.grad)
        tensor(1.)
    

    【讨论】:

      【解决方案2】:

      我猜问题出在with torch.enable_grad(): 语句上。退出with 语句后,torch.enable_grad() 不再适用,函数运行后torch 将清除毕业生。

      【讨论】:

      • 不,这不是该语句的行为。 torch.enable_grad() 在上述给定上下文中无效。请查看文档:“在no_grad 上下文中启用梯度计算。这在no_grad 之外无效。” pytorch.org/docs/stable/…
      【解决方案3】:

      因为除了计算parameter_current 的梯度之外,我并不清楚你真正想要归档什么, 我只是专注于描述它为什么不起作用以及你可以做些什么来准确计算梯度。

      我在代码中添加了一些 cmets 以更清楚地说明问题所在。

      但简而言之,问题是您的parameter_current 不是您的损失 计算的一部分。您调用 backward() 的张量是 outputmySecondFunction

      所以目前你只计算i 的梯度,因为你已经为它设置了requires_grad=True

      详情请查看cmets:

      import torch
      
      def myFirstFunction(parameter_current_here):
          # I removed some stuff to reduce it to the core features
          # removed torch.enable_grad(), since it is enabled by default
          # removed Optimal=100000000000000 and Optimal=i, they are not used
          optimalValue=100000000000000
          for j in range(2,10):
              # Are you sure you want to compute gradients this tensor i? 
              # Because this is actually what requires_grad=True does.
              # Just as a side note, this isn't your problem, but affects performance of the model.
              i= torch.ones(1,requires_grad=True)*j
              optimalValueNow=i*parameter_current_here.sum()
              if (optimalValueNow<optimalValue):
                  optimalValue=optimalValueNow
      
          # Part Problem 1:
          # optimalValueNow is multiplied with your parameter_current
          # i is just your parameter i, nothing else
          # lets jump now the output below in the loop: outputMyFirstFunction
          return optimalValueNow,i
      
      def mySecondFunction(Current):
          y=(20*Current)/2 + (Current**2)/10
          return y
      
      counter=0
      while counter<5:
          parameter_current = torch.randn(2, 2,requires_grad=True)
      
          # Part Problem 2:
          # this is a tuple (optimalValueNow,i) like described above
          outputMyFirstFunction=myFirstFunction(parameter_current)
          # now you are taking i as an input
          # and i is just torch.ones(1,requires_grad=True)*j
          # it as no connection to parameter_current
          # thus nothing is optimized
          outputmySecondFunction=mySecondFunction(outputMyFirstFunction[1])
      
          # calculating gradients, since parameter_current is not part of the computation 
          # no gradients will be computed, you only get gradients for i
          # Btw. if you would not have set requires_grad=True for i, you actually would get an error message
          # for calling backward on this
          outputmySecondFunction.backward()
      
          print("outputMyFirstFunction after backward:",outputMyFirstFunction)
          print("outputmySecondFunction after backward:",outputmySecondFunction)
          print("parameter_current Gradient after backward:",parameter_current.grad)
      
          counter=counter+1
      

      因此,如果您想计算 parameter_current 的梯度,您只需确保它是计算的一部分 在你调用backward() 的张量中,你可以这样做,例如通过改变:

      outputmySecondFunction=mySecondFunction(outputMyFirstFunction[1])
      

      到:

      outputmySecondFunction=mySecondFunction(outputMyFirstFunction[0])
      

      会有这个效果,只要你改变它,你就会得到parameter_current的渐变!

      希望对你有帮助!



      完整的工作代码:

      import torch
      
      def myFirstFunction(parameter_current_here):
          optimalValue=100000000000000
          for j in range(2,10):
              i= torch.ones(1,requires_grad=True)*j
              optimalValueNow=i*parameter_current_here.sum()
              if (optimalValueNow<optimalValue):
                  optimalValue=optimalValueNow
      
          return optimalValueNow,i
      
      def mySecondFunction(Current):
          y=(20*Current)/2 + (Current**2)/10
          return y
      
      counter=0
      while counter<5:
          parameter_current = torch.randn(2, 2,requires_grad=True)
          outputMyFirstFunction=myFirstFunction(parameter_current)
          outputmySecondFunction=mySecondFunction(outputMyFirstFunction[0]) # changed line
          outputmySecondFunction.backward()
      
          print("outputMyFirstFunction after backward:",outputMyFirstFunction)
          print("outputmySecondFunction after backward:",outputmySecondFunction)
          print("parameter_current Gradient after backward:",parameter_current.grad)
      
          counter=counter+1
      

      输出:

      outputMyFirstFunction after backward: (tensor([ 1.0394]), tensor([ 9.]))
      outputmySecondFunction after backward: tensor([ 10.5021])
      parameter_current Gradient after backward: tensor([[ 91.8709,  91.8709],
              [ 91.8709,  91.8709]])
      outputMyFirstFunction after backward: (tensor([ 13.1481]), tensor([ 9.]))
      outputmySecondFunction after backward: tensor([ 148.7688])
      parameter_current Gradient after backward: tensor([[ 113.6667,  113.6667],
              [ 113.6667,  113.6667]])
      outputMyFirstFunction after backward: (tensor([ 5.7205]), tensor([ 9.]))
      outputmySecondFunction after backward: tensor([ 60.4772])
      parameter_current Gradient after backward: tensor([[ 100.2969,  100.2969],
              [ 100.2969,  100.2969]])
      outputMyFirstFunction after backward: (tensor([-13.9846]), tensor([ 9.]))
      outputmySecondFunction after backward: tensor([-120.2888])
      parameter_current Gradient after backward: tensor([[ 64.8278,  64.8278],
              [ 64.8278,  64.8278]])
      outputMyFirstFunction after backward: (tensor([-10.5533]), tensor([ 9.]))
      outputmySecondFunction after backward: tensor([-94.3959])
      parameter_current Gradient after backward: tensor([[ 71.0040,  71.0040],
              [ 71.0040,  71.0040]])
      

      【讨论】:

      • 非常感谢您。但问题是您将此行中的 1 更改为 0:outputmySecondFunction=mySecondFunction(outputMyFirstFunction[1]),而对于我的逻辑,它应该是 1(第二个函数的输入是第一个函数的第二个输出不是第一个)。此外, i 与 parameter_current 隐式关联,因为 optimizeValueNow 是从 parameter_current 显式计算的,然后 i 使用一些 if 条件(您从我的第一个函数中省略的部分)基于 optimizeValueNow 更新。你能根据这一切更新你的代码/解释吗?
      • 哦,实际上,我在返回第一个函数时犯了一个错误,现在我更正了。所以,函数的正确输出应该是最优值,而不是我之前的最优值。因此,如您所见,我们正在更新 i 和 Optimal(这是我们需要传递给第二个函数的内容)是更新后的 i,它隐式依赖于 parameter_current,正如我在上面的评论中解释的那样。再次,我们将非常感谢您提供进一步的意见!
      • @Aly 为什么将requires_grad=True 设置为i?这是一个局部变量,每次调用该函数时都会创建一个新的i。所以在这个给定的设置中你不能优化它。
      • 你更新的程序有同样的问题Optimal = i = torch.ones(1,requires_grad=True)*j,你正在对它做一些进一步的计算,最后在结果上调用损失。在此计算中没有与parameter_current 的连接。
      • "i 隐式绑定到 parameter_current" 这不是因为您在与optimal 相关的图形分支上调用backward。现在正在这样做的方式没有错误。 parameter_current 只是不涉及您调用backward 的图形部分。更改parameter_currentoutputmySecondFunction 的结果只是没有影响,请再次检查我的代码中的cmets。你必须经历它。您有两个选择,要么像我建议的那样将其从 1 更改为 0,要么更改您的计算,以便:-> 下一条评论
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-23
      • 2021-04-03
      • 2020-02-10
      • 1970-01-01
      相关资源
      最近更新 更多