天天看點

pytorch系列 --3 Variable,Tensor 和 Gradient

Variable & Automatic Gradient Calculation

  • Tensor vs Variable
  • graph and gradient

    注意,在pytorch0.4中,tensor和pytorch合并了。

    https://pytorch.org/blog/pytorch-0_4_0-migration-guide/

    torch.Tensor 和 torch.autograd.Variable 是同一個類,更準确的說,torch.Tensor 也能像舊的Variable一樣

    追蹤曆史,進行梯度計算等。Variable 仍然正常工作,但是傳回的對象是torch.Tensor,這意味着不需要在代碼中使用Variable(Tensor)了。

但是此篇文章主要講述Variable的相關知識,将在下一篇在文章中講述pytorch0.4的具體變化

引進庫

import torch
from torch.autograd import Variable
           

Tensor 和 Variable

Variable:

pytorch系列 --3 Variable,Tensor 和 Gradient

autograde.Variable是一個類,在使用

tensor_variable = autograde.Variable(tensor)

後,

tensor_variable 有三個重要的屬性:

  • data:儲存着tensor_variable 的tensor向量
  • grad: 儲存tensor_variable 的梯度值
  • grad_fn: 儲存建立tensor_variable 的函數

    此外,還有一個屬性

    requires_grad

    确定tensor_variable 是否具有求導的功能。

    以上Variable就是pytorch實作反向傳播最核心的先決條件。

    接下裡來看一下Variable的具體功能:

1) 聲明 declaration

x_tensor = torch.Tensor(3,4)
x_tensor
           

out:

-12170.8672 0.0000 -12170.8672 0.0000

0.0000 0.0000 0.0000 0.0000

-12170.9453 0.0000 -12170.9453 0.0000

[torch.FloatTensor of size 3x4]

使用Variale轉換Tensor

x_variable = Variable(x_tensor)
x_variable
           

out:

12170.8672 0.0000 -12170.8672 0.0000

0.0000 0.0000 0.0000 0.0000

-12170.9453 0.0000 -12170.9453 0.0000

[torch.FloatTensor of size 3x4]

2)Variable的屬性

.data :Variable的Tensor資料

# .data -> wrapped tensor 
x_variable.data
           

out:

-12170.8672 0.0000 -12170.8672 0.0000

0.0000 0.0000 0.0000 0.0000

-12170.9453 0.0000 -12170.9453 0.0000

[torch.FloatTensor of size 3x4]

.grad: variable的梯度值

# .grad -> gradient of the variable
print(x_variable.grad)
           

out:

None

.reqires_grad: variable是否能進行梯度計算

# .requires_grad -> whether variable requres gradient
print(x_variable.requires_grad)

x_variable = Variable(x_tensor,requires_grad=True)
x_variable.requires_grad
           

.grad_fn: variable的建立函數

y = x_variable * 2

print(y.grad_fn)
           

out:

<MulBackward object at 0x0000012641CF6F98>

3. Graph & Variables

先看一個簡單的計算圖的例子:

可以簡單的計算出w的梯度是x,也就是2,其餘x,b的同樣的可以簡單計算出。

w = Variable(torch.Tensor([1]), requires_grad=True)
x = Variable(torch.Tensor([2]), requires_grad=True)
b = Variable(torch.Tensor([3]), requires_grad=True)
y=w*x+b  # y=1*x +2

y.backward()
print(w.grad, x.grad, b.grad)
           

out:

2 1 1

接下來,将x的

requires_grad=True

改為

requires_grad=False

,此時求x的grad:

可以發現x的grad是None,也就是沒有計算保留x的梯度

w = Variable(torch.Tensor([1]), requires_grad=True)
x = Variable(torch.Tensor([2]),  requires_grad=False)
b = Variable(torch.Tensor([3]), requires_grad=True)
y=w*x+b  # y=1*x +2

y.backward()
print(x.grad)
           

out:

2 None 1

如果求梯度的值是一個向量張量,而非标量張量呢?假設對于

z

是一個長度為4的一維張量。那要求z,4個次元上的梯度值了,這個概念在數學中并不陌生,比如y是多元輸出,那麼要在y每一個次元上求梯度值。此時要使用gradients參數,要求gradients的size和y的size相同.同時:

g r a d i e n t s 每 一 維 度 上 的 值 ∗ y 每 一 維 度 上 的 梯 度 值 為 最 終 參 數 中 g r a d 保 留 的 值 gradients每一次元上的值 *y每一次元上的梯度值 為 最終參數中grad保留的值 gradients每一次元上的值∗y每一次元上的梯度值為最終參數中grad保留的值

下面通過例子看一下:

看一下輸出:

第2行隻計算了z第一個值得梯度,第3行隻計算了z第2個值得梯度,

而第4行計算了z所有是個值得輸出,第5行将第四行的輸出在乘以gradients的每一個值得到最終的梯度。

x = Variable(torch.FloatTensor([1, 2, 3, 4]), requires_grad=True)
z = 2*x
print(z, z.size())
# do backward for first element of z
z.backward(torch.FloatTensor([1, 0, 0, 0]))
print(x.grad.data)
x.grad.data.zero_() #remove gradient in x.grad, or it will be accumulated

# do backward for second element of z
z.backward(torch.FloatTensor([0, 1, 0, 0]))
print(x.grad.data)
x.grad.data.zero_()

# do backward for all elements of z, with weight equal to the derivative of
# loss w.r.t z_1, z_2, z_3 and z_4
z.backward(torch.FloatTensor([1, 1, 1, 1]))
print(x.grad.data)
x.grad.data.zero_()

z.backward(torch.FloatTensor([1, 0.1, 0.01, 0.001]))
print(x.grad.data)
           

out:

tensor([2., 4., 6., 8.], grad_fn=) torch.Size([4])

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

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

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

tensor([2.0000, 0.2000, 0.0200, 0.0020])

對于神經網絡而言,最後的loss都是一個标量形式,是以不需要傳入gradients,采用标量形式的梯度計算即可。

from torch.autograd import Variable
import torch
x = Variable(torch.FloatTensor([[1, 2, 3, 4]]), requires_grad=True)
z = 2*x
loss = z.sum(dim=1)

# do backward for first element of z
z.backward(torch.FloatTensor([[1, 0, 0, 0]]))
print(x.grad.data)
x.grad.data.zero_() #remove gradient in x.grad, or it will be accumulated

# do backward for second element of z
z.backward(torch.FloatTensor([[0, 1, 0, 0]]))
print(x.grad.data)
x.grad.data.zero_()

# do backward for all elements of z, with weight equal to the derivative of
# loss w.r.t z_1, z_2, z_3 and z_4
z.backward(torch.FloatTensor([[1, 1, 1, 1]]))
print(x.grad.data)
x.grad.data.zero_()

# or we can directly backprop using loss
loss.backward() # equivalent to loss.backward(torch.FloatTensor([1.0]))
print(x.grad.data)
           

代碼最後一行的輸出為:

out:

2 2 2 2

[torch.FloatTensor of size 1x4]

可以看出使用loss可以達到與調用z.backward(gradients)相同的作用。該問題參考stackoverflowhttps://stackoverflow.com/questions/43451125/pytorch-what-are-the-gradient-arguments

繼續閱讀