自動求導 & Variable對象(torch.autograde.Variable)


前言:

def to_var(self, x, volatile=False): #torch.Size([1024, 118])
    if torch.cuda.is_available():
        x = x.cuda()
    return Variable(x, volatile=volatile)

 class torch.autograd.Variable:為什么要引入Variable?首先回答為什么引入Tensor。僅僅使用numpy也可以實現前向反向傳播,但numpy不支持GPU運算。而PytorchTensor提供多種操作運算,此外Tensor支持GPU。問來了,兩三個網絡可以推公式寫反向傳播,當網絡很復雜的時需要自動化。autograd可以幫助我們,當利用autograd時,前向傳播會定義一個計算圖,圖中的節點就是Tensor。圖中的變就是函數。當我們將Tensor(Variable(tensor,....))Variable時,Variable就變成了節點。若x為一個Variable,那么x.data即為Tensorx.grad也為一個Variable。那么x.grad.data就為梯度的值。總結:Pytorch Variable與Pytorch Tensor有着相同的API,Tensor上的所有操作幾乎都可用在Variable上。兩者不同之處在於利用Variable定義一個計算圖,可以實現自動求導

  重要屬性如下:

  requires_grad:

    指定要不要更新這個參數(通過梯度(迭代)來更新),對於不需要更新的參數,可以把它設定為False,可以加快運算。Variable默認是不需要求導的,即requires_grad屬性默認為False,如果某一個節點requires_grad被設為True,那么所有依賴它的節點requires_grad都為True。

    用戶在手動定義Variable時,參數requires默認值是False。而在Module中的層在定義時,相關的Variable的requires_grad參數默認是True。在計算圖中,如果有一個輸入的requires_grad是True,那么輸出的requires_grad也是True。只有在所有輸入的requires_grad都為False時,輸出的requires_grad才為False。

 Valatile:

    指定需不需要保留記錄用的參數。指定參數為True代表不需要記錄(即還要接着更新),可以加快運算。如果有一個參數的volatile是True,則它的requires_grad一定是False。簡單來說,對於需要更新的Variable記得將requires_grad設成True,當只需要得到結果而不需要更新的Variable可以將volatile設成True加快運算速度。

    Variable的volatile屬性默認為False,如果某一個Variable的volatile屬性被設為True,那么所有依賴它的節點的volatile屬性都為True。volatile屬性為True的節點不會求導,volatile的優先級比requires_grad的高。

    當有一個輸入的volatile=True時,那么輸出的volatile=True。volatile=True推薦在模型推理過程(測試)中使用,這時只需要令輸入的volatile=True,保證用最小的內存來執行推理,不會保存任何中間狀態。在使用volatile=True的時候,變量是不存儲creator屬性的,這樣減少內存的使用。

 torch.autograde.VariableAutograde的核心類,它封裝了Tensor,並整合了反向傳播相關實現

 Variable包含三個屬性:

  data:存儲了Tensor,是本體數據

  grad:保存了data的梯度,是個Variable而非Tensor,與data形狀一致

  grad_fn:指向Function對象,用於反向傳播的梯度計算之用

1、data

import torch
from torch.autograd import Variable

x = Variable(torch.ones(2, 2), requires_grad=True)
print(x)
print("---------")
print(x.data)
輸出:
tensor([[ 1.,  1.],
        [ 1.,  1.]])
---------
tensor([[ 1.,  1.],
        [ 1.,  1.]])

2、梯度求解

 構建一個簡單的方程:y=x[0,0] + x[0,1]+ x[1,0]+ x[1,1],variable的運算結果也是Variable,但是,中間結果(Variable類型)反向傳播中不會被求導

 這和Tensorflow不太一致,Tensorflow中中間運算結果的數據結構是Tensor

y=x.sum()
print(y)#tensor(4.)

 可以查看目標函數的.grad_fn方法,它用來求梯度,

y.grad_fn  # grad_fn:指向Function對象,用於反向傳播的梯度計算之用
print(y.grad_fn)  # <SumBackward0 object at 0x00000000021CB908>
y.backward()  # 反向傳播
x.grad  # Variable的梯度保存在Variable.grad中
print(x.grad)
#tensor([[ 1.,  1.],
        [ 1.,  1.]])

 grad屬性保存在Variable中,新的梯度下來會進行累加,可以看到再次求導結果會變成了2

y.backward()
x.grad  # 可以看到變量梯度是累加的
print(x.grad)

#tensor([[ 2.,  2.],
        [ 2.,  2.]])

 所以要有歸0的操作:

print(x.grad.data.zero_())
#tensor([[ 0.,  0.],
        [ 0.,  0.]])

 匯總:

 1 x = Variable(torch.ones(2, 2), requires_grad=True)
 2 print(x)  # 1
 3 print("---------")
 4 print(x.data)  # 1
 5 y = x.sum()
 6 print(y)  # tensor(4.)
 7 
 8 y.backward()  # 反向傳播
 9 x.grad  # Variable的梯度保存在Variable.grad中
10 print(x.grad)  # 1
11 
12 y.backward()
13 x.grad  # 可以看到變量梯度是累加的
14 print(x.grad)  # 2
15 
16 x.grad.data.zero_()
17 print(x.grad)  # 0

 對比Variable和Tensor的接口,相差無兩:

import torch
from torch.autograd import Variable

# Variable和Tensor的接口近乎一致,可以無縫切換
x = Variable(torch.ones(2, 2))
y = torch.cos(x)  # 傳入Variable
x_tensor_cos = torch.cos(x.data)  # 傳入Tensor

print(y)
print(x_tensor_cos)
#tensor([[ 0.5403,  0.5403],
        [ 0.5403,  0.5403]])
tensor([[ 0.5403,  0.5403],
        [ 0.5403,  0.5403]])

 

 3、自動求導

 torch.qutograd包提供Tensor所有操作的自動求導。

 3.1、數據結構介紹

  autograd.Variable是這個包最核心的類。它包裝一個Tensor,並且幾乎支持所有的定義在其上的操作。一旦完成你的運算,你可以調用.backward()來自動計算出所有的梯度,Variable有三個屬性:

   訪問原始的tensor使用屬性.data

  關於這一Variable的梯度則集中於.grad

  .creator反映了創建者,標識了是否由用戶使用.Variable直接創建(None)。

'''求導數'''
x = Variable(torch.ones(2, 2), requires_grad=True)
y = x + 2
# AttributeError: 'Tensor' object has no attribute 'creator'
# print(x.creator)      #None,用戶直接創建沒有creater屬性
print(y.creator)  # <torch.autograd._functions.basic_ops.AddConstant object at 0x7fb9b4d4b208>

  3.2、求導運算

  如果你想要進行求導計算,你可以在Variable上調用.backward()

  •   如果Variable是一個標量(例如它包含一個單元素數據),你無需對backward指定任何參數
'''求導數'''
x = Variable(torch.ones(2, 2), requires_grad=True)
y = x + 2
z = y*y*3
o = z.mean()

o.backward()

print(x)
print(y)
print(z)
print(x.grad)          # 輸出對o對x求倒結果
print(y.grad)          # y不是自動求導變量requires_grad沒設為True
#tensor([[ 1.,  1.],
        [ 1.,  1.]])
tensor([[ 3.,  3.],
        [ 3.,  3.]])
tensor([[ 27.,  27.],
        [ 27.,  27.]])
tensor([[ 4.5000,  4.5000],
        [ 4.5000,  4.5000]])
None

Process finished with exit code 0

    

  •   如果它有更多的元素(矢量),你需要指定一個和tensor的形狀匹配的grad_output參數(y在指定方向投影對x的導數)
'''求導數'''
x = torch.randn(3)
x = Variable(x, requires_grad=True)
y = x * 2
gradients = torch.FloatTensor([0.5, 0.5, 1])

y.backward(gradients)  # 沿着某方向的梯度
print(x.grad)
#tensor([ 1.,  1.,  2.])

參考:

參考1:『PyTorch』第三彈重置_Variable對象

參考2:『PyTorch』第三彈_自動求導

參考3:Pytorch細節記錄

參考4:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM