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:
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