pytorch系列 --4 pytorch 0.4改動後Variable和Tensor合併問題data和.detach
本文主要講述pytorch0.4更新後相關的程式碼遷移問題
Tensor和Variable合併
torch.Tensor 和torch.autograd.Variable現在是同一個類。torch.Tensor 能夠像之前的Variable一樣追蹤歷史和反向傳播。Variable仍能夠正常工作,但是返回的是Tensor。所以在0.4的程式碼中,不需要使用Variable了。
可以看出使用Variable封裝Tensor之後返回的也是Tensor。
import torch
from torch.autograd import Variable
x_tensor = torch. Tensor(3,4)
x_var = Variable(x_tensor,requires_grad=True)
print(x_var)
out:
tensor([[1.7753e+28, 4.5601e+30, 1.8465e+25, 1.0901e+27],
[1.8289e+34, 7.4103e+28, 1.6443e+19, 4.9172e+33],
[9.2246e-39, 6.8383e-43, 1.4013e-45, 0.0000e+00]], requires_grad=True)
接下來看一下,合併Tensor和Variable之後autograd
是如何實現歷史追蹤和反向傳播的
作為能否autograd
的標籤,requires_grad
現在是Tensor的屬性,所以,只要當一個操作(operation)的任何輸入Tensor
具有requires_grad = True
的屬性,autograd
就可以自動追蹤歷史和反向傳播了。
官方的例項程式碼:
# 預設建立requires_grad = False的Tensor
x = torch.ones(1) # create a tensor with requires_grad=False (default)
x.requires_grad
# out: False
# 建立另一個Tensor,同樣requires_grad = False
y = torch.ones(1) # another tensor with requires_grad=False
# both inputs have requires_grad=False. so does the output
z = x + y
# 因為兩個Tensor x,y,requires_grad=False.都無法實現自動微分,
# 所以操作(operation)z=x+y後的z也是無法自動微分,requires_grad=False
z.requires_grad
# out: False
# then autograd won't track this computation. let's verify!
# 因而無法autograd,程式報錯
z.backward()
# out:程式報錯:RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
# now create a tensor with requires_grad=True
w = torch.ones(1, requires_grad=True)
w.requires_grad
# out: True
# add to the previous result that has require_grad=False
# 因為total的操作中輸入Tensor w的requires_grad=True,因而操作可以進行反向傳播和自動求導。
total = w + z
# the total sum now requires grad!
total.requires_grad
# out: True
# autograd can compute the gradients as well
total.backward()
w.grad
#out: tensor([ 1.])
# and no computation is wasted to compute gradients for x, y and z, which don't require grad
# 由於z,x,y的requires_grad=False,所以並沒有計算三者的梯度
z.grad == x.grad == y.grad == None
# True
那接下來看一下如何在已經建立的tensor上新增requires_grad的屬性呢?
相比於在建立tensor是就設定requires_grad
的值,可以 使用my_tensor=requires_grad_()
實現in-place
的操作來改變requires_grad
的屬性
existing_tensor.requires_grad_()
existing_tensor.requires_grad
# out:True
也可以在建立時就設定屬性值
my_tensor = torch.zeros(3, 4, requires_grad=True)
my_tensor.requires_grad
# out: True
重點介紹一下.data和detach()函式的區別
推薦在經網路網訓練或測試時訪問tensor的資料時使用.detach()方法。
簡單的說就是使用y=x.data屬性來訪問資料時,pytorch不會記錄資料是否改變,此時改變了y的值,意味著也要改變x的值,而在自動求導時會使用更改後的值,這回導致錯誤求導結果。
a = torch.tensor([1,2,3.], requires_grad = True)
out = a.sigmoid()
c = out.data
c.zero_()
#out: tensor([ 0., 0., 0.])
out # out was modified by c.zero_()
#out: tensor([ 0., 0., 0.])
out.sum().backward()
a.grad # The result is very, very wrong because `out` changed!
#out: tensor([ 0., 0., 0.])
而使用y=x.detach()時,如果了y值,也意味著改變了x值,此時呼叫x.backword()會報錯。也就是說.detach()方法會記錄資料的變化狀態。
所以,推薦使用x.detach()來訪問資料,更加安全。
a = torch.tensor([1,2,3.], requires_grad = True)
out = a.sigmoid()
c = out.detach()
c.zero_()
#out: tensor([ 0., 0., 0.])
out # modified by c.zero_() !!
#out: tensor([ 0., 0., 0.])
out.sum().backward() # Requires the original value of out, but that was overwritten by c.zero_()
RuntimeError: one of the variables needed for gradient computation has been modified by an
Tensor的type()方法
type(tensor)
將不在返回tensor的資料型別,返回的是class,使用isinstance()
或tensor.type()
來得到tensor的資料型別.
x = torch.DoubleTensor([1, 1, 1])
print(type(x)) # was torch.DoubleTensor
# out: "<class 'torch.Tensor'>"
print(x.type()) # OK: 'torch.DoubleTensor'
# out: 'torch.DoubleTensor'
print(isinstance(x, torch.DoubleTensor)) # OK: True
# out: True
支援0維向量(標量)
在0.4之前,索引1維的Tensor但會python number, 而索引Variable返回的是size為(1,)的向量;兩者在行為上不一致,同樣的對於一些降維函式,比如Tensor.sum(),返回的也是Python number,而variable.sum()返回的是size為(1,)的向量。
在0.4的版本release中,pytorch支援標量(0-維tensor).可以使用torch.tensor建立標量。而sum(),loss()等返回一個數的降維函式也將返回標量(0-維 tensor)
對於0-維 tensor, .item()方法,返回Python number。
torch.tensor(3.1416) # create a scalar directly
# out: tensor(3.1416)
torch.tensor(3.1416).size() # scalar is 0-dimensional
#out: torch.Size([])
torch.tensor([3]).size() # compare to a vector of size 1
#out: torch.Size([1])
ector = torch.arange(2, 6) # this is a vector
vector
#out: tensor([ 2., 3., 4., 5.])
vector.size()
#out: torch.Size([4])
vector[3] # indexing into a vector gives a scalar
#out: tensor(5.)
vector[3].item() # .item() gives the value as a Python number
#out: 5.0
mysum = torch.tensor([2, 3]).sum() # sum()返回一個數,在此返回標量
mysum
#out: tensor(5)
mysum.size()
#out: torch.Size([])
mysum[0] # 使用此會出現警告
#out: User Warning: invalid index of a 0-dim tensor. This will be an error in PyTorch 0.5. Use tensor.item() to convert a 0-dim tensor to a Python number
losses
在0.4之前,廣泛的loss使用是:total_loss += loss.data[0]
, 這是因為loss是variable,其中size為(1,),而在0.4之後,loss是一個0-維的向量(標量)。可以使用loss.item()從標量中獲得Python number。
注意,當不轉換為Python number來計算loss的累加值時,程式將會有大量的記憶體使用,這是因為total_loss += loss.data[0]
式子的右邊不是Python float而變成了0維tensor。這楊,總損失就會包含loss和梯度歷史,這些梯度歷史會在大的autograd graph中儲存更長時間,帶來大量的記憶體消耗。
上述loss積累的式子可以變為:
total_loss += loss.item()
在本系列的程式碼中,將遵循新版本語言規範。