Pytorch自動(dòng)求導(dǎo)函數(shù)詳解流程以及與TensorFlow搭建網(wǎng)絡(luò)的對(duì)比
一、定義新的自動(dòng)求導(dǎo)函數(shù)
在底層,每個(gè)原始的自動(dòng)求導(dǎo)運(yùn)算實(shí)際上是兩個(gè)在Tensor上運(yùn)行的函數(shù)。其中,forward函數(shù)計(jì)算從輸入Tensor獲得的輸出Tensors。而backward函數(shù)接收輸出,Tensors對(duì)于某個(gè)標(biāo)量值得梯度,并且計(jì)算輸入Tensors相對(duì)于該相同標(biāo)量值得梯度。
在Pytorch中,可以容易地通過定義torch.autograd.Function的子類實(shí)現(xiàn)forward和backward函數(shù),來定義自動(dòng)求導(dǎo)函數(shù)。之后就可以使用這個(gè)新的自動(dòng)梯度運(yùn)算符了。我們可以通過構(gòu)造一個(gè)實(shí)例并調(diào)用函數(shù),傳入包含輸入數(shù)據(jù)的tensor調(diào)用它,這樣來使用新的自動(dòng)求導(dǎo)運(yùn)算
以下例子,自定義一個(gè)自動(dòng)求導(dǎo)函數(shù)展示ReLU的非線性,并調(diào)用它實(shí)現(xiàn)兩層網(wǎng)絡(luò),如上一節(jié)
import torch
class myrelu(torch.autograd.Function):#自定義子類
# 通過建立torch.autograd的子類來實(shí)現(xiàn)自定義的autograd函數(shù),并完成張量的正向和反向傳播
@staticmethod
def forward(ctx, x):
# 在正向傳播中,接受到一個(gè)上下文對(duì)象和一個(gè)包含輸入的張量,必須返回一個(gè)包含輸出的張量,可以使用上下文對(duì)象來緩存對(duì)象,以便在反向傳播中使用
ctx.save_for_backward(x)
return x.clamp(min=0)
@staticmethod
def backward(ctx, grad_output):
"""
在反向傳播中,我們接收到上下文對(duì)象和一個(gè)張量,
其包含了相對(duì)于正向傳播過程中產(chǎn)生的輸出的損失的梯度。
我們可以從上下文對(duì)象中檢索緩存的數(shù)據(jù),
并且必須計(jì)算并返回與正向傳播的輸入相關(guān)的損失的梯度。
"""
x, = ctx.saved_tensors
grad_x = grad_output.clone()
grad_x[x < 0] = 0
return grad_x
調(diào)用自定義的類實(shí)現(xiàn)兩層網(wǎng)絡(luò)
#%%
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# n是批量大小,d_in是輸入維度
# h是隱藏的維度,d_out是輸出維度
n,d_in,h,d_out=64,1000,100,10
# 創(chuàng)建隨機(jī)輸入和輸出數(shù)據(jù),requires_grad默認(rèn)設(shè)置為False,表示不需要后期微分操作
x=torch.randn(n,d_in,device=device)
y=torch.randn(n,d_out,device=device)
# 隨機(jī)初始化權(quán)重,requires_grad默認(rèn)設(shè)置為True,表示想要計(jì)算其微分
w1=torch.randn(d_in,h,device=device,requires_grad=True)
w2=torch.randn(h,d_out,device=device,requires_grad=True)
learning_rate=1e-6
for i in range(500):
#前向傳播,使用tensor上的操作計(jì)算預(yù)測值y
#調(diào)用自定義的myrelu.apply函數(shù)
y_pred=myrelu.apply(x.mm(w1)).mm(w2)
#使用tensor中的操作計(jì)算損失值,loss.item()得到loss這個(gè)張量對(duì)應(yīng)的數(shù)值
loss=(y_pred-y).pow(2).sum()
print(i,loss.item())
#使用autograd計(jì)算反向傳播,這個(gè)調(diào)用將計(jì)算loss對(duì)所有的requires_grad=True的tensor梯度,
#調(diào)用之后,w1.grad和w2.grad將分別是loss對(duì)w1和w2的梯度張量
loss.backward()
#使用梯度下降更新權(quán)重,只想對(duì)w1和w2的值進(jìn)行原地改變:不想更新構(gòu)建計(jì)算圖
#所以使用torch.no_grad()阻止pytorch更新構(gòu)建計(jì)算圖
with torch.no_grad():
w1-=learning_rate*w1.grad
w2-=learning_rate*w2.grad
#反向傳播后手動(dòng)將梯度置零
w1.grad.zero_()
w2.grad.zero_()
運(yùn)行結(jié)果

…

二、Pytorch 和 TensorFlow對(duì)比
- PyTorch自動(dòng)求導(dǎo)看似非常像TensorFlow:這兩個(gè)框架中,都定義了計(jì)算圖,使用自動(dòng)微分來計(jì)算梯度,兩者最大的不同是TensorFlow的計(jì)算圖是靜態(tài)的,而PyTorch使用的是動(dòng)態(tài)的計(jì)算圖。
- 在TensorFlow中,定義計(jì)算圖一次,然后重復(fù)執(zhí)行相同的圖,可能會(huì)提供不同的輸入數(shù)據(jù),而在PyTorch中,每一個(gè)前向通道定義一個(gè)新的計(jì)算圖。
- **靜態(tài)圖的好處在于可以預(yù)先對(duì)圖進(jìn)行優(yōu)化。**如:一個(gè)框架可以融合一些圖的運(yùn)算來提升效率,或者產(chǎn)生一個(gè)策略來將圖分布到多個(gè)GPU或機(jī)器上。但是如果重復(fù)使用相同的圖,那么重復(fù)運(yùn)行同一個(gè)圖時(shí),前期潛在的代價(jià)高昂的預(yù)先優(yōu)化的消耗就會(huì)被分?jǐn)偂?/li>
- 靜態(tài)圖和動(dòng)態(tài)圖的一個(gè)區(qū)別就是控制流。對(duì)于一些模型,對(duì)每個(gè)數(shù)據(jù)點(diǎn)執(zhí)行不同的計(jì)算。如:一個(gè)遞歸神經(jīng)網(wǎng)絡(luò)可能對(duì)于每個(gè)數(shù)據(jù)點(diǎn)執(zhí)行不同的時(shí)間步數(shù),這個(gè)展開可以作為一個(gè)循環(huán)來實(shí)現(xiàn)。對(duì)于一個(gè)靜態(tài)圖,循環(huán)結(jié)構(gòu)要作為圖的一部分。因此,TensorFlow提供了運(yùn)算符將循環(huán)嵌入到圖當(dāng)中。對(duì)于動(dòng)態(tài)圖來說,情況更加簡單:為每個(gè)例子即時(shí)創(chuàng)建圖,使用普通的命令式控制流來為每個(gè)輸入執(zhí)行不同的計(jì)算。
使用TensorFlow擬合一個(gè)簡單的兩層網(wǎng)絡(luò)(上面做對(duì)比):
#%%使用TensorFlow
import tensorflow.compat.v1 as tf #為了用placeholder不惜一切代價(jià)
tf.disable_v2_behavior()
import numpy as np
#%%
# 建立計(jì)算圖
# n是批量大小,d_in是輸入維度
# h是隱藏的維度,d_out是輸出維度
n,d_in,h,d_out=64,1000,100,10
# 為輸入和目標(biāo)數(shù)據(jù)創(chuàng)建placeholder,在執(zhí)行計(jì)算圖時(shí),他們將會(huì)被真實(shí)的數(shù)據(jù)填充
x=tf.placeholder(tf.float32,shape=(None,d_in))
y=tf.placeholder(tf.float32,shape=(None,d_out))
# 為權(quán)重創(chuàng)建variable并用隨機(jī)數(shù)據(jù)初始化,TensorFlow的variable在執(zhí)行計(jì)算圖時(shí)不會(huì)改變
w1 = tf.Variable(tf.random_normal((d_in,h)))
w2=tf.Variable(tf.random_normal((h,d_out)))
# 前向傳播:使用TensorFlow的張量運(yùn)算計(jì)算預(yù)測值y(這段代碼不執(zhí)行任何數(shù)值運(yùn)算,只是建立了稍后要執(zhí)行的計(jì)算圖)
h=tf.matmul(x,w1)
h_relu=tf.maximum(h,tf.zeros(1))
y_pred=tf.matmul(h_relu,w2)
# 使用TensorFlow的張量運(yùn)算損失loss
loss=tf.reduce_sum((y-y_pred)**2.0)
# 計(jì)算loss對(duì)于權(quán)重w1和w2的導(dǎo)數(shù)
grad_w1,grad_w2=tf.gradients(loss,[w1,w2])
# 使用梯度下降更新權(quán)重,為了實(shí)際更新權(quán)重,我們需要在執(zhí)行計(jì)算圖時(shí)計(jì)算new_w1和new_w2
# 注:在TensorFlow中,更新權(quán)重值得行為是計(jì)算圖的一部分,但在Pytorch中發(fā)生在計(jì)算圖形之外
learning_rate=1e-6
new_w1=w1.assign(w1-learning_rate*grad_w1)
new_w2=w2.assign(w2-learning_rate*grad_w2)
# 現(xiàn)在搭建好了計(jì)算圖,開始一個(gè)TensorFlow回話來執(zhí)行計(jì)算圖
with tf.Session() as sess:
# 運(yùn)算一次計(jì)算圖來出事話variable w1和w2
sess.run(tf.global_variables_initializer())
# 創(chuàng)建numpy數(shù)組存儲(chǔ)輸入x和目標(biāo)y的實(shí)際數(shù)據(jù)
x_value=np.random.randn(n,d_in)
y_value=np.random.randn(n,d_out)
for i in range(500):
# 多次運(yùn)行計(jì)算圖,每次執(zhí)行時(shí),都有feed_dict參數(shù),
# 將x_value綁定到x,將y_value綁定到y(tǒng).每次執(zhí)行計(jì)算圖都要計(jì)算損失,
# new_w1和new_w2,這些張量的值以numpy數(shù)組的形式返回
loss_value,i,i=sess.run([loss,new_w1,new_w2],
feed_dict={x:x_value,y:y_value})
print(loss_value)
運(yùn)行結(jié)果

…

今日告一段落,重點(diǎn)是比較了TensorFlow和Pytorch在自動(dòng)求導(dǎo)中的區(qū)別——計(jì)算圖前者是靜態(tài)的,后者是動(dòng)態(tài)的。
再見啦,明天可能不更~因?yàn)橄挛缤砩隙加姓n,雖然我可能不去上(哈哈哈哈哈哈哈哈,別學(xué)我)后面一節(jié)來寫神經(jīng)網(wǎng)絡(luò),不見不散!!
到此這篇關(guān)于Pytorch自動(dòng)求導(dǎo)函數(shù)詳解流程以及與TensorFlow搭建網(wǎng)絡(luò)的對(duì)比的文章就介紹到這了,更多相關(guān)Pytorch 自動(dòng)求導(dǎo)函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 實(shí)現(xiàn)將Numpy數(shù)組保存為圖像
今天小編就為大家分享一篇python 實(shí)現(xiàn)將Numpy數(shù)組保存為圖像,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-01-01
Python中itertools庫的四個(gè)函數(shù)介紹
這篇文章主要介紹了Python中itertools庫的四個(gè)函數(shù),主要討論itertools庫中的十分使用的幾個(gè)函數(shù),并重點(diǎn)介紹什么時(shí)候我們應(yīng)該考慮使用它們,需要的朋友可以參考一下2022-04-04
python+selenium 定位到元素,無法點(diǎn)擊的解決方法
今天小編就為大家分享一篇python+selenium 定位到元素,無法點(diǎn)擊的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-01-01
Pygame游戲開發(fā)之太空射擊實(shí)戰(zhàn)盾牌篇
相信大多數(shù)8090后都玩過太空射擊游戲,在過去游戲不多的年代太空射擊自然屬于經(jīng)典好玩的一款了,今天我們來自己動(dòng)手實(shí)現(xiàn)它,在編寫學(xué)習(xí)中回顧過往展望未來,在本課中,我們將為玩家添加一個(gè)盾牌以及一個(gè)用于顯示盾牌等級(jí)的欄2022-08-08
淺談keras2 predict和fit_generator的坑
這篇文章主要介紹了淺談keras2 predict和fit_generator的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-06-06
圖解python全局變量與局部變量相關(guān)知識(shí)
這篇文章主要介紹了圖解python全局變量與局部變量相關(guān)知識(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Anaconda環(huán)境改名的實(shí)現(xiàn)步驟
本文主要介紹了Anaconda環(huán)境改名的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
詳解Python實(shí)現(xiàn)圖像分割增強(qiáng)的兩種方法
圖像分割就是把圖像分成若干個(gè)特定的、具有獨(dú)特性質(zhì)的區(qū)域并提出感興趣目標(biāo)的技術(shù)和過程。本文將為大家分享兩個(gè)用Python實(shí)現(xiàn)像分割增強(qiáng)的方法,需要的可以參考一下2022-03-03

