注意
點選 這裡以下載完整的範例程式碼
學習基礎知識 || 快速入門 || 張量 || 資料集和資料載入器 || 轉換 || 建立模型 || 自動微分 || 最佳化 || 儲存和載入模型
使用 torch.autograd
進行自動微分¶
在訓練神經網路時,最常使用的演算法是**反向傳播**。在這個演算法中,參數(模型權重)會根據損失函數相對於給定參數的**梯度**進行調整。
為了計算這些梯度,PyTorch 有一個稱為 torch.autograd
的內建微分引擎。它支援針對任何計算圖自動計算梯度。
考慮最簡單的單層神經網路,輸入為 x
,參數為 w
和 b
,以及一些損失函數。它可以在 PyTorch 中以以下方式定義
張量、函數和計算圖¶
這段程式碼定義了以下**計算圖**

在這個網路中,w
和 b
是我們需要最佳化的**參數**。因此,我們需要能夠計算損失函數相對於這些變數的梯度。為此,我們設定這些張量的 requires_grad
屬性。
注意
您可以在建立張量時設定 requires_grad
的值,也可以稍後使用 x.requires_grad_(True)
方法設定。
我們應用於張量以建構計算圖的函數實際上是 Function
類別的物件。這個物件知道如何在*正向*方向計算函數,也知道在*反向傳播*步驟中如何計算其導數。反向傳播函數的參照儲存在張量的 grad_fn
屬性中。您可以在文件中找到更多關於 Function
的資訊。
計算梯度¶
為了最佳化神經網路中參數的權重,我們需要計算損失函數相對於參數的導數,也就是說,在 x
和 y
的某些固定值下,我們需要 \(\frac{\partial loss}{\partial w}\) 和 \(\frac{\partial loss}{\partial b}\)。為了計算這些導數,我們呼叫 loss.backward()
,然後從 w.grad
和 b.grad
中取得值。
注意
- 我們只能取得計算圖中葉節點的
grad
屬性,這些節點的requires_grad
屬性設定為True
。對於圖表中的所有其他節點,將無法使用梯度。 - 出於效能原因,我們只能在給定圖表上使用
backward
進行一次梯度計算。如果需要在同一個圖表上進行多次backward
呼叫,則需要將retain_graph=True
傳遞給backward
呼叫。
停用梯度追蹤¶
根據預設,所有具有 requires_grad=True
的張量都會追蹤其計算歷史記錄並支援梯度計算。但是,在某些情況下,我們不需要這樣做,例如,當我們已經訓練了模型,並且只想將其應用於一些輸入資料時,也就是說,我們只想透過網路進行*正向*計算。我們可以透過使用 torch.no_grad()
區塊包圍我們的計算程式碼來停止追蹤計算
另一種達到相同結果的方法是在張量上使用 detach()
方法
- 您可能想要停用梯度追蹤的原因有很多
- 將神經網路中的某些參數標記為**凍結參數**。這是微調預先訓練的網路的常見情況。
- 當您只進行正向傳遞時,可以**加快計算速度**,因為在不追蹤梯度的張量上進行計算會更有效率。
更多關於計算圖的資訊¶
從概念上講,autograd 會將資料(張量)和所有執行的運算(以及產生的新張量)的記錄保存在由 Function 物件組成的有向無環圖 (DAG) 中。在這個 DAG 中,葉節點是輸入張量,根節點是輸出張量。透過從根節點到葉節點追蹤這個圖表,您可以使用鏈式法則自動計算梯度。
在正向傳遞中,autograd 會同時執行兩件事
- 執行請求的運算以計算結果張量
- 在 DAG 中維護運算的*梯度函數*。
當在 DAG 根節點上呼叫 .backward()
時,就會開始反向傳遞。autograd
接著會
- 從每個
.grad_fn
計算梯度, - 將它們累積在相應張量的
.grad
屬性中 - 使用鏈式法則,一路傳播到葉節點張量。
注意
**DAG 在 PyTorch 中是動態的** 有一點需要注意,圖表是從頭開始重新建立的;每次呼叫 .backward()
後,autograd 都會開始填充一個新的圖表。這正是允許您在模型中使用控制流程語句的原因;您可以根據需要在每次迭代中更改形狀、大小和運算。
選讀:張量梯度和雅可比積¶
在許多情況下,我們有一個純量損失函數,並且需要計算相對於某些參數的梯度。但是,在某些情況下,輸出函數是一個任意張量。在這種情況下,PyTorch 允許您計算所謂的**雅可比積**,而不是實際的梯度。
對於向量函數 \(\vec{y}=f(\vec{x})\),其中 \(\vec{x}=\langle x_1,\dots,x_n\rangle\) 且 \(\vec{y}=\langle y_1,\dots,y_m\rangle\),\(\vec{y}\) 相對於 \(\vec{x}\) 的梯度由**雅可比矩陣**給出
PyTorch 允許您針對給定的輸入向量 \(v=(v_1 \dots v_m)\) 計算**雅可比積** \(v^T\cdot J\),而不是計算雅可比矩陣本身。這是通過使用 \(v\) 作為參數調用 backward
來實現的。\(v\) 的大小應該與我們想要計算其乘積的原始張量的大小相同
請注意,當我們使用相同的參數第二次調用 backward
時,梯度的值會有所不同。發生這種情況是因為在進行 backward
傳播時,PyTorch 會**累積梯度**,也就是說,計算出的梯度值會添加到計算圖所有葉節點的 grad
屬性中。如果您想計算正確的梯度,則需要事先將 grad
屬性歸零。在實際訓練中,*優化器* 可以幫助我們做到這一點。
注意
之前我們是在沒有參數的情況下調用 backward()
函數。這基本上等同於調用 backward(torch.tensor(1.0))
,這是在純量值函數(例如神經網路訓練期間的損失)的情況下計算梯度的有用方法。