『PyTorch』第五弹_深入理解autograd_上:Variable

Posted 叠加态的猫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了『PyTorch』第五弹_深入理解autograd_上:Variable相关的知识,希望对你有一定的参考价值。

一、Variable类源码简介

class Variable(_C._VariableBase):

    """
    Attributes:
        data: 任意类型的封装好的张量。
        grad: 保存与data类型和位置相匹配的梯度,此属性难以分配并且不能重新分配。
        requires_grad: 标记变量是否已经由一个需要调用到此变量的子图创建的bool值。只能在叶子变量上进行修改。
        volatile: 标记变量是否能在推理模式下应用(如不保存历史记录)的bool值。只能在叶变量上更改。
        is_leaf: 标记变量是否是图叶子(如由用户创建的变量)的bool值.
        grad_fn: Gradient function graph trace.

    Parameters:
        data (any tensor class): 要包装的张量.
        requires_grad (bool): bool型的标记值. **Keyword only.**
        volatile (bool): bool型的标记值. **Keyword only.**
    """

    def backward(self, gradient=None, retain_graph=None, create_graph=None, retain_variables=None):
        """计算关于当前图叶子变量的梯度,图使用链式法则导致分化
        如果Variable是一个标量(例如它包含一个单元素数据),你无需对backward()指定任何参数
        如果变量不是标量(包含多个元素数据的矢量)且需要梯度,函数需要额外的梯度;
        需要指定一个和tensor的形状匹配的grad_output参数(y在指定方向投影对x的导数);
        可以是一个类型和位置相匹配且包含与自身相关的不同函数梯度的张量。
        函数在叶子上累积梯度,调用前需要对该叶子进行清零。

        Arguments:
            grad_variables (Tensor, Variable or None): 
                           变量的梯度,如果是一个张量,除非“create_graph”是True,否则会自动转换成volatile型的变量。
                           可以为标量变量或不需要grad的值指定None值。如果None值可接受,则此参数可选。
            retain_graph (bool, optional): 如果为False,用来计算梯度的图将被释放。
                                           在几乎所有情况下,将此选项设置为True不是必需的,通常可以以更有效的方式解决。
                                           默认值为create_graph的值。
            create_graph (bool, optional): 为True时,会构造一个导数的图,用来计算出更高阶导数结果。
                                           默认为False,除非``gradient``是一个volatile变量。
        """
        torch.autograd.backward(self, gradient, retain_graph, create_graph, retain_variables)


    def register_hook(self, hook):
        """Registers a backward hook.

        每当与variable相关的梯度被计算时调用hook,hook的申明:hook(grad)->Variable or None
        不能对hook的参数进行修改,但可以选择性地返回一个新的梯度以用在`grad`的相应位置。

        函数返回一个handle,其``handle.remove()``方法用于将hook从模块中移除。

        Example:
            >>> v = Variable(torch.Tensor([0, 0, 0]), requires_grad=True)
            >>> h = v.register_hook(lambda grad: grad * 2)  # double the gradient
            >>> v.backward(torch.Tensor([1, 1, 1]))
            >>> v.grad.data
             2
             2
             2
            [torch.FloatTensor of size 3]
            >>> h.remove()  # removes the hook
        """
        if self.volatile:
            raise RuntimeError("cannot register a hook on a volatile variable")
        if not self.requires_grad:
            raise RuntimeError("cannot register a hook on a variable that "
                               "doesn‘t require gradient")
        if self._backward_hooks is None:
            self._backward_hooks = OrderedDict()
            if self.grad_fn is not None:
                self.grad_fn._register_hook_dict(self)
        handle = hooks.RemovableHandle(self._backward_hooks)
        self._backward_hooks[handle.id] = hook
        return handle

    def reinforce(self, reward):
        """Registers a reward obtained as a result of a stochastic process.
        区分随机节点需要为他们提供reward值。如果图表中包含任何的随机操作,都应该在其输出上调用此函数,否则会出现错误。
        Parameters:
            reward(Tensor): 带有每个元素奖赏的张量,必须与Variable数据的设备位置和形状相匹配。
        """
        if not isinstance(self.grad_fn, StochasticFunction):
            raise RuntimeError("reinforce() can be only called on outputs "
                               "of stochastic functions")
        self.grad_fn._reinforce(reward)

    def detach(self):
        """返回一个从当前图分离出来的心变量。
        结果不需要梯度,如果输入是volatile,则输出也是volatile。

        .. 注意::
          返回变量使用与原始变量相同的数据张量,并且可以看到其中任何一个的就地修改,并且可能会触发正确性检查中的错误。
        """
        result = NoGrad()(self)  # this is needed, because it merges version counters
        result._grad_fn = None
        return result

    def detach_(self):
        """从创建它的图中分离出变量并作为该图的一个叶子"""
        self._grad_fn = None
        self.requires_grad = False

    def retain_grad(self):
        """Enables .grad attribute for non-leaf Variables."""
        if self.grad_fn is None:  # no-op for leaves
            return
        if not self.requires_grad:
            raise RuntimeError("can‘t retain_grad on Variable that has requires_grad=False")
        if hasattr(self, ‘retains_grad‘):
            return
        weak_self = weakref.ref(self)

        def retain_grad_hook(grad):
            var = weak_self()
            if var is None:
                return
            if var._grad is None:
                var._grad = grad.clone()
            else:
                var._grad = var._grad + grad

        self.register_hook(retain_grad_hook)
        self.retains_grad = True

 

二、Variable类和计算图

简单的建立一个计算图,便于理解几个相关知识点:

  • requires_grad参数:是否要求导数,默认False,叶节点指定True后,依赖节点都被置为True
  • .backward()方法:根Variable的方法会反向求解叶Variable的梯度
  • .backward()方法grad_variable参数:形状与根Variable一致,非标量Variable反向传播方向指定
  • 叶节点:由用户创建的计算图Variable对象,反向传播后会保留梯度grad数值,其他Variable会清空为None
  • grad_fn属性:指向创建Tensor的Function,如果某一个对象由用户创建,则指向None
import torch as t
from torch.autograd import Variable as V

a = V(t.ones(3,4),requires_grad=True)
b = V(t.zeros(3,4))
c = a.add(b)
d = c.sum()
d.backward()

# 虽然没有要求cd的梯度,但是cd依赖于a,所以a要求求导则cd梯度属性会被默认置为True
print(a.requires_grad, b.requires_grad, c.requires_grad,d.requires_grad)
# 叶节点(由用户创建)的grad_fn指向None
print(a.is_leaf, b.is_leaf, c.is_leaf,d.is_leaf)
# 中间节点虽然要求求梯度,但是由于不是叶节点,其梯度不会保留,所以仍然是None
print(a.grad,b.grad,c.grad,d.grad)
True False True True
True True False False
Variable containing:
 1  1  1  1
 1  1  1  1
 1  1  1  1
[torch.FloatTensor of size 3x4]
 None None None

 

模拟一个简单的反向传播:

def f(x):
    """x^2 * e^x"""
    y = x**2 * t.exp(x)
    return y

def gradf(x):
    """2*x*e^x + x^2*e^x"""
    dx = 2*x*t.exp(x) + x**2*t.exp(x)
    return dx

x = V(t.randn(3,4), requires_grad=True)
y = f(x)
y.backward(t.ones(y.size()))
print(x.grad)
print(gradf(x))
Variable containing:
 -0.3315   3.5068  -0.1079  -0.4308
 -0.1202  -0.4529  -0.1873   0.6514
  0.2343   0.1050   0.1223  15.9192
[torch.FloatTensor of size 3x4]

Variable containing:
 -0.3315   3.5068  -0.1079  -0.4308
 -0.1202  -0.4529  -0.1873   0.6514
  0.2343   0.1050   0.1223  15.9192
[torch.FloatTensor of size 3x4]

 结果一致。

 

以上是关于『PyTorch』第五弹_深入理解autograd_上:Variable的主要内容,如果未能解决你的问题,请参考以下文章

『PyTorch』第五弹_深入理解autograd_下:Variable梯度探究

『PyTorch』第五弹_深入理解Tensor对象_中上:索引

『PyTorch』第五弹_深入理解Tensor对象_上:初始化以及尺寸调整

『PyTorch』第五弹_深入理解Tensor对象_下:从内存看Tensor

『PyTorch』第五弹_深入理解Tensor对象_中下:数学计算以及numpy比较

C语言期末第五弹