自动微分基本理论
自動微分基本理論
神經網絡核心是自動微分,本文主要介紹如何使用自動微分,以及自動微分機制,幫助更好的使用自動微分進行訓練。
一、背景
神經網絡是由節點和節點間的相互連接組成的。網絡中每層的每個節點代表一種特定的函數,對輸入進行計算。每個函數都是由不同參數(權重w和偏置b)組成。神經網絡訓練的過程,就是不斷讓這些函數的參數進行學習、優化,能夠更好的處理后面輸入的過程。
讓神經網絡的判斷更加準確,首先需要有衡量效果的工具,于是損失函數應運而生。如果想要神經網絡的效果好,就要讓損失函數盡可能的小,于是深度學習引入了能夠有效計算函數最小值的算法–梯度下降等優化算法,以及參數優化更新過程–反向傳播。
? 前向傳播是輸入通過每一層節點計算后得到每層輸出,上層輸出又作為下一層的輸入,最終達到輸出層。然后通過損失函數計算得到loss值。
? 反向傳播是通過loss值來指導前向節點中的函數參數如何改變,更新每層中每個節點的參數,來讓整個神經網絡達到更小的loss值。
自動微分機制只關注組網中的前向傳播過程,AIFramework框架自動完成反向傳播過程,從繁瑣的求導、求梯度的過程中解放出來。
二、如何使用自動微分機制
本文通過一個比較簡單的模型來還原飛槳的自動微分過程。 本示例基于Paddle2.0編寫。
#加載飛槳和相關類庫
import paddle
from paddle.vision.models import vgg11
import paddle.nn.functional as F
import numpy as np
print(paddle.version)
2.0.1
本案例首先定義網絡。因為本示例著重展示如何使用飛槳進行自動微分,組網部分不過多展開,直接使用高層API中封裝好的模型vgg11。
然后隨機初始化一個輸入x,對應標簽label。
model = vgg11()
x = paddle.rand([1,3,224,224])
label = paddle.randint(0,1000)
將輸入傳入到模型中,進行前向傳播。
前向傳播
predicts = model(x)
前向傳播結束后,就得到模型的預測結果predicts,這時,可以使用飛槳中的對應損失函數API進行損失函數的計算。該例子中使用cross_entropy來計算損失函數,衡量模型的預測情況。
計算損失
loss = F.cross_entropy(predicts, label)
隨后進行反向傳播,在飛槳中只需要調用backward(),即可自動化展開反向傳播過程。各梯度保存在grad屬性中。
開始進行反向傳播
loss.backward()
定義優化器,本例子中使用Adam優化器,設置learning_rate為0.001,把該模型的所有參數傳入優化器中。
設置優化器
optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
最后,通過step來開始執行優化器,進行模型參數的更新
更新參數
optim.step()
通過以上步驟,已經完成了一個神經網絡前向傳播、反向傳播的所有過程。
三、自動微分相關所有的使用方法說明
主要介紹所有自動微分過程中會使用到的方法、屬性等。屬于第二部分的擴展。
1、飛槳中的Tensor有stop_gradient屬性,這個屬性可以查看一個Tensor是否計算,傳播梯度。
? 如果為True,該Tensor不會計算梯度,阻絕Autograd的梯度傳播。
? 反之,計算梯度并傳播梯度。用戶自行創建的的Tensor,默認stop_gradient為True,即默認不計算梯度;模型參數的stop_gradient默認都為False,即默認計算梯度。
import paddle
a = paddle.to_tensor([1.0, 2.0, 3.0])
b = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False) # 將b設置為需要計算梯度的屬性
print(a.stop_gradient)
print(b.stop_gradient)
True
False
a.stop_gradient = False
print(a.stop_gradient)
False
2、接下來,本文用一個簡單的計算圖來了解如何調用backward()函數。開始從當前Tensor開始計算反向的神經網絡,傳導并計算計算圖中Tensor的梯度。
import paddle
x = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False)
y = paddle.to_tensor([4.0, 5.0, 6.0], stop_gradient=False)
z = x ** 2 + 4 * y
假設上面創建的x和y分別是神經網絡中的參數,z為神經網絡的損失值loss。
對z調用backward(),可以自動計算x和y的梯度,存進grad屬性中。
z.backward()
print(“Tensor x’s grad is: {}”.format(x.grad))
print(“Tensor y’s grad is: {}”.format(y.grad))
Tensor x’s grad is: [2. 4. 6.]
Tensor y’s grad is: [4. 4. 4.]
此外,飛槳默認會釋放反向計算圖。如果在backward()之后繼續添加OP,需要將backward()中的retain_graph參數設置為True,之前的反向計算圖會保留。
提示:將其設置為False會更加節省內存。默認值是False,所以可以直接不設置此參數。
import paddle
x = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False)
y = x + 3
y.backward(retain_graph=True) # 設置retain_graph為True,保留反向計算圖
print(“Tensor x’s grad is: {}”.format(x.grad))
Tensor x’s grad is: [1. 1. 1.]
3、backward()會累積梯度,飛槳還提供了clear_grad()函數來清除當前Tensor的梯度。
import paddle
import numpy as np
x = np.ones([2, 2], np.float32)
inputs2 = []
for _ in range(10):
tmp = paddle.to_tensor(x)
tmp.stop_gradient = False
inputs2.append(tmp)
ret2 = paddle.add_n(inputs2)
loss2 = paddle.sum(ret2)
loss2.backward()
print(“Before clear {}”.format(loss2.gradient()))
loss2.clear_grad()
print(“After clear {}”.format(loss2.gradient()))
Before clear [1.]
After clear [0.]
四、飛槳自動微分運行機制
主要介紹在實現反向傳播進行自動微分計算時,內部是如何運行工作的。此部分為選讀部分,更多是介紹飛槳內部實現機制,可以選擇跳過,跳過不會影響正常使用。
飛槳的自動微分是通過trace的方式,記錄前向OP的執行,自動創建反向var和添加相應的反向OP,然后來實現反向梯度計算的。
下面本文用一些的例子,模擬這個過程。
例子一:首先用一個比較簡單的例子來了解整個過程。
import paddle
a = paddle.to_tensor(2.0, stop_gradient=False)
b = paddle.to_tensor(5.0, stop_gradient=True)
c = a * b
c.backward()
print(“Tensor a’s grad is: {}”.format(a.grad))
print(“Tensor b’s grad is: {}”.format(b.grad))
Tensor a’s grad is: [5.]
Tensor b’s grad is: None
在上面代碼中c.backward()執行前,可以理解整個計算圖是這樣的:
當創建Tensor,Tensor的stop_grad=False時,自動為此Tensor創建一個反向Tensor。在此例子中,a的反向Tensor就是a_grad。在a_grad中,記錄反向OP,因為a沒有作為任何反向op的輸入,所以grad_op為None。
當執行OP時,自動創建反向OP,不同的OP創建反向OP的方法不同,傳的內容也不同。本文以這個乘法OP為例:
-乘法OP的反向OP,即MulBackward的輸入,正向OP的兩個輸入,以及正向OP的輸出Tensor的反向Tensor。在此例子中就是,a、b、c_grad
-乘法OP的反向OP,即MulBackward的輸出,正向OP的兩個輸入的反向Tensor(如果輸入是stop_gradient=True,則即為None)。在此例子中就是,a_grad、None(b_grad)
-乘法OP的反向OP,即MulBackward的grad_pending_ops是自動構建反向網絡的時候,這個反向op知道下一個可以執行的反向op是哪一個,可以理解為反向網絡中,一個反向op指向下一個反向op的邊。
當c通過乘法OP被創建后,c會創建一個反向Tensor:c_grad,grad_op為該乘法OP的反向OP,即MulBackward。
調用backward()后,正式開始進行反向傳播過程,開始自動計算微分。
例二:用一個稍微復雜一點的例子深入了解這個過程。
import paddle
a = paddle.to_tensor(2.0, stop_gradient=False)
b = paddle.to_tensor(5.0, stop_gradient=False)
c = a * b
d = paddle.to_tensor(4.0, stop_gradient=False)
e = c * d
e.backward()
print(“Tensor a’s grad is: {}”.format(a.grad))
print(“Tensor b’s grad is: {}”.format(b.grad))
print(“Tensor c’s grad is: {}”.format(c.grad))
print(“Tensor d’s grad is: {}”.format(d.grad))
Tensor a’s grad is: [20.]
Tensor b’s grad is: [8.]
Tensor c’s grad is: [4.]
Tensor d’s grad is: [10.]
該例的正向和反向圖構建過程即:
五、總結
本文主要介紹了如何使用自動微分,以及自動微分機制。
總結
- 上一篇: 车辆在线标定
- 下一篇: TVM适配NN编译Compiler缺陷