pytorch autograd整理
網上有不少介紹這個的,我再自己整理一遍以加強理解,便于以后查閱。
Pytorch已經成為超越Tensorflow,成為當前最流行的深度學習框架。我覺得作為一個深度學習框架,最重要的就是兩大塊,一個是對GPU的便捷操作,第二個是對反向傳播的自動實現,pytorch都做的非常不錯。pytorch的autograd模塊實現反向傳播式的自動求導
1,鏈式求導法則和反向傳播
深度神經網絡可以看成一個多層嵌套的復合函數,例如卷積層、ReLU層等每層都是一個函數,復合函數求導使用經典的鏈式求導法則,就是
dydx=dydu?dudx\frac{dy}{dx} = \frac{dy}{du}\cdot \frac{du}{dx}dxdy?=dudy??dxdu?
其中 dududu是中間層,可以有多個。對于多元函數,例如深度學習中每層,這時把求導變成求偏導即可,此時的導數也改名叫做梯度。
這個公式其實也很好理解,因為導數的含義就是輸出隨輸入的變化率,多個串聯的輸入輸出的變化率乘起來就等于最后輸出關于最初輸入的變化率。當然復合函數也不僅是只有一個輸入一個輸出的這種串聯結構,還可以有多個輸入一個輸出的形式(不能一個輸入多個輸出,那就不叫函數了)。如果把復合函數理解為數據經過各個節點進行不斷加工處理的一個網絡的話,就會好理解的多,引用知乎上的一張圖片:箭頭是反向傳播的過程,正向過程沿箭頭反方向。
反向傳播的目的是為了求得最終輸出關于輸入的梯度,我們把它化解為各層函數的梯度的組合形式,每層函數的梯度的方程式都是固定和事先知道的,但是每層函數的輸入輸出值仍需要正向傳播計算一遍才能都知道,而導數的計算時又是相反的過程,每層的梯度求解依賴于后層的梯度數值和本層的求導公式。
注意,深度神經網絡中,輸入和各層的參數都是葉子節點。葉子節點的梯度不參與反向傳播主過程,所以葉子節點可以不求導,不影響其他節點的求導。當然,葉子節點的梯度反而正是我們需要的,所以我們需要從反向傳播的主節點中再導出各葉子節點的梯度。
2,requires_grad
經過上面的梳理,我們再結合pytorch具體代碼來理解。由于葉子節點的梯度并不是反向傳播過程必須的,所以就可以根據使用者的需求來設置是否求它們的梯度。對于非葉子節點,因為它們的梯度是必須的,所以不能設置為requires_grad=False
3,retain_grad
對于非葉子節點,雖然它們的梯度必須求,但通常它們只是中間過程,Pytorch為了節省內存(顯存)占用,默認為用完即刪,不做保存。如果我們需要它,可以給需要的非葉子節點的變量設置為retain_grad=True,這樣它的梯度就會保存下來。
4, retain_graph
pytorch使用動態圖機制,也就是說每次正向傳播到輸出節點的時候(輸出節點通常就是loss值),在loss處保存了產生它的所有前序節點的關系(用一個有向無環圖表示),這個關系用來指導反向傳播時的路徑,但是這個關系只用一次就刪除了,這樣就允許下一次正向傳播時使用不一樣的路徑,這種方式就是動態圖機制,它更加的靈活。但是有時候也許我們一次反向傳播之后還不想正向傳播,還需要再加入一些新的計算后再反向傳播一次,(比如我們使用多個損失函數相加,可以每個損失函數反向傳播一次后再計算下一個損失函數再反向傳播,這樣節省顯存),這個時候必須保留前面反向傳播的計算圖,就需要設置loss.backward(retain_graph=True)。注意最后一次沒必要再保留,這樣可以降低一些顯存。
5,inplace操作
注意某個張量在被其他函數使用之后就不能再使用inplace操作來修改值了,因為這樣反向傳播計算梯度的時候需要用到的原來那個數值就找不到了。如果需要修改,可以使用非inplace操作。inplace操作有: +=1,切片賦值[0]=1等。
6, 不能微分的函數
如果計算過程中有不能微分的函數,反向傳播就會出問題。
常見的有argmax(),sign()等,會報錯。
對于round(),ceil(),floor(),這幾個雖然不會報錯,但會導致梯度變成0,失去意義,也不要使用。
對于abs(), relu等似乎數學上不嚴格可微,存在個別不可微點的,pytorch進行了特殊處理,反而是可以使用的。
7, detach()
x1 = x.detach()用來在當前計算圖中分離x,新的x1和x共享相同的存儲空間,但新的x1不需要梯度。
最后放一段簡單的代碼,可以用來試驗剛才提到的各種功能。還可以試驗optimizer,它的用法下次再總結。
import torchx1 = torch.tensor([[1.,2.],[2.,3.],[1.,1.]],requires_grad=True) w1 = torch.tensor([[1.,2.,3.],[3.,2.,1.]],requires_grad=True) for epoch in range(1):y1 = torch.mm(w1,x1)y1.retain_grad()w2 = torch.tensor([[1.,3.],[2.,1.]])y2 = torch.mm(w2,y1)#w1 += 1y = y2.view(-1).max()optimizer = torch.optim.SGD([x1,w1],lr=0.001 )optimizer.zero_grad()y.backward(retain_graph=True)y3 = y**2y3.backward()optimizer.step()print(epoch,y) print('w1:',w1,'\n','w1.grad:',w1.grad) print('w2:',w2,'\n','w2.grad:',w2.grad) print('x1:',x1,'\n','x1.grad:',x1.grad)總結
以上是生活随笔為你收集整理的pytorch autograd整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简明代码介绍类激活图CAM, GradC
- 下一篇: pytorch简单代码实现deep dr