PyTorch基础(二)-----自动求导Autograd
一、前言
上一篇文章中提到PyTorch提供了兩個重要的高級功能,分別是:
- 具有強大的GPU加速的張量計算(如NumPy)
- 包含自動求導系統的的深度神經網絡
第一個特性我們會在之后的學習中用到,這里暫且按下不表,我們首先來探討研究一下什么是自動求導Autograd、自動求導Autograd的原理是怎樣的等問題。
- 當然首先我們要保證正確導入了torch包
PyTorch的Autograd模塊實現了深度學習算法中的反向傳播求梯度,在張量(Tensor類)上的所有操作,Autograd都能為它們自動提供微分,簡化了手動計算導數的復雜過程。
在張量創建時,通過設置 requires_grad 標識為Ture來告訴Pytorch需要對該張量進行自動求導,PyTorch會記錄該張量的每一步操作歷史并自動計算。
- 首先我們新建一個張量x
PyTorch會自動追蹤和記錄對于張量的所有操作,當計算完成后調用.backward()方法自動計算梯度并且將計算結果保存到grad屬性中。
- 然后我們另建一個張量y
二、什么是自動求導Autograd
- 1.當涉及到大型神經網絡時,我們都不擅長微積分,這是事實,我們不可能隨時隨地的高效的計算一個很復雜的微積分。因此通過顯式求解數學方程來計算這樣大的復合函數的梯度是不現實的,特別是這些曲線存在于大量的維數中,是無法理解的。這就是PyTorch的Autograd發揮作用的地方。它抽象了復雜的數學,幫助我們“神奇地”計算高維曲線的梯度,只需要幾行代碼。
- 2.每個張量都有一個.grad_fn屬性,如果這個張量是用戶手動創建的那么這個張量的grad_fn是None。
- 3.在張量進行操作后,grad_fn已經被賦予了一個新的函數,這個函數引用了一個創建了這個Tensor類的Function對象。 Tensor和Function互相連接生成了一個非循環圖,它記錄并且編碼了完整的計算歷史。
2.1 簡單的自動求導
- 案例1
- 自動求導
如果Tensor類表示的是一個標量(即它包含一個元素的張量),則不需要為backward()指定任何參數,但是如果它有更多的元素,則需要指定一個gradient參數,它是形狀匹配的張量。 以上的 z.backward()相當于是z.backward(torch.tensor(1.))的簡寫。 這種參數常出現在圖像分類中的單標簽分類,輸出一個標量代表圖像的標簽。
2.2 復雜的自動求導
- 案例2
- 自動求導
我們可以使用with torch.no_grad()上下文管理器臨時禁制對已設置requires_grad = True的張量進行自動求導,這個方法在測試集計算準確率的時候會經常用到。
三、自動求導Autograd過程的簡單解析
為了說明Pytorch的自動求導原理,我們來嘗試分析一下PyTorch的源代碼,雖然Pytorch的 Tensor和 TensorBase都是使用CPP來實現的,但是可以使用一些Python的一些方法查看這些對象在Python的屬性和狀態。 Python的 dir() 函數返回參數的屬性、方法列表。上述案例2中的z是一個Tensor變量,看看里面有哪些成員變量。
dir(z)會打印處一大串東西來,返回很多,我們直接排除掉一些Python中特殊方法(以__開頭和結束的)和私有方法(以_開頭的,直接看幾個比較主要的屬性:
- .is_leaf:記錄是否是葉子節點。通過這個屬性來確定這個變量的類型 在官方文檔中所說的“graph leaves”,“leaf variables”,都是指像x,y這樣的手動創建的、而非運算得到的變量,這些變量稱為創建變量。 像z這樣的,是通過計算后得到的結果稱為結果變量。
- 判斷一個Tensor變量是創建變量還是結果變量可以通過.is_leaf
x是手動創建的沒有通過計算,所以他被認為是一個葉子節點也就是一個創建變量,而z是通過x與y的一系列計算得到的,所以不是葉子結點也就是結果變量。
- grad_fn:每個張量都有一個grad_fn屬性用于保存張量的操作,如果這個張量為用戶自己創建的,則grad_fn為None。
為什么我們執行z.backward()方法的時候會更新x.grad和y.grad呢? .grad_fn屬性記錄的就是這部分的操作,雖然.backward()方法也是CPP實現的,但是可以通過Python來進行簡單的探索。
grad_fn是一個AddBackward0類型的變量 AddBackward0這個類也是用Cpp來寫的,但是我們從名字里就能夠大概知道,他是加法(ADD)的反反向傳播(Backward),看看里面有些什么東西。
- 輸出結果
next_functions就是grad_fn的精華
z.grad_fn.next_functions
next_functions是一個tuple of tuple of PowBackward0 and int。
為什么是2個tuple ? 因為我們的操作是z= x**2+y**3 剛才的AddBackward0是相加,而前面的操作是乘方 PowBackward0。tuple第一個元素就是x相關的操作記錄
- 查看z的grad_fn屬性next_functions的第一個元組的第一個元素
- 輸出結果
- 繼續剖析,查看xg的next_functions
- 輸出結果
在PyTorch的反向圖計算中,AccumulateGrad類型代表的就是葉子節點類型,也就是計算圖終止節點。AccumulateGrad類中有一個.variable屬性指向葉子節點。
x_leaf.variable這個.variable的屬性就是我們的生成的變量x
這樣整個規程就很清晰了:
-
1.當我們執行z.backward()的時候。這個操作將調用z里面的grad_fn這個屬性,執行求導的操作。
-
2.這個操作將遍歷grad_fn的next_functions,然后分別取出里面的Function(AccumulateGrad),執行求導操作。這部分是一個遞歸的過程直到最后類型為葉子節點。
-
3.計算出結果以后,將結果保存到他們對應的variable 這個變量所引用的對象(x和y)的 grad這個屬性里面。
-
4.求導結束。所有的葉節點的grad變量都得到了相應的更新
最終當我們執行完c.backward()之后,a和b里面的grad值就得到了更新。 -
首先我們來剖析一下這個案例,通過這個案例來進一步理解Autograd的工作流程
- 輸出結果
圖片來源:https://zhuanlan.zhihu.com/p/148669484
從計算圖中可以看出,一個張量中:
- data中保存著所存有的數據
- grad中保存這梯度
- requires_grad表示是否開始追蹤所有的操作歷史
Autograd具體工作流程:
x、y是我們自己創建的張量,讓x、y執行相乘操作,生成張量z。
- 1.當我們調用.backward()方法時,該操作會調用張量z中的grad_fn屬性,前面我們說過,grad_fn屬性用來保存張量的操作。如果張量是創建張量,那么該張量的grad_fn屬性為None。
- 2.這個操作將遍歷grad_fn的next_functions,然后分別取出里面的Function(AccumulateGrad),執行求導操作。這部分是一個遞歸的過程直到最后類型為葉子節點。
- 3.計算出結果以后,將結果保存到他們對應的variable 這個變量所引用的對象(x)的 grad屬性里面。
求導結束。所有的葉節點的grad變量都得到了相應的更新。 - 4.最終當我們執行完z.backward()之后,x中的grad值就得到了更新。
四、擴展Autograd
如果需要自定義autograd擴展新的功能,就需要擴展Function類。因為Function使用autograd來計算結果和梯度,并對操作歷史進行編碼。 在Function類中最主要的方法就是forward()和backward()他們分別代表了前向傳播和反向傳播。
一個自定義的Function需要一下三個方法:
-
__init__ (optional):如果這個操作需要額外的參數則需要定義這個Function的構造函數,不需要的話可以忽略。
-
forward():執行前向傳播的計算代碼
-
backward():反向傳播時梯度計算的代碼。 參數的個數和forward返回值的個數一樣,每個參數代表傳回到此操作的梯度。
定義完我們的新操作后,我們來進行測試
a=torch.rand(3,3,requires_grad=True) b=MulConstant.apply(a,5) print("a:"+str(a)) print("b:"+str(b)) # b為a的元素乘以5反向傳播,返回值不是標量,所以backward方法需要參數
b.backward(torch.ones_like(a)) a.grad參考文獻
- https://github.com/zergtant/pytorch-handbook/blob/master/chapter2/2.1.2-pytorch-basics-autograd.ipynb
- https://zhuanlan.zhihu.com/p/148669484
總結
以上是生活随笔為你收集整理的PyTorch基础(二)-----自动求导Autograd的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中秋节公司发了这个(结尾分享红包)
- 下一篇: 互联网晚报 | 12月27日 星期一 |