PyTorch提供的autograd包可以根據輸⼊和前向傳播過程⾃動構建計算圖,並執⾏反向傳播。html
Tensor的幾個重要屬性或方法python
幾個例子具體地解釋一下:函數
import torch x = torch.ones(2, 2, requires_grad=True) print(x) print(x.grad_fn) y = x+2 print(y) print(y.grad_fn) z = y*y*3 out=z.mean() print(z,out)
輸出ui
tensor([[1., 1.], [1., 1.]], requires_grad=True) None tensor([[3., 3.], [3., 3.]], grad_fn=<AddBackward>) <AddBackward object at 0x0000018752434B70> tensor([[27., 27.], [27., 27.]], grad_fn=<MulBackward>) tensor(27., grad_fn=<MeanBackward1>)
y由加法獲得,因此y.grad_fn=
print(x.is_leaf, y.is_leaf) # True False
能夠用.requires_grad_()來用in-place的方式改變requires_grad屬性.code
a = torch.randn(2, 2) # 缺失狀況下默認 requires_grad = False a = ((a * 3) / (a - 1)) print(a.requires_grad) # False a.requires_grad_(True) print(a.requires_grad) # True b = (a * a).sum() print(b.grad_fn)
輸出htm
False True <SumBackward0 object at 0x0000018752434D30>
所計算的梯度都是結果變量關於建立變量的梯度。
好比對:blog
x = torch.ones(2, 2, requires_grad=True) print(x) print(x.grad_fn) y = x+2 print(y) print(y.grad_fn) z = y*3 z.backward(torch.ones_like(z)) print(y.grad) #None print(x.grad)
輸出element
None tensor([[3., 3.], [3., 3.]])
上述代碼至關於建立了一個動態圖,其中x是咱們建立的變量,y和z都是由於x的改變會改變的結果變量. 因此在這個動態圖裏可以求的梯度只有\(\frac{\partial{z}}{\partial{x}}\),\(\frac{\partial{y}}{\partial{x}}\)文檔
爲何l.backward(gradient)須要傳入一個和l一樣形狀的gradient?
對於l.backward()而言,當l是標量時,能夠不傳參,至關於l.backward(torch.tensor(1.))
當l不是標量時,須要傳入一個和l同shape的gradient。
假設 x 通過一番計算獲得 y,那麼 y.backward(w) 求的不是 y 對 x 的導數,而是 l = torch.sum(y*w) 對 x 的導數。w 能夠視爲 y 的各份量的權重,也能夠視爲遙遠的損失函數 l 對 y 的偏導數(這正是函數說明文檔的含義)。特別地,若 y 爲標量,w 取默認值 1.0,纔是按照咱們一般理解的那樣,求 y 對 x 的導數
簡單地說就是,張量對張量無法求導,因此咱們須要人爲地定義一個w,把一個非標量的Tensor經過torch.sum(y*w)的形式轉換成標量。咱們本身定義的這個w的不一樣,固然最後獲得的梯度就不一樣.一般定義爲全1.也就是認爲Tensor y中的每個變量的重要性是等同的.
另外一個角度的理解就是,y是一個tensor,是一個向量,有N個標量,這每個標量都與x有關。對這N個標量咱們須要賦以不一樣的權重,以顯示y中每個標量受到x影響的程度.
好比對
import torch x = torch.ones(2, 2, requires_grad=True) print(x) print(x.grad_fn) y = x+2 print(y) print(y.grad_fn) z = y*3 print(z.shape) w1=torch.Tensor([[1,2],[1,2]]) z.backward([w1]) print(x.grad) x.grad.data.zero_() w2=torch.Tensor([[1,1],[1,1]]) z.backward([w2]) print(x.grad)
輸出
tensor([[1., 1.], [1., 1.]], requires_grad=True) None tensor([[3., 3.], [3., 3.]], grad_fn=<AddBackward>) <AddBackward object at 0x00000187524A6828> torch.Size([2, 2]) tensor([[3., 6.], [3., 6.]]) tensor([[3., 3.], [3., 3.]])
對w1和w2而言,z.backward()之後x.grad是不一樣的。
注意:梯度是累加的,因此第二次計算以前咱們作了清零的操做:x.grad.data.zero_()
能夠參考:
https://zhuanlan.zhihu.com/p/29923090
http://www.javashuo.com/article/p-wgrlmkdc-kd.html
再來看看中斷梯度追蹤的例子:
x = torch.tensor(1.0, requires_grad=True) y1 = x ** 2 with torch.no_grad(): y2 = x ** 3 y3 = y1 + y2 print(x.requires_grad) print(y1, y1.requires_grad) # True print(y2, y2.requires_grad) # False print(y3, y3.requires_grad) # True
輸出:
True tensor(1., grad_fn=<PowBackward0>) True tensor(1.) False tensor(2., grad_fn=<ThAddBackward>) True
反向傳播,求梯度
y3.backward() print(x.grad)
輸出:
tensor(2.)
爲何是2呢?$ y_3 = y_1 + y_2 = x^2 + x^3$,當 \(x=1\) 時 \(\frac {dy_3} {dx}\) 不該該是5嗎?事實上,因爲 \(y_2\) 的定義是被torch.no_grad():
包裹的,因此與 \(y_2\) 有關的梯度是不會回傳的,只有與 \(y_1\) 有關的梯度纔會回傳,即 \(x^2\) 對 \(x\) 的梯度。
上面提到,y2.requires_grad=False
,因此不能調用 y2.backward()
,會報錯:
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
此外,若是咱們想要修改tensor
的數值,可是又不但願被autograd
記錄(即不會影響反向傳播),那麼我麼能夠對tensor.data
進行操做。
x = torch.ones(1,requires_grad=True) print(x.data) # 仍是一個tensor print(x.data.requires_grad) # 可是已是獨立於計算圖以外 y = 2 * x x.data *= 100 # 只改變了值,不會記錄在計算圖,因此不會影響梯度傳播 y.backward() print(x) # 更改data的值也會影響tensor的值 print(x.grad)
輸出:
tensor([1.]) False tensor([100.], requires_grad=True) tensor([2.])