溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

發(fā)布時(shí)間:2022-12-29 16:20:25 來(lái)源:億速云 閱讀:144 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。

1 自動(dòng)微分

許多經(jīng)典的數(shù)值微分算法非常快,因?yàn)樗鼈冎恍枰?jì)算差商。然而,他們的主要缺點(diǎn)在于他們是數(shù)值的,這意味著有限的算術(shù)精度和不精確的函數(shù)求值,而這些都從根本上限制了求解結(jié)果的質(zhì)量。因此。充滿(mǎn)噪聲的、復(fù)雜多變的函數(shù)很難得到精準(zhǔn)的數(shù)值微分。

自動(dòng)微分技術(shù)(稱(chēng)為“automatic differentiation, autodiff”)是介于符號(hào)微分和數(shù)值微分的一種技術(shù),它是在計(jì)算效率和計(jì)算精度之間的一種折衷。自動(dòng)微分不受任何離散化算法誤差的約束,它充分利用了微分的鏈?zhǔn)椒▌t和其他關(guān)于導(dǎo)數(shù)的性質(zhì)來(lái)準(zhǔn)確地計(jì)算它們。

2 前向自動(dòng)微分

我們先來(lái)計(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ù)類(lèi)型來(lái)存儲(chǔ)[u,u′]和[v,v′]這類(lèi)數(shù)對(duì)。我們可以在這些數(shù)對(duì)上定義一種代數(shù)運(yùn)算,這些代數(shù)運(yùn)算編碼了一些經(jīng)典的操作:

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

在進(jìn)行前向自動(dòng)微分之前,我們需要先將計(jì)算f(t)所產(chǎn)生的操作序列表示為計(jì)算圖。接著,采用自底向上的遞推算法的思想,從做為遞推起點(diǎn)的數(shù)對(duì)t≡[t0,1](因?yàn)閐t/dt=1)開(kāi)始,我們能夠按照我們上述編碼規(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ì)用戶(hù)透明地執(zhí)行了。

例1 比如,對(duì)于函數(shù)f(x)=exp?(x2−x)/x,想要依次計(jì)算dyi/dx(這里yi為所有計(jì)算中間項(xiàng))。則我們先從x開(kāi)始將表達(dá)式分解為計(jì)算圖:

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

然后前向遞推地按照我們之前所述的編碼規(guī)則來(lái)進(jìn)行求導(dǎo)

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

注意鏈?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如下:

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

比如,對(duì)于

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

我們有

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

下面展示了一個(gè)對(duì)二元函數(shù)模擬前向自動(dòng)微分的過(guò)程。

例2 設(shè)(x1,x2)=x1⋅exp?(x2)−x1,模擬前向微分過(guò)程。

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

接下來(lái)我們看如何用Python代碼來(lái)實(shí)現(xiàn)單變量函數(shù)的前向自動(dòng)微分過(guò)程。為了簡(jiǎn)便起見(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

可見(jiàn),前向過(guò)程完成計(jì)算得到f(2.0)≈3.69, f′(2.0)≈9.24。

3 反向自動(dòng)微分

我們前面介紹的前向自動(dòng)微分方法在計(jì)算y=f(t)的時(shí)候并行地計(jì)算f′(t)。接下來(lái)我們介紹一種“反向”自動(dòng)微分方法,相比上一種的方法它僅需要更少的函數(shù)求值,不過(guò)需要以更多的內(nèi)存消耗和更復(fù)雜的實(shí)現(xiàn)做為代價(jià)。

同樣,這個(gè)技術(shù)需要先將計(jì)算f(t)所產(chǎn)生的操作序列表示為計(jì)算圖。不過(guò),與之前的從dt/dt=1開(kāi)始,然后往dy/dt方向計(jì)算不同,反向自動(dòng)求導(dǎo)算法從dy/dy=1開(kāi)始并且按與之前同樣的規(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)微分過(guò)程。

Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分

可見(jiàn)若采用反向自動(dòng)微分,我們需要存儲(chǔ)計(jì)算過(guò)程中的所有東西,故內(nèi)存的使用量會(huì)和時(shí)間成正比。不過(guò),在現(xiàn)有的深度學(xué)習(xí)框架中,對(duì)反向自動(dòng)微分的實(shí)現(xiàn)進(jìn)行了進(jìn)一步優(yōu)化,我們會(huì)在深度學(xué)習(xí)專(zhuān)題文章中再進(jìn)行詳述。

讀到這里,這篇“Python如何實(shí)現(xiàn)前向和反向自動(dòng)微分”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI