【PyTorch入門】(二)自動求導(Autograd)

本系列介紹了入門PyTorch所需要了解的內容。本文主要參考文獻:《Deep Learning with PyTorch: A 60 Minute Blitz》(PyTorch深度學習60分鐘快速入門),更新於2019.06.12。

文章目錄

PyTorch中所有神經網絡的核心是autograd包,這個包提供所有在張量上進行自動求導的操作。這個過程是define-by-run的,也就是反向傳播過程由代碼運行方式定義,而且每次迭代都可以不同。

Tensor

torch.Tensor是這個包的重要類別。如果將其屬性.requires_grad設成True,則會跟蹤發生在這個張量上的所有操作。在計算結束後,可以通過調用.backward()進行梯度的自動計算。這個張量的梯度被積累在屬性.grad中。

要停止一個張量參與反向傳播,可以調用.detach()函數使其脫離反向傳播計算過程。

如果要停止反向傳播和內存利用,可以將整個代碼用代碼塊torch.no_grad(),這個對於模型的評估格外有用,因爲模型中可能存在可訓練的參數(requires_grad=True),但是卻不希望計算它們的梯度。

對於反向傳播很重要的另一個類別時Function

TensorFunction之間是相互聯繫的,並且構建了一個非週期圖(acyclic graph),記錄了計算的完整歷史。每個張量都有個.grad_fn屬性來引用創造這個TensorFunction(除了由用戶定義的張量,這些張量的grad_fn是·None`)。

可以通過調用Tensor上的.backward()實現求導。但是如果這個張量是標量(只有一個元素數據),不需要對backward()單獨定義任何變量;但是元素很多的時候,需要制定gradient變量來保證張量的維度保持不變。

import torch

創建一個張量並設定requires_grad=True來跟蹤實施在上面的計算:

x = torch.ones(2,2,requires_grad=True)
print(x)

在這裏插入圖片描述

進行一個張量操作:

y = x + 2
print(y)

在這裏插入圖片描述

y是通過某個操作定義的變量,因此具有grad_fn

print(y.grad_fn)

在這裏插入圖片描述

進一步對y進行操作:

z = y * y * 3
out = z.mean()

print(z.out)

在這裏插入圖片描述
requires_grad_( ... )可以in-place地改變已有張量的requires_grad標誌。如果未指定,輸入標誌默認爲False

a = torch.randn(2,2)
a = ((a* 3) / (a-1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)

在這裏插入圖片描述

Gradients

現在開始反向傳播。由於out包括的是一個標量,out.backward()等同於out.backward(torch.tensor(1.))

out.backward()

顯示導數d(out)/dx:

print(x.grad)

在這裏插入圖片描述

這裏應該得到4.5組成的矩陣。稱out張量爲o,可以得到
o=14izi, zi=3(xi+2)2o=\frac{1}{4}\sum_iz_i,\ z_i=3(x_i+2)^2zixi=1=27z_i\vert_{x_i=1}=27

因此,oxi=32(xi+2)\frac{\partial o}{\partial x_i}=\frac{3}{2}(x_i+2),因此oxixi=1=92=4.5\frac{\partial o}{\partial x_i}\vert_{x_i=1}=\frac{9}{2}=4.5

數學上,如果有一個值爲向量的函數y=f(x)\vec y = f(\vec x)y\vec yx\vec x的梯度是一個雅可比矩陣(Jacobian matrix)【在向量微積分中,雅可比矩陣是一階偏導數以一定方式排列成的矩陣,其行列式稱爲雅可比行列式。】:

J={y1x1y1xnymx1ymxn}J=\left\{\begin{matrix} \frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n} \end{matrix}\right\}

整體而言,torch.autograd是一個用於計算向量-雅克比乘積的機器(an engine for computing vector-Jacobian product)。即給定任意向量v=(v1,v2, ,vm)Tv = (v_1, v_2, \cdots, v_m)^T,計算乘積vTJv^T\cdot J。如果vv恰好是一個標量函數l=g(y)l=g(\vec y)的梯度,即v=(ly1lym)v=\left(\frac{\partial l}{\partial y_1}\cdots\frac{\partial l}{\partial y_m}\right),那麼根據chain rule,vector-Jacobian乘積應該是ll相對於x\vec x的梯度:
JTv=(y1x1ymx1y1xnymxn)(ly1lym)=(lx1lxn)J^T\cdot v=\left(\begin{matrix} \frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_1} \\ \vdots & \ddots & \vdots\\ \frac{\partial y_1}{\partial x_n} & \cdots & \frac{\partial y_m}{\partial x_n} \end{matrix}\right)\left(\begin{matrix} \frac{\partial l}{\partial y_1} \\ \vdots\\ \frac{\partial l}{\partial y_m}\\ \end{matrix}\right)=\left(\begin{matrix} \frac{\partial l}{\partial x_1}\\ \vdots\\ \frac{\partial l}{\partial x_n} \end{matrix}\right)

注意,vTJv^T\cdot J是行向量,通過JTvJ^T\cdot v可以轉換成列向量。

這個vector-Jacobian乘積使得將外部梯度送入一個沒有標量輸出的模型變得非常容易。

現在來具體看一個例子;

x = torch.randn(3,requires_grad=True)
y = x* 2
while y.data.norm() < 1000:
	y = y * 2

print(y)

終端結果:
在這裏插入圖片描述
此時,y已經不是一個標量了,torch.autograd沒有辦法直接計算完整的Jacobian,但是我們其實只需要vector-Jacobian乘積,那麼簡單地將向量以變量的形式反向傳播就可以:

v = torch.tensor(p0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)

終端結果:
在這裏插入圖片描述

也可以通過將代碼塊融入with torch.no_grad():中實現.requires_grad=True下的反向梯度傳播:

print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
	print((x ** 2).requires_grad)

終端結果:
在這裏插入圖片描述
代碼總運行時間3.013秒。

輔助閱讀材料:【文檔學習】Pytorch——torch.autorgrad包

更多內容,歡迎加入星球討論。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章