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中,可以容易地通過(guò)定義torch.autograd.Function的子類實(shí)現(xiàn)forward和backward函數(shù),來(lái)定義自動(dòng)求導(dǎo)函數(shù)。之后就可以使用這個(gè)新的自動(dòng)梯度運(yùn)算符了。我們可以通過(guò)構(gòu)造一個(gè)實(shí)例并調(diào)用函數(shù),傳入包含輸入數(shù)據(jù)的tensor調(diào)用它,這樣來(lái)使用新的自動(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):#自定義子類 # 通過(guò)建立torch.autograd的子類來(lái)實(shí)現(xiàn)自定義的autograd函數(shù),并完成張量的正向和反向傳播 @staticmethod def forward(ctx, x): # 在正向傳播中,接受到一個(gè)上下文對(duì)象和一個(gè)包含輸入的張量,必須返回一個(gè)包含輸出的張量,可以使用上下文對(duì)象來(lái)緩存對(duì)象,以便在反向傳播中使用 ctx.save_for_backward(x) return x.clamp(min=0) @staticmethod def backward(ctx, grad_output): """ 在反向傳播中,我們接收到上下文對(duì)象和一個(gè)張量, 其包含了相對(duì)于正向傳播過(guò)程中產(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è)置為T(mén)rue,表示想要計(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ù)測(cè)值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)微分來(lái)計(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)算來(lái)提升效率,或者產(chǎn)生一個(gè)策略來(lái)將圖分布到多個(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è)展開(kāi)可以作為一個(gè)循環(huán)來(lái)實(shí)現(xiàn)。對(duì)于一個(gè)靜態(tài)圖,循環(huán)結(jié)構(gòu)要作為圖的一部分。因此,TensorFlow提供了運(yùn)算符將循環(huán)嵌入到圖當(dāng)中。對(duì)于動(dòng)態(tài)圖來(lái)說(shuō),情況更加簡(jiǎn)單:為每個(gè)例子即時(shí)創(chuàng)建圖,使用普通的命令式控制流來(lái)為每個(gè)輸入執(zhí)行不同的計(jì)算。
使用TensorFlow擬合一個(gè)簡(jiǎn)單的兩層網(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ù)測(cè)值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ì)算圖,開(kāi)始一個(gè)TensorFlow回話來(lái)執(zhí)行計(jì)算圖 with tf.Session() as sess: # 運(yùn)算一次計(jì)算圖來(lái)出事話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)的。
再見(jiàn)啦,明天可能不更~因?yàn)橄挛缤砩隙加姓n,雖然我可能不去上(哈哈哈哈哈哈哈哈,別學(xué)我)后面一節(jié)來(lái)寫(xiě)神經(jīng)網(wǎng)絡(luò),不見(jiàn)不散!!
到此這篇關(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ì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01Python中itertools庫(kù)的四個(gè)函數(shù)介紹
這篇文章主要介紹了Python中itertools庫(kù)的四個(gè)函數(shù),主要討論itertools庫(kù)中的十分使用的幾個(gè)函數(shù),并重點(diǎn)介紹什么時(shí)候我們應(yīng)該考慮使用它們,需要的朋友可以參考一下2022-04-04python+selenium 定位到元素,無(wú)法點(diǎn)擊的解決方法
今天小編就為大家分享一篇python+selenium 定位到元素,無(wú)法點(diǎn)擊的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Pygame游戲開(kāi)發(fā)之太空射擊實(shí)戰(zhàn)盾牌篇
相信大多數(shù)8090后都玩過(guò)太空射擊游戲,在過(guò)去游戲不多的年代太空射擊自然屬于經(jīng)典好玩的一款了,今天我們來(lái)自己動(dòng)手實(shí)現(xiàn)它,在編寫(xiě)學(xué)習(xí)中回顧過(guò)往展望未來(lái),在本課中,我們將為玩家添加一個(gè)盾牌以及一個(gè)用于顯示盾牌等級(jí)的欄2022-08-08淺談keras2 predict和fit_generator的坑
這篇文章主要介紹了淺談keras2 predict和fit_generator的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06圖解python全局變量與局部變量相關(guān)知識(shí)
這篇文章主要介紹了圖解python全局變量與局部變量相關(guān)知識(shí),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Anaconda環(huán)境改名的實(shí)現(xiàn)步驟
本文主要介紹了Anaconda環(huán)境改名的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07詳解Python實(shí)現(xiàn)圖像分割增強(qiáng)的兩種方法
圖像分割就是把圖像分成若干個(gè)特定的、具有獨(dú)特性質(zhì)的區(qū)域并提出感興趣目標(biāo)的技術(shù)和過(guò)程。本文將為大家分享兩個(gè)用Python實(shí)現(xiàn)像分割增強(qiáng)的方法,需要的可以參考一下2022-03-03