本文將介紹Pytorch的以下內容
自動微分函數
優化
模型保存和載入
好了,我們首先介紹一下關于微分的內容。
在訓練神經網絡時,最常用的算法是反向傳播算法。在該算法中,根據損失函數相對于給定參數的梯度來調整參數(模型權重)。
為了計算這些梯度,PyTorch有一個內置的微分引擎,名為torch.autograd。它支持任何計算圖的梯度自動計算。
考慮最簡單的單層神經網絡,輸入x,參數w和b,以及一些損失函數。它可以在PyTorch中以以下方式定義:
import torchx = torch.ones(5) # input tensor
y = torch.zeros(3) # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
張量、函數與計算圖
這段代碼定義了以下計算圖:
在這個網絡中,w和b是我們需要優化的參數。因此,我們需要能夠計算損失函數相對于這些變量的梯度。為了做到這一點,我們設置了這些張量的requires_grad屬性。
我們應用于張量來構造計算圖的函數實際上是函數類的對象。該對象知道如何在正向方向上計算函數,以及如何在反向傳播步驟中計算其導數。對反向傳播函數的引用存儲在張量的grad_fn屬性中。您可以在文檔中找到Function的更多信息。
print(f"Gradient function for z = {z.grad_fn}")
print(f"Gradient function for loss = {loss.grad_fn}")
輸出為:
Gradient function for z = <AddBackward0 object at 0x0000022EDB445C30>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward0 object at 0x0000022EDB445D20>
計算梯度
為了優化神經網絡中參數的權重,我們需要計算損失函數對參數的導數,即我們需要?loss/?w和?loss/?B。為了計算這些導數,我們調用loss.backward(),然后從w.g grad和b.g grad中檢索值:
loss.backward()
print(w.grad)
print(b.grad)
輸出為:
tensor([[0.0549, 0.1796, 0.0399],[0.0549, 0.1796, 0.0399],[0.0549, 0.1796, 0.0399],[0.0549, 0.1796, 0.0399],[0.0549, 0.1796, 0.0399]])
tensor([0.0549, 0.1796, 0.0399])
禁用梯度跟蹤
默認情況下,所有requires_grad=True的張量都在跟蹤它們的計算歷史并支持梯度計算。然而,在某些情況下,我們不需要這樣做,例如,當我們訓練了模型,只想將其應用于一些輸入數據時,即我們只想通過網絡進行前向計算。我們可以通過使用torch.no_grad()塊包圍我們的計算代碼來停止跟蹤計算:
z = torch.matmul(x, w)+b
print(z.requires_grad)with torch.no_grad():z = torch.matmul(x, w)+b
print(z.requires_grad)
輸出為:
True
False
實現相同結果的另一種方法是在張量上使用detach()方法:
z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)
輸出為:
False
你可能想要禁用漸變跟蹤的原因如下:
-
將神經網絡中的一些參數標記為凍結參數。
-
當你只做正向傳遞時,為了加快計算速度,因為在不跟蹤梯度的張量上的計算會更有效率。
更多關于計算圖的知識
從概念上講,autograd在由Function對象組成的有向無環圖(DAG)中保存數據(張量)和所有執行的操作(以及產生的新張量)的記錄。在DAG中,葉是輸入張量,根是輸出張量。通過從根到葉的跟蹤圖,您可以使用鏈式法則自動計算梯度。
在向前傳遞中,autograd同時做兩件事:
-
運行請求的操作來計算結果張量
-
在DAG中維持操作的梯度函數。
當在DAG根上調用.backward()時,向后傳遞開始。autograd:
-
計算每個。grad_fn的梯度,
-
在各自張量的.grad屬性中累積它們
-
利用鏈式法則,一直傳播到葉張量。
[!TIP]
PyTorch中的dag是動態的,需要注意的重要一點是圖形是從頭開始重新創建的;在每次.backward()調用之后,autograd開始填充一個新圖。這正是允許您在模型中使用控制流語句的原因;如果需要,您可以在每次迭代中更改形狀、大小和操作
張量梯度和雅可比積
在很多情況下,我們有一個標量損失函數,我們需要計算關于一些參數的梯度。然而,在某些情況下,輸出函數是一個任意張量。在這種情況下,PyTorch允許你計算所謂的雅可比積,而不是實際的梯度。
inp = torch.eye(4, 5, requires_grad=True)
out = (inp+1).pow(2).t()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"First call\n{inp.grad}")
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nSecond call\n{inp.grad}")
inp.grad.zero_()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nCall after zeroing gradients\n{inp.grad}")
輸出為:
First call
tensor([[4., 2., 2., 2., 2.],[2., 4., 2., 2., 2.],[2., 2., 4., 2., 2.],[2., 2., 2., 4., 2.]])Second call
tensor([[8., 4., 4., 4., 4.],[4., 8., 4., 4., 4.],[4., 4., 8., 4., 4.],[4., 4., 4., 8., 4.]])Call after zeroing gradients
tensor([[4., 2., 2., 2.,