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