天天看點

Paddle中的自動微分功能測試

Paddle中的自動微分功能測試
簡 介: 初步分析了求解梯度的部分。詳細的過程可以參見以下文獻: pytorch自動求梯度—詳解

關鍵詞

: 自動求導,Paddle

01 自動微分

  

PaddlePaddle

的神經網絡核心是自動微分,本篇文章主要為你介紹如何使用飛槳的自動微分,以及飛槳的自動微分機制,幫助你更好的使用飛槳進行訓練。

一、背景

  神經網絡是由節點和節點間的互相連接配接組成的。網絡中每層的每個節點代表一種特定的函數,來對輸入進行計算。每個函數都是由不同參數(權重

w

和偏置

b

)組成。神經網絡訓練的過程,就是不斷讓這些函數的參數進行學習、優化,以能夠更好的處理後面輸入的過程。

  為了讓神經網絡的判斷更加準确,首先需要有衡量效果的工具,于是損失函數應運而生。如果你想要神經網絡的效果好,那麼就要讓損失函數盡可能的小,于是深度學習引入了能夠有效計算函數最小值的算法–梯度下降等優化算法,以及參數優化更新過程–反向傳播。

  • 前向傳播是輸入通過每一層節點計算後得到每層輸出,上層輸出又作為下一層的輸入,最終達到輸出層。然後通過損失函數計算得到

    loss

    值。
  • 反向傳播是通過

    loss

    值來指導前向節點中的函數參數如何改變,并更新每層中每個節點的參數,來讓整個神經網絡達到更小的

    loss

  自動微分機制就是讓你隻關注組網中的前向傳播過程,然後飛槳架構來自動完成反向傳播過程,進而來讓你從繁瑣的求導、求梯度的過程中解放出來。

二、應用Paddle自動微分機制

  本文通過一個比較簡單的模型來還原飛槳的自動微分過程。 本示例基于

Paddle2.0

編寫。

import paddle
from paddle.vision.models import vgg11
import paddle.nn.functional as F
import numpy as np

print(paddle.__version__)           
2.2.1           

1、定義模型

  本案例首先定義網絡。因為本示例着重展示如何使用飛槳進行自動微分,故組網部分不過多展開,直接使用高層

API

中封裝好的模型

vgg11

  然後随機初始化一個輸入

x

,和對應标簽

label

model = vgg11()

x = paddle.rand([1,3,224,224])
label = paddle.randint(0,1000)           

  label 取值為一個 0 - 1000之間的随機值:

Tensor(shape=[1], dtype=int64, place=CPUPlace, stop_gradient=True,
       [578])           

2、前向傳播

  然後将輸入傳入到模型中,進行前向傳播過程。

predicts = model(x)
print(predicts)           
Tensor(shape=[1, 1000], dtype=float32, place=CPUPlace, stop_gradient=False,
       [[-5.86728096,  4.47268057, -0.95992291,  0.23020555,  1.00721347,
          2.99545169, -2.75388002, -2.63786125, -3.75683165,  0.73450726,
          0.85474956, -0.75945181, -5.11652756,  0.88256705, -4.56009960,
.....
          1.58102369, -1.69987798, -1.06336939, -0.56181955,  0.71741229,
         -3.75649190,  0.65584004,  0.91495293, -0.22558716,  0.40234384,
          1.70077145,  1.04505992, -3.24514723, -0.23471458, -0.45503062]])           

  前向傳播結束後,你就得到模型的預測結果

predicts

,這時可以使用飛槳中的對應損失函數

API

進行損失函數的計算。該例子中使用

cross_entropy

來計算損失函數,來衡量模型的預測情況。

3、計算損失與反向傳播

loss = F.cross_entropy(predicts, label)

print(loss)           
Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [12.34226704])           

  随後進行反向傳播,在飛槳中你隻需要調用

backward()

即可自動化展開反向傳播過程。各梯度儲存在

grad

屬性中。

loss.backward()           

4、優化器

  然後來定義優化器,本例子中使用

Adam

優化器,設定

learning_rate

0.001

,并把該模型的所有參數傳入優化器中。

optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())           

  最後通過

step

來開始執行優化器,并進行模型參數的更新

optim.step()           

  通過以上步驟,你已經完成了一個神經網絡前向傳播、反向傳播的所有過程。快自己動手試試吧!

三、自動微分使用說明

  此章主要介紹飛槳中所有自動微分過程中會使用到的方法、屬性等。屬于第二部分的擴充閱讀。

1、stop grandient屬性

  飛槳中的

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

$${{\partial z} \over {\partial x}} = 2x,\space \space \space {{\partial z} \over {\partial y}} = 4$$

  對

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: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
       [2., 4., 6.])
Tensor y's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
       [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: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
       [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.]           

(1)說明

  如果不貂絨

backward()

則無法産生

gradient()

print("Before clear {}".format(loss2.gradient()))
print(ret2.gradient())

loss2.clear_grad()
ret2.clear_grad()
print("After clear {}".format(loss2.gradient()))

print(ret2.gradient())           

  隻有貂絨 backward(),才可以獲得對應的梯度:

Before clear [1.]
[[1. 1.]
 [1. 1.]]
After clear [0.]
[[0. 0.]
 [0. 0.]]           

四、自動微分運作機制

  本章主要介紹飛槳在實作反向傳播進行自動微分計算時,内部是如何運作工作的。此部分為選讀部分,更多是介紹飛槳内部實作機制,可以選擇跳過,跳過不會影響你的正常使用。

1、自動微分

  飛槳的自動微分是通過

trace

的方式,記錄前向

OP

的執行,并自動建立反向

var

和添加相應的反向

OP

,然後來實作反向梯度計算的。

Paddle中的自動微分功能測試

▲ 圖1.4.1 自動微分的流程圖

2、舉例

  下面本文用一些的例子,來模拟這個過程。

  例子一:首先用一個比較簡單的例子來讓你了解整個過程。

(1)測試代碼

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: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [5.])
Tensor b's grad is: None           

(2)分析

  在上面代碼中

c.backward()

執行前,你可以了解整個計算圖是這樣的:

Paddle中的自動微分功能測試

▲ 圖1.4.2 PaddlePaddle梯度求解

  當建立

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()

後,正式開始進行反向傳播過程,開始自動計算微分。

Paddle中的自動微分功能測試

▲ 圖1.4.3 反向計算梯度過程

  例子二:用一個稍微複雜一點的例子讓你深入了解這個過程。

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: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [20.])
Tensor b's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [8.])
Tensor c's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [4.])
Tensor d's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [10.])           

  該例子的正向和反向圖建構過程即:

Paddle中的自動微分功能測試

▲ 圖1.4.4 例子2計算梯度過程

※ 總  結 ※

  初步分析了求解梯度的部分。詳細的過程可以參見以下文獻:

■ 相關文獻連結:

● 相關圖表連結:

★ 本文來自于CSDN文獻:<

Paddle中的自動微分功能測試

關鍵詞

PaddlePaddle

w

b

  • loss

  • loss

    loss

Paddle2.0

import paddle
from paddle.vision.models import vgg11
import paddle.nn.functional as F
import numpy as np

print(paddle.__version__)           
2.2.1           

API

vgg11

x

label

model = vgg11()

x = paddle.rand([1,3,224,224])
label = paddle.randint(0,1000)           
Tensor(shape=[1], dtype=int64, place=CPUPlace, stop_gradient=True,
       [578])           

predicts = model(x)
print(predicts)           
Tensor(shape=[1, 1000], dtype=float32, place=CPUPlace, stop_gradient=False,
       [[-5.86728096,  4.47268057, -0.95992291,  0.23020555,  1.00721347,
          2.99545169, -2.75388002, -2.63786125, -3.75683165,  0.73450726,
          0.85474956, -0.75945181, -5.11652756,  0.88256705, -4.56009960,
.....
          1.58102369, -1.69987798, -1.06336939, -0.56181955,  0.71741229,
         -3.75649190,  0.65584004,  0.91495293, -0.22558716,  0.40234384,
          1.70077145,  1.04505992, -3.24514723, -0.23471458, -0.45503062]])           

predicts

API

cross_entropy

loss = F.cross_entropy(predicts, label)

print(loss)           
Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [12.34226704])           

backward()

grad

loss.backward()           

Adam

learning_rate

0.001

optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())           

step

optim.step()           

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           

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: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
       [2., 4., 6.])
Tensor y's grad is: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
       [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: Tensor(shape=[3], dtype=float32, place=CPUPlace, stop_gradient=False,
       [1., 1., 1.])           

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.]           

backward()

gradient()

print("Before clear {}".format(loss2.gradient()))
print(ret2.gradient())

loss2.clear_grad()
ret2.clear_grad()
print("After clear {}".format(loss2.gradient()))

print(ret2.gradient())           
Before clear [1.]
[[1. 1.]
 [1. 1.]]
After clear [0.]
[[0. 0.]
 [0. 0.]]           

trace

OP

var

OP

Paddle中的自動微分功能測試

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: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [5.])
Tensor b's grad is: None           

c.backward()

Paddle中的自動微分功能測試

Tensor

Tensor

stop_grad=False

Tensor

Tensor

a

Tensor

a_grad

a_grad

OP

a

op

grad_op

None

OP

OP

OP

OP

OP

c

OP

c

Tensor

c_grad,

grad_op

OP

OP

MulBackward

backward()

Paddle中的自動微分功能測試
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: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [20.])
Tensor b's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [8.])
Tensor c's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [4.])
Tensor d's grad is: Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
       [10.])           
Paddle中的自動微分功能測試

繼續閱讀