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

…

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

…

今日告一段落,重點是比較了TensorFlow和Pytorch在自動求導(dǎo)中的區(qū)別——計算圖前者是靜態(tài)的,后者是動態(tài)的。
再見啦,明天可能不更~因為下午晚上都有課,雖然我可能不去上(哈哈哈哈哈哈哈哈,別學我)后面一節(jié)來寫神經(jīng)網(wǎng)絡(luò),不見不散?。?/p>
到此這篇關(guān)于Pytorch自動求導(dǎo)函數(shù)詳解流程以及與TensorFlow搭建網(wǎng)絡(luò)的對比的文章就介紹到這了,更多相關(guān)Pytorch 自動求導(dǎo)函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 實現(xiàn)將Numpy數(shù)組保存為圖像
今天小編就為大家分享一篇python 實現(xiàn)將Numpy數(shù)組保存為圖像,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
python+selenium 定位到元素,無法點擊的解決方法
今天小編就為大家分享一篇python+selenium 定位到元素,無法點擊的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01
Pygame游戲開發(fā)之太空射擊實戰(zhàn)盾牌篇
相信大多數(shù)8090后都玩過太空射擊游戲,在過去游戲不多的年代太空射擊自然屬于經(jīng)典好玩的一款了,今天我們來自己動手實現(xiàn)它,在編寫學習中回顧過往展望未來,在本課中,我們將為玩家添加一個盾牌以及一個用于顯示盾牌等級的欄2022-08-08
淺談keras2 predict和fit_generator的坑
這篇文章主要介紹了淺談keras2 predict和fit_generator的坑,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06

