深度學(xué)習(xí)利器之自動(dòng)微分(1)
深度學(xué)習(xí)利器之自動(dòng)微分(1)
0x00 摘要
本文和下文以 Automatic Differentiation in Machine Learning: a Survey 這篇論文為基礎(chǔ),逐步分析自動(dòng)微分這個(gè)機(jī)器學(xué)習(xí)的基礎(chǔ)利器。
0.1 緣起
筆者計(jì)劃分析 PyTorch 的分布式實(shí)現(xiàn),但是在分析分布式autograd 時(shí)發(fā)現(xiàn),如果不把自動(dòng)微分以及普通 autograd 引擎梳理清楚,分布式autograd的分析就是寸步難行,因此返回頭來學(xué)習(xí),遂有此文。
0.2 自動(dòng)微分
我們知道,深度學(xué)習(xí)框架訓(xùn)練模型的基本流程是:
- 依據(jù)模型搭建計(jì)算圖。
- 依據(jù)輸入計(jì)算損失函數(shù)。
- 計(jì)算損失函數(shù)對(duì)模型參數(shù)的導(dǎo)數(shù)。
- 根據(jù)得出的導(dǎo)數(shù),利用梯度下降法等方法來反向更新模型參數(shù),讓損失函數(shù)最小化。
搭建計(jì)算圖(依賴關(guān)系)和計(jì)算損失函數(shù)的過程,稱為"正向傳播”,這個(gè)是依據(jù)用戶模型完成,本質(zhì)上是用戶自己處理。而依據(jù)損失函數(shù)求導(dǎo)的過程,稱為"反向傳播”,這個(gè)對(duì)于用戶來說太繁重,所以各種深度學(xué)習(xí)框架都提供了自動(dòng)求導(dǎo)功能。深度學(xué)習(xí)框架幫助我們解決的核心問題就是兩個(gè):
- 反向傳播時(shí)的自動(dòng)梯度計(jì)算和更新。
- 使用 GPU 進(jìn)行計(jì)算。
于是這就牽扯出來自動(dòng)梯度計(jì)算這個(gè)概念。
在數(shù)學(xué)與計(jì)算代數(shù)學(xué)中,自動(dòng)微分或者自動(dòng)求導(dǎo)(Automatic Differentiation,簡稱AD)也被稱為微分算法或數(shù)值微分。它是一種數(shù)值計(jì)算的方式,其功能是計(jì)算復(fù)雜函數(shù)(多層復(fù)合函數(shù))在某一點(diǎn)處的導(dǎo)數(shù),梯度,Hessian矩陣值等等。
0x01 基本概念
為了行文完整,我們首先要介紹一些基本概念或者思想,可能部分概念大家已經(jīng)熟知,請(qǐng)直接跳到第二章。
1.1 機(jī)器學(xué)習(xí)
赫伯特·西蒙(Herbert Simon,1975年圖靈獎(jiǎng)獲得者、1978年諾貝爾經(jīng)濟(jì)學(xué)獎(jiǎng)獲得者)對(duì)"學(xué)習(xí)”下過一個(gè)定義:"如果一個(gè)系統(tǒng),能夠通過執(zhí)行某個(gè)過程,就此改進(jìn)了它的性能,那么這個(gè)過程就是學(xué)習(xí)”。
所以說,機(jī)器學(xué)習(xí)就是從經(jīng)驗(yàn)數(shù)據(jù)中學(xué)習(xí),提取數(shù)據(jù)中的重要的模式和趨勢(shì),從而改進(jìn)預(yù)估函數(shù)(有關(guān)特定輸入和預(yù)期輸出的功能函數(shù))的性能。
比如:一個(gè)函數(shù)可以用來區(qū)分貓和狗,我們需要使用大量的訓(xùn)練數(shù)據(jù)來挖掘培養(yǎng)這個(gè)函數(shù),改進(jìn)其性能。
1.2 深度學(xué)習(xí)
傳統(tǒng)機(jī)器學(xué)習(xí)使用知識(shí)和經(jīng)驗(yàn)從原始數(shù)據(jù)中提取各種特征進(jìn)行訓(xùn)練。提取特征就是機(jī)器學(xué)習(xí)的重要組成部分:特征工程。因?yàn)樵紨?shù)據(jù)涉及到的特征數(shù)目太龐大,而且特征種類千差萬別,所以特征工程是個(gè)極具挑戰(zhàn)的部分。深度學(xué)習(xí)則是讓神經(jīng)網(wǎng)絡(luò)自己學(xué)習(xí)/提取數(shù)據(jù)的各種特征,或者通過組合各種底層特征來形成一些高層特征。
1.3 損失函數(shù)
對(duì)于機(jī)器學(xué)習(xí)的功能函數(shù),我們給定一個(gè)輸入,會(huì)得到一個(gè)輸出,比如輸入貓,輸出"是否為貓”。但是這個(gè)實(shí)際輸出值可能與我們的預(yù)期值不一樣。因此,我們需要構(gòu)建一個(gè)評(píng)估體系,來辨別函數(shù)的好壞,這就引出了損失函數(shù)。
損失函數(shù)(loss function) 或者是代價(jià)函數(shù)(cost function)就是用來度量預(yù)期輸出(真實(shí)值)和實(shí)際輸出(預(yù)測(cè)值)的"落差”程度 或者說是精確度。
損失函數(shù)可以把模型擬合程度量化成一個(gè)函數(shù)值,如果我們選取不同的模型參數(shù),則損失函數(shù)值會(huì)做相應(yīng)的改變。損失函數(shù)值越小,說明 實(shí)際輸出 和預(yù)期輸出 的差值就越小,也就表明構(gòu)建的模型精確度就越高。比如常見的均方誤差(Mean Squared Error)損失函數(shù),從幾何意義上來說,它可以看成預(yù)測(cè)值和實(shí)際值的平均距離的平方。
1.4 權(quán)重和偏置
損失函數(shù)里一般有兩種參數(shù):
-
我們把神經(jīng)元與神經(jīng)元之間的影響程度叫作為權(quán)重(weight)。權(quán)重的大小就是連接的強(qiáng)弱。它通知下一層相鄰神經(jīng)元更應(yīng)該關(guān)注哪些輸入信號(hào)量。
-
除了連接權(quán)值,神經(jīng)元內(nèi)部還有一個(gè)施加于自身的特殊權(quán)值,叫偏置(bias)。偏置用來調(diào)整函數(shù)與真實(shí)值距離的偏差。偏置能使輔助神經(jīng)元是否更容易被激活。也就是說,它決定神經(jīng)元的連接加權(quán)和得有多大,才能讓激發(fā)變得有意義。
神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的設(shè)計(jì)目的在于,讓神經(jīng)網(wǎng)絡(luò)以"更佳”的性能來學(xué)習(xí)。而這里的所謂"學(xué)習(xí)”,就是不斷調(diào)整權(quán)重和偏置,從而找到神經(jīng)元之間最合適的權(quán)重和偏置,讓損失函數(shù)的值達(dá)到最小。
1.5 導(dǎo)數(shù)和梯度
神經(jīng)網(wǎng)絡(luò)的特征之一,就是從數(shù)據(jù)樣本中學(xué)習(xí)。也就是說,可以由訓(xùn)練數(shù)據(jù)來自動(dòng)確定網(wǎng)絡(luò)權(quán)值參數(shù)的值。
既然我們有了損失函數(shù)這個(gè)評(píng)估體系,那么就可以利用其來反向調(diào)整網(wǎng)絡(luò)中權(quán)重,使得損失最小,即如果某些權(quán)重使得損失函數(shù)達(dá)到最小值,這些權(quán)重就是我們尋找的最理想?yún)?shù)。
假設(shè)損失函數(shù)為 y = f(x),我們尋找其最小值,就是求這個(gè)函數(shù)的極值點(diǎn),那么就是求其一階導(dǎo)數(shù) f'(x) = 0 這個(gè)微分方程的解。但是計(jì)算機(jī)不擅長求微分方程,所以只能通過插值等方法進(jìn)行海量嘗試,把函數(shù)的極值點(diǎn)求出來。
什么是導(dǎo)數(shù)呢?
所謂導(dǎo)數(shù),就是用來分析函數(shù)"變化率”的一種度量。針對(duì)函數(shù)中的某個(gè)特定點(diǎn) x0,該點(diǎn)的導(dǎo)數(shù)就是x0點(diǎn)的"瞬間斜率”,也即切線斜率。
什么是梯度呢?
梯度的本意是一個(gè)向量(矢量),表示某一函數(shù)在該點(diǎn)處的方向?qū)?shù)沿著該方向取得最大值,即函數(shù)在該點(diǎn)處沿著該方向(此梯度的方向)變化最快,變化率最大(為該梯度的模)。
在單變量的實(shí)值函數(shù)中,對(duì)于函數(shù)的某個(gè)特定點(diǎn),它的梯度方向就表示從該點(diǎn)出發(fā),函數(shù)值增長最為迅猛的方向或者說是函數(shù)導(dǎo)數(shù)變化率最大的方向。
對(duì)于機(jī)器學(xué)習(xí)/深度學(xué)習(xí)來說,梯度方向就是損失函數(shù)變化最快的方向,因?yàn)槲覀兿M麚p失最小,所以我們就通常利用數(shù)值微分來計(jì)算神經(jīng)網(wǎng)絡(luò)權(quán)值參數(shù)的梯度,按照梯度下降來確定調(diào)參的方向,按照這個(gè)方向來優(yōu)化。
1.6 梯度下降
梯度下降的大致思路是:首先給參數(shù) w, b 隨機(jī)設(shè)定一些初值,然后采用迭代的算法,計(jì)算當(dāng)前網(wǎng)絡(luò)的輸出,然后根據(jù)網(wǎng)絡(luò)輸出與預(yù)期輸出之間的差值,反方向地去改變前面各層的參數(shù),直至網(wǎng)絡(luò)收斂穩(wěn)定。
具體來說,就是:
- 給參數(shù) w, b 隨機(jī)設(shè)定一些初值。
- for i = 0 to 訓(xùn)練數(shù)據(jù)的個(gè)數(shù):
- 根據(jù)參數(shù) w, b 這些初值來計(jì)算當(dāng)前網(wǎng)絡(luò)對(duì)于第 i 個(gè)訓(xùn)練數(shù)據(jù)的輸出
- 根據(jù)網(wǎng)絡(luò)輸出與預(yù)期輸出之間的差值,得到一個(gè)權(quán)重 w 和偏差 b 相對(duì)于損失函數(shù)的梯度。
- 最終,針對(duì)每一個(gè)訓(xùn)練數(shù)據(jù),都得到一個(gè)權(quán)重和偏差的梯度值。
- 把各個(gè)樣本數(shù)據(jù)的權(quán)重梯度加起來,計(jì)算所有樣本的權(quán)重梯度的平均值 \(\nabla w\)。
- 把各個(gè)樣本數(shù)據(jù)的偏差梯度加起來,計(jì)算所有樣本的偏差梯度的平均值 \(\nabla b\)。
- 更新權(quán)重值和偏差值:
- w = w - \(\nabla w\)
- b = b - \(\nabla b\)
- 返回 2,繼續(xù)迭代,直至網(wǎng)絡(luò)收斂。
? 當(dāng)然,梯度下降有很多優(yōu)化方法,具體邏輯各有不同。
1.7 反向傳播
說到back propagation算法,我們通常強(qiáng)調(diào)的是反向傳播。其實(shí)在本質(zhì)上,它是一個(gè)雙向算法。也就是說,它其實(shí)是分兩大步走:
前向傳播:把批量數(shù)據(jù)送入網(wǎng)絡(luò),計(jì)算&正向傳播輸入信息(就是把一系列矩陣通過激活函數(shù)的加工,一層一層的向前"蔓延”,直到抵達(dá)輸出層),最終輸出的預(yù)測(cè)值與真實(shí)label比較之后,用損失函數(shù)計(jì)算出此次迭代的損失,其關(guān)注點(diǎn)是輸入怎么影響到每一層。
反向傳播:反向傳播誤差從網(wǎng)絡(luò)最后端開始進(jìn)入到網(wǎng)絡(luò)模型中之前的每一層,根據(jù)鏈?zhǔn)角髮?dǎo),調(diào)整網(wǎng)絡(luò)權(quán)重和偏置,最終逐步調(diào)整權(quán)重,使得最終輸出與期望輸出的差距達(dá)到最小,其關(guān)注點(diǎn)是每一層怎么影響到最終結(jié)果。
具體如下圖:

我們可以看到,這個(gè)圖中涉及到了大量的梯度計(jì)算,于是又涉及到一個(gè)問題:這些梯度如何計(jì)算?深度學(xué)習(xí)框架,幫助我們解決的核心問題就是兩個(gè):
- 反向傳播時(shí)的自動(dòng)梯度計(jì)算和更新,也被稱作自動(dòng)微分。
- 使用 GPU 進(jìn)行計(jì)算。
1.8 可微分編程
1.8.1 可微分編程永生
Yann Lecun在其博文 "深度學(xué)習(xí)已死,可微分編程永生” 之中提到:
"深度學(xué)習(xí)本質(zhì)上是一種新的編程方式——可微分編程——而且這個(gè)領(lǐng)域正試圖用這種方式來制定可重用的結(jié)構(gòu)。目前我們已經(jīng)有:卷積,池化,LSTM,GAN,VAE,memory單元,routing單元,等等。”
但重要的一點(diǎn)是,人們現(xiàn)在正在將各種參數(shù)化函數(shù)模塊的網(wǎng)絡(luò)組裝起來,構(gòu)建一種新的軟件,并且使用某種基于梯度的優(yōu)化來訓(xùn)練這些軟件。
越來越多的人正在以一種依賴于數(shù)據(jù)的方式(循環(huán)和條件)來程序化地定義網(wǎng)絡(luò),讓它們隨著輸入數(shù)據(jù)的動(dòng)態(tài)變化而變化。這與是普通的程序非常類似,除了前者是參數(shù)化的、可以自動(dòng)可微分,并且可以訓(xùn)練和優(yōu)化。動(dòng)態(tài)網(wǎng)絡(luò)變得越來越流行(尤其是對(duì)于NLP而言),這要?dú)w功于PyTorch和Chainer等深度學(xué)習(xí)框架(注意:早在1994年,以前的深度學(xué)習(xí)框架Lush,就能處理一種稱為Graph Transformer Networks的特殊動(dòng)態(tài)網(wǎng)絡(luò),用于文本識(shí)別)。

1.8.2 深度學(xué)習(xí)成功的關(guān)鍵
MIT媒體實(shí)驗(yàn)室的David Dalrymple 也介紹過可微分編程。Dalrymple認(rèn)為,深度學(xué)習(xí)的成功有兩大關(guān)鍵,一是反向傳播,二是權(quán)重相關(guān)(weight-tying),這兩大特性與函數(shù)編程(functional programing)中調(diào)用可重用函數(shù)十分相同。可微分編程有成為"timeless”的潛力。
反向傳播以非常優(yōu)雅的方式應(yīng)用了鏈?zhǔn)揭?guī)則(一個(gè)簡單的微積分技巧),從而把連續(xù)數(shù)學(xué)和離散數(shù)學(xué)進(jìn)行了深度整合,使復(fù)雜的潛在解決方案族可以通過向量微積分自主改進(jìn)。
反向傳播的關(guān)鍵是將潛在解決方案的模式(template)組織為一個(gè)有向圖。通過反向遍歷這個(gè)圖,算法能夠自動(dòng)計(jì)算"梯度向量”,而這個(gè)"梯度向量" 能引導(dǎo)算法尋找越來越好的解決方案。
權(quán)重相關(guān)(weight-tying)是第二個(gè)關(guān)鍵之處,它使得同一個(gè)權(quán)重相關(guān)的網(wǎng)絡(luò)組件可以同時(shí)在多個(gè)地方被使用,組件的每個(gè)副本都保持一致。Weight-tying 會(huì)使網(wǎng)絡(luò)學(xué)習(xí)到更加泛化的能力,因?yàn)閱卧~或者物體可能出現(xiàn)在文本塊或圖像的多個(gè)位置。權(quán)重相關(guān)(weight-tied)的組件,實(shí)際上與編程中可重用函數(shù)的概念相同(就類似于你編寫一個(gè)函數(shù),然后在程序中多個(gè)地方都進(jìn)行調(diào)用),而且對(duì)組件的重用方式也與函數(shù)編程中通用的"高階函數(shù)”生成的方式完全一致。
1.8.3 可微分編程
可微分編程是一個(gè)比較新的概念,是反向傳播和weight-tying的延伸。用戶僅指定了函數(shù)的結(jié)構(gòu)以及其調(diào)用順序,函數(shù)程序?qū)嶋H上被編譯成類似于反向傳播所需的計(jì)算圖。圖的各個(gè)組成部分也必須是可微的,可微分編程把實(shí)現(xiàn)/部署的細(xì)節(jié)留給優(yōu)化器——語言會(huì)使用反向傳播根據(jù)整個(gè)程序的目標(biāo)自動(dòng)學(xué)習(xí)細(xì)節(jié),基于梯度進(jìn)行優(yōu)化,就像優(yōu)化深度學(xué)習(xí)中的權(quán)重一樣。
特斯拉人工智能部門主管Andrej Karpathy也提出過一個(gè)"軟件2.0”概念。
軟件1.0(Software 1.0)是用Python、C++等語言編寫,由對(duì)計(jì)算機(jī)的明確指令組成。通過編寫每行代碼,程序員可以確定程序空間中的某個(gè)特定點(diǎn)。
Software 2.0 是用神經(jīng)網(wǎng)絡(luò)權(quán)重編寫的。沒有人參與這段代碼的編寫。在軟件2.0的情況下,人類對(duì)一個(gè)理想程序的行為指定一些約束(例如,輸入輸出數(shù)據(jù)集),并依據(jù)可用的計(jì)算資源來搜索程序空間中滿足約束條件的程序。在這個(gè)空間中,搜索過程可以利用反向傳播和隨機(jī)梯度下降滿足要求。
Karpathy認(rèn)為,在現(xiàn)實(shí)世界中,大部分問題都是收集數(shù)據(jù)比明確地編寫程序更容易。未來,大部分程序員不再需要維護(hù)復(fù)雜的軟件庫,編寫復(fù)雜的程序,或者分析程序運(yùn)行時(shí)間。他們需要做的是收集、整理、操作、標(biāo)記、分析和可視化提供給神經(jīng)網(wǎng)絡(luò)的數(shù)據(jù)。
綜上,既然知道了自動(dòng)計(jì)算梯度的重要性,我們下面就來借助一篇論文 Automatic Differentiation in Machine Learning: a Survey 來具體學(xué)習(xí)一下。
0x02 微分方法
2.1 常見方法
我們首先看看微分的幾種比較常用的方法:
- 手動(dòng)求解法(Manual Differentiation) : 完全手動(dòng)完成,依據(jù)鏈?zhǔn)椒▌t解出梯度公式,帶入數(shù)值,得到梯度。
- 數(shù)值微分法(Numerical Differentiation) :利用導(dǎo)數(shù)的原始定義,直接求解微分值。
- 符號(hào)微分法(Symbolic Differentiation) : 利用求導(dǎo)規(guī)則對(duì)表達(dá)式進(jìn)行自動(dòng)計(jì)算,其計(jì)算結(jié)果是導(dǎo)函數(shù)的表達(dá)式而非具體的數(shù)值。即,先求解析解,然后轉(zhuǎn)換為程序,再通過程序計(jì)算出函數(shù)的梯度。
- 自動(dòng)微分法(Automatic Differentiation) :介于數(shù)值微分和符號(hào)微分之間的方法,采用類似有向圖的計(jì)算來求解微分值。
具體如下圖:

2.2 手動(dòng)微分
手動(dòng)微分就是對(duì)每一個(gè)目標(biāo)函數(shù)都需要利用求導(dǎo)公式手動(dòng)寫出求導(dǎo)公式,然后依照公式編寫代碼,帶入數(shù)值,求出最終梯度。
這種方法準(zhǔn)確有效,但是不適合工程實(shí)現(xiàn),因?yàn)橥ㄓ眯院挽`活性很差,每一次我們修改算法模型,都要修改對(duì)應(yīng)的梯度求解算法。如果模型復(fù)雜或者項(xiàng)目頻繁反復(fù)迭代,那么算法工程師 別說 996 了,就是 365 x 24 也頂不住。
2.3 數(shù)值微分
數(shù)值微分方式應(yīng)該是最直接而且簡單的一種自動(dòng)求導(dǎo)方式。從導(dǎo)數(shù)的原始定義中,我們可以直觀看到前向差分公式為:

當(dāng)h取很小的數(shù)值,比如0.000001 時(shí),導(dǎo)數(shù)是可以利用差分來近似計(jì)算出來的。只需要給出函數(shù)值以及自變量的差值,數(shù)值微分算法就可計(jì)算出導(dǎo)數(shù)值。單側(cè)差分公式根據(jù)導(dǎo)數(shù)的定義直接近似計(jì)算某一點(diǎn)處的導(dǎo)數(shù)值。
數(shù)值微分的優(yōu)點(diǎn)是:
- 上面的計(jì)算式幾乎適用所有情況,除非該點(diǎn)不可導(dǎo),
- 實(shí)現(xiàn)簡單。
- 對(duì)用戶隱藏求解過程。
但是,數(shù)值微分有幾個(gè)問題:
- 計(jì)算量太大,求解速度是這幾種方法中最慢的,尤其是當(dāng)參數(shù)多的時(shí)候,因?yàn)橐驗(yàn)槊坑?jì)算一個(gè)參數(shù)的導(dǎo)數(shù),你都需要重新計(jì)算f(x+h)。
- 因?yàn)槭菙?shù)值逼近,所有會(huì)不可靠,不穩(wěn)定的情況,無法獲得一個(gè)相對(duì)準(zhǔn)確的導(dǎo)數(shù)值。如果 h 選取不當(dāng),可能會(huì)得到與符號(hào)相反的結(jié)果,導(dǎo)致誤差增大。尤其是兩個(gè)嚴(yán)重問題:
- 截?cái)噱e(cuò)誤(Truncation error):在數(shù)值計(jì)算中 h 無法真正取零導(dǎo)致的近似誤差。
- 舍入誤差(Roundoff Error):在計(jì)算過程中出現(xiàn)的對(duì)小數(shù)位數(shù)的不斷舍入會(huì)導(dǎo)致求導(dǎo)過程中的誤差不斷累積。
為了緩解截?cái)噱e(cuò)誤,人們提出了中心微分近似(center difference approximation),這方法仍然無法解決舍入誤差,只是減少誤差,但是它比單側(cè)差分公式有更小的誤差和更好的穩(wěn)定性。具體公式如下:

雖然數(shù)值微分有一些缺點(diǎn),但是好處是簡單實(shí)現(xiàn),所以可以用來校驗(yàn)其他算法所得到梯度的正確性,比如"gradient check"就是利用數(shù)值微分法。
2.4 符號(hào)微分
符號(hào)微分(Symbolic Differentiation)屬符號(hào)計(jì)算的范疇,利用求導(dǎo)規(guī)則對(duì)表達(dá)式進(jìn)行自動(dòng)計(jì)算,其計(jì)算結(jié)果是導(dǎo)函數(shù)的表達(dá)式。符號(hào)計(jì)算用于求解數(shù)學(xué)中的公式解(也稱解析解),得到的是 解的表達(dá)式而非具體的數(shù)值。
符號(hào)微分適合符號(hào)表達(dá)式的自動(dòng)求導(dǎo),符號(hào)微分的原理是用下面的簡單求導(dǎo)規(guī)則替代手動(dòng)微分:

符號(hào)微分利用代數(shù)軟件,實(shí)現(xiàn)微分的一些公式,然后根據(jù)基本函數(shù)的求導(dǎo)公式以及四則運(yùn)算、復(fù)合函數(shù)的求導(dǎo)法則,將公式的計(jì)算過程轉(zhuǎn)化成微分過程,這樣就可以對(duì)用戶提供的具有closed form的數(shù)學(xué)表達(dá)式進(jìn)行"自動(dòng)微分"求解。就是先求解析解,然后轉(zhuǎn)換為程序,再通過程序計(jì)算出函數(shù)的梯度。
符號(hào)微分計(jì)算出的表達(dá)式需要用字符串或其他數(shù)據(jù)結(jié)構(gòu)存儲(chǔ),如表達(dá)式樹。數(shù)學(xué)軟件如Mathematica,Maple,matlab中實(shí)現(xiàn)了這種技術(shù)。python語言的符號(hào)計(jì)算庫也提供了這類算法。
符號(hào)微分的問題是:
- 表達(dá)式必須是closed form的數(shù)學(xué)表達(dá)式,也就是必須能寫成完整數(shù)學(xué)表達(dá)式的,不能有編程語言中的循環(huán)結(jié)構(gòu),條件結(jié)構(gòu)等。這樣才能將整個(gè)問題轉(zhuǎn)換為一個(gè)純數(shù)學(xué)符號(hào)問題,從而利用一些代數(shù)軟件進(jìn)行符號(hào)微分求解。
- 表達(dá)式復(fù)雜時(shí)候(深層復(fù)合函數(shù),如神經(jīng)網(wǎng)絡(luò)的映射函數(shù)),因?yàn)橛?jì)算機(jī)也許并不能進(jìn)行智能的簡化,所以容易出現(xiàn)"表達(dá)式膨脹”(expression swell)的問題。
表達(dá)式膨脹如下圖所示,稍不注意,符號(hào)微分求解就會(huì)如下中間列所示,表達(dá)式急劇膨脹,導(dǎo)致問題求解也隨著變慢,計(jì)算上的冗余且成本高昂:

其實(shí),對(duì)于機(jī)器學(xué)習(xí)中的應(yīng)用,不需要得到導(dǎo)數(shù)的表達(dá)式,而只需計(jì)算函數(shù)在某一點(diǎn)處的導(dǎo)數(shù)值。
2.5 自動(dòng)微分
2.5.1 中間方法
自動(dòng)微分是介于數(shù)值微分和符號(hào)微分之間的方法,采用類似有向圖的計(jì)算來求解微分值。
-
數(shù)值微分:一開始就直接代入數(shù)值近似求解。
-
符號(hào)微分:直接對(duì)代數(shù)表達(dá)式求解析解,最后才代入數(shù)值進(jìn)行計(jì)算。
-
自動(dòng)微分:首先對(duì)基本算子(函數(shù))應(yīng)用符號(hào)微分方法,其次帶入數(shù)值進(jìn)行計(jì)算,保留中間結(jié)果,最后通過鏈?zhǔn)角髮?dǎo)法將中間結(jié)果應(yīng)用于整個(gè)函數(shù),這樣可以做到完全向用戶隱藏微分求解過程,也可以靈活于編程語言的循環(huán)結(jié)構(gòu)、條件結(jié)構(gòu)等結(jié)合起來。
關(guān)于解析解我們還要做一些說明。幾乎所有機(jī)器學(xué)習(xí)算法在訓(xùn)練或預(yù)測(cè)時(shí)都可以歸結(jié)為求解最優(yōu)化問題,如果目標(biāo)函數(shù)可導(dǎo),則問題就變?yōu)榍笥?xùn)練函數(shù)的駐點(diǎn)。但是通常情況下我們無法得到駐點(diǎn)的解析解,因此只能采用數(shù)值優(yōu)化算法,如梯度下降法,牛頓法,擬牛頓法等等。這些數(shù)值優(yōu)化算法都依賴于函數(shù)的一階導(dǎo)數(shù)值或二階導(dǎo)數(shù)值(包括梯度與Hessian矩陣)。因此需要解決如何求一個(gè)復(fù)雜函數(shù)的導(dǎo)數(shù)問題,自動(dòng)微分技術(shù)是解決此問題的一種通用方法。
由于自動(dòng)微分法只對(duì)基本函數(shù)或常數(shù)運(yùn)用符號(hào)微分法則,所以它可以靈活結(jié)合編程語言的循環(huán)結(jié)構(gòu),條件結(jié)構(gòu)等。使用自動(dòng)微分和不使用自動(dòng)微分對(duì)代碼總體改動(dòng)非常小,由于它實(shí)際是一種圖計(jì)算,可以對(duì)其做很多優(yōu)化,所以該方法在現(xiàn)代深度學(xué)習(xí)系統(tǒng)中得到廣泛應(yīng)用。
2.5.2 數(shù)學(xué)基礎(chǔ)
自動(dòng)微分 (AD)是用程序來自動(dòng)化推導(dǎo)Jacobian矩陣或者其中的一部分,是計(jì)算因變量對(duì)某個(gè)自變量導(dǎo)數(shù)的一種數(shù)值計(jì)算方式,所以其數(shù)學(xué)基礎(chǔ)是鏈?zhǔn)角髮?dǎo)法則和雅克比矩陣。
2.5.2.1 鏈?zhǔn)角髮?dǎo)
在計(jì)算鏈?zhǔn)椒▌t之前,我們先回顧一下復(fù)合函數(shù)。復(fù)合函數(shù)在本質(zhì)上就是有關(guān)函數(shù)的函數(shù)(function of functions)。它將一個(gè)函數(shù)的返回值作為參數(shù)傳遞給另一個(gè)函數(shù),并且將另一個(gè)函數(shù)的返回值作為參數(shù)再傳遞給下一個(gè)函數(shù),也就是 函數(shù)套函數(shù),把幾個(gè)簡單的函數(shù)復(fù)合為一個(gè)較為復(fù)雜的函數(shù)。
鏈?zhǔn)椒▌t是微積分中的求導(dǎo)法則,用于求一個(gè)復(fù)合函數(shù)的導(dǎo)數(shù),是在微積分的求導(dǎo)運(yùn)算中一種常用的方法。復(fù)合函數(shù)的導(dǎo)數(shù)將是構(gòu)成復(fù)合這有限個(gè)函數(shù)在相應(yīng)點(diǎn)的 導(dǎo)數(shù)的乘積,就像鎖鏈一樣一環(huán)套一環(huán),故稱鏈?zhǔn)椒▌t。
比如求導(dǎo):
鏈?zhǔn)角髮?dǎo),令:
則
即可求導(dǎo)。
2.5.2.2 雅克比矩陣
在向量微積分中,雅可比矩陣是一階偏導(dǎo)數(shù)以一定方式排列成的矩陣,其行列式稱為雅可比行列式。雅可比矩陣的重要性在于它體現(xiàn)了一個(gè)可微方程與給出點(diǎn)的最優(yōu)線性逼近。
雅可比矩陣表示兩個(gè)向量所有可能的偏導(dǎo)數(shù)。它是一個(gè)向量相對(duì)于另一個(gè)向量的梯度,其實(shí)現(xiàn)的是 n維向量 到 m 維向量的映射。
在矢量運(yùn)算中,雅克比矩陣是基于函數(shù)對(duì)所有變量一階偏導(dǎo)數(shù)的數(shù)值矩陣,當(dāng)輸入個(gè)數(shù) = 輸出個(gè)數(shù)時(shí)又稱為雅克比行列式。
假設(shè)輸入向量 \(x∈Rn\),而輸出向量 \(y∈Rm\),則Jacobian矩陣定義為:

0xFF 參考
Yann LeCun:深度學(xué)習(xí)已死,可微分編程萬歲!
TensorFlow可微編程實(shí)踐2---自動(dòng)微分符號(hào)體系
https://en.wikipedia.org/wiki/Automatic_differentiation
Pytorch學(xué)習(xí)2020春-1-線性回歸
自動(dòng)微分(Automatic Differentiation)
自動(dòng)微分(Automatic Differentiation)簡介——tensorflow核心原理
【深度學(xué)習(xí)之美01】什么是(機(jī)器/深度)學(xué)習(xí)?
【深度學(xué)習(xí)之美15】如何感性認(rèn)識(shí)損失函數(shù)?
【深度學(xué)習(xí)之美21】BP算法詳解之前向傳播
【深度學(xué)習(xí)之美22】BP算法詳解之鏈?zhǔn)椒▌t
【深度學(xué)習(xí)之美23】BP算法詳解之反向傳播
【深度學(xué)習(xí)理論】一文搞透梯度下降Gradient descent
梯度下降算法(Gradient Descent)的原理和實(shí)現(xiàn)步驟
【深度學(xué)習(xí)理論】純公式手推+代碼擼——神經(jīng)網(wǎng)絡(luò)的反向傳播+梯度下降
神經(jīng)網(wǎng)絡(luò)中 BP 算法的原理與 Python 實(shí)現(xiàn)源碼解析
機(jī)器學(xué)習(xí)之——自動(dòng)求導(dǎo)
Automatic Differentiation in Machine Learning: a Survey
自動(dòng)微分(Automatic Differentiation)簡介
Automatic Differentiation in Machine Learning: a Survey
自動(dòng)微分(Automatic Differentiation)簡介
PyTorch 的 backward 為什么有一個(gè) grad_variables 參數(shù)?
BACKPACK: PACKING MORE INTO BACKPROP
微分編程(一):傳統(tǒng)自動(dòng)微分的三宗罪
【深度學(xué)習(xí)理論】一文搞透pytorch中的tensor、autograd、反向傳播和計(jì)算圖
[PyTorch 學(xué)習(xí)筆記] 1.5 autograd 與邏輯回歸
OpenMMLab:PyTorch 源碼解讀之 torch.autograd:梯度計(jì)算詳解
https://zhuanlan.zhihu.com/p/348555597)
AI 框架基礎(chǔ)技術(shù)之自動(dòng)求導(dǎo)機(jī)制 (Autograd)
自動(dòng)微分(Automatic Differentiation)簡介
TensorFlow可微編程實(shí)踐1---自動(dòng)微分簡介
浙公網(wǎng)安備 33010602011771號(hào)