1. 程式人生 > >『PyTorch』第五彈_深入理解autograd_上:Variable

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

ogr runt 無需 nbsp 移除 數值 port auto hasattr

一、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