網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
1 自動(dòng)微分
我們?cè)凇稊?shù)值分析》課程中已經(jīng)學(xué)過許多經(jīng)典的數(shù)值微分方法。許多經(jīng)典的數(shù)值微分算法非常快,因?yàn)樗鼈冎恍枰?jì)算差商。然而,他們的主要缺點(diǎn)在于他們是數(shù)值的,這意味著有限的算術(shù)精度和不精確的函數(shù)求值,而這些都從根本上限制了求解結(jié)果的質(zhì)量。因此。充滿噪聲的、復(fù)雜多變的函數(shù)很難得到精準(zhǔn)的數(shù)值微分。
自動(dòng)微分技術(shù)(稱為“automatic differentiation, autodiff”)是介于符號(hào)微分和數(shù)值微分的一種技術(shù),它是在計(jì)算效率和計(jì)算精度之間的一種折衷。自動(dòng)微分不受任何離散化算法誤差的約束,它充分利用了微分的鏈?zhǔn)椒▌t和其他關(guān)于導(dǎo)數(shù)的性質(zhì)來準(zhǔn)確地計(jì)算它們。
2 前向自動(dòng)微分
我們先來計(jì)算簡(jiǎn)單的前向自動(dòng)微分。假設(shè)我們有兩個(gè)變量u和v,使用浮點(diǎn)數(shù)存儲(chǔ)。我們將變量u′=du/dt和v′=dv/dt和這些變量一起存儲(chǔ),這里tt是獨(dú)立的變量。在一些程序設(shè)計(jì)語(yǔ)言(如Python)中,我們可以選擇定義一種新的數(shù)據(jù)類型來存儲(chǔ)[u,u′]和[v,v′]這類數(shù)對(duì)。我們可以在這些數(shù)對(duì)上定義一種代數(shù)運(yùn)算,這些代數(shù)運(yùn)算編碼了一些經(jīng)典的操作:
在進(jìn)行前向自動(dòng)微分之前,我們需要先將計(jì)算f(t)所產(chǎn)生的操作序列表示為計(jì)算圖。接著,采用自底向上的遞推算法的思想,從做為遞推起點(diǎn)的數(shù)對(duì)t≡[t0,1](因?yàn)閐t/dt=1)開始,我們能夠按照我們上述編碼規(guī)則同時(shí)對(duì)函數(shù)f(t)和它的導(dǎo)數(shù)f′(t)進(jìn)行求值。我們?cè)诰幊陶Z(yǔ)言中可以選擇令數(shù)對(duì)重載運(yùn)算符,這樣額外的求導(dǎo)數(shù)運(yùn)算就可以對(duì)用戶透明地執(zhí)行了。
例1?比如,對(duì)于函數(shù)f(x)=exp?(x2?x)/x,想要依次計(jì)算dyi/dx(這里yi為所有計(jì)算中間項(xiàng))。則我們先從x開始將表達(dá)式分解為計(jì)算圖:
然后前向遞推地按照我們之前所述的編碼規(guī)則來進(jìn)行求導(dǎo)
注意鏈?zhǔn)椒▌t(chain rule)告訴我們:
(f(g(x)))′=f′(g(x))?g′(x)
所以我們對(duì)
yk=g(yi)
有
y′k=g′(yi)?yi′
事實(shí)上,我們也能夠處理有多個(gè)輸入的函數(shù)g:
k=g(yi,?,yj)
多元微分鏈?zhǔn)椒▌t如下:
比如,對(duì)于
我們有
下面展示了一個(gè)對(duì)二元函數(shù)模擬前向自動(dòng)微分的過程。
例2?設(shè)(x1,x2)=x1?exp?(x2)?x1,模擬前向微分過程。
接下來我們看如何用Python代碼來實(shí)現(xiàn)單變量函數(shù)的前向自動(dòng)微分過程。為了簡(jiǎn)便起見,我們下面只編碼了幾個(gè)常用的求導(dǎo)規(guī)則。
import math
class Var:
def __init__(self, val, deriv=1.0):
self.val = val
self.deriv = deriv
def __add__(self, other):
if isinstance(other, Var):
val = self.val + other.val
deriv = self.deriv + other.deriv
else:
val = self.val + other
deriv = self.deriv
return Var(val, deriv)
def __radd__(self, other):
return self + other
def __sub__(self, other):
if isinstance(other, Var):
val = self.val - other.val
deriv = self.deriv - other.deriv
else:
val = self.val - other
deriv = self.deriv
return Var(val, deriv)
def __rsub__(self, other):
val = other - self.val
deriv = - self.deriv
return Var(val, deriv)
def __mul__(self, other):
if isinstance(other, Var):
val = self.val * other.val
deriv = self.val * other.deriv + self.deriv * other.val
else:
val = self.val * other
deriv = self.deriv * other
return Var(val, deriv)
def __rmul__(self, other):
return self * other
def __truediv__(self, other):
if isinstance(other, Var):
val = self.val / other.val
deriv = (self.deriv * other.val - self.val * other.deriv)/other.val**2
else:
val = self.val / other
deriv = self.deriv / other
return Var(val, deriv)
def __rtruediv__(self, other):
val = other / self.val
deriv = other * 1/self.val**2
return Var(val, deriv)
def __repr__(self):
return "value: {}\t gradient: {}".format(self.val, self.deriv)
def exp(f: Var):
return Var(math.exp(f.val), math.exp(f.val) * f.deriv)
例如,我們?nèi)魢L試計(jì)算函數(shù)f(x)=exp?(x2?x)/x在x=2.0處的導(dǎo)數(shù)f′(2.0)如下:
fx = lambda x: exp(x*x - x)/x
df = fx(Var(2.0))
print(df)
打印輸出:
value: 3.694528049465325 ? ? ? ? deriv: 9.236320123663312
可見,前向過程完成計(jì)算得到f(2.0)≈3.69, f′(2.0)≈9.24。
3 反向自動(dòng)微分
我們前面介紹的前向自動(dòng)微分方法在計(jì)算y=f(t)的時(shí)候并行地計(jì)算f′(t)。接下來我們介紹一種“反向”自動(dòng)微分方法,相比上一種的方法它僅需要更少的函數(shù)求值,不過需要以更多的內(nèi)存消耗和更復(fù)雜的實(shí)現(xiàn)做為代價(jià)。
同樣,這個(gè)技術(shù)需要先將計(jì)算f(t)所產(chǎn)生的操作序列表示為計(jì)算圖。不過,與之前的從dt/dt=1開始,然后往dy/dt方向計(jì)算不同,反向自動(dòng)求導(dǎo)算法從dy/dy=1開始并且按與之前同樣的規(guī)則往反方向計(jì)算,一步步地將分母替換為dt。反向自動(dòng)微分可以避免不必要的計(jì)算,特別是當(dāng)y是一個(gè)多元函數(shù)的時(shí)候。例如,對(duì)f(t1,t2)=f1(t1)+f2(t2),反向自動(dòng)微分并不需要計(jì)算f1關(guān)于t2的微分或f2關(guān)于t1的微分。
例3?設(shè)f(x1,x2)=x1?exp(x2)?x1,模擬反向自動(dòng)微分過程。
可見若采用反向自動(dòng)微分,我們需要存儲(chǔ)計(jì)算過程中的所有東西,故內(nèi)存的使用量會(huì)和時(shí)間成正比。不過,在現(xiàn)有的深度學(xué)習(xí)框架中,對(duì)反向自動(dòng)微分的實(shí)現(xiàn)進(jìn)行了進(jìn)一步優(yōu)化,我們會(huì)在深度學(xué)習(xí)專題文章中再進(jìn)行詳述。
4 總結(jié)
自動(dòng)微分被廣泛認(rèn)為是一種未被充分重視的數(shù)值技術(shù), 它可以以盡量小的執(zhí)行代價(jià)來產(chǎn)生函數(shù)的精確導(dǎo)數(shù)。它在軟件需要計(jì)算導(dǎo)數(shù)或Hessian來運(yùn)行優(yōu)化算法時(shí)顯得格外有價(jià)值,從而避免每次目標(biāo)函數(shù)改變時(shí)都去重新手動(dòng)計(jì)算導(dǎo)數(shù)。當(dāng)然,做為其便捷性的代價(jià),自動(dòng)微分也會(huì)帶來計(jì)算的效率問題,因?yàn)樵趯?shí)際工作中自動(dòng)微分方法并不會(huì)去化簡(jiǎn)表達(dá)式,而是直接應(yīng)用最顯式的編碼規(guī)則。
原文鏈接:https://www.cnblogs.com/orion-orion/p/17010353.html
相關(guān)推薦
- 2022-07-12 Linux中xargs命令的用法
- 2022-10-14 查看pip安裝的python包的位置等詳細(xì)信息
- 2022-10-27 Kotlin?Flow封裝類SharedFlow?StateFlow?LiveData使用對(duì)比_An
- 2022-09-22 右值引用(C++11)
- 2022-03-08 C#中BackgroundWorker類用法總結(jié)_C#教程
- 2024-03-01 解決 “TypeError: Cannot read properties of undefined
- 2023-01-18 Go語(yǔ)言讀取YAML?配置文件的兩種方式分享_Golang
- 2022-09-15 Python淺析迭代器Iterator的使用_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支