Theano 中文文檔 0.9 - 7.2.3 Theano中的導(dǎo)數(shù)

7.2.3 Theano中的導(dǎo)數(shù)

譯者:Python 文檔協(xié)作翻譯小組,原文:Derivatives in Theano

本文以 CC BY-NC-SA 4.0 協(xié)議發(fā)布,轉(zhuǎn)載請保留作者署名和文章出處。

Python 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。

計算梯度

現(xiàn)在讓我們使用Theano來完成一個稍微復(fù)雜的任務(wù):創(chuàng)建一個函數(shù),該函數(shù)計算相對于其參數(shù)x的某個表達式y的導(dǎo)數(shù)。為此,我們將使用宏T.grad。例如,我們可以計算

x^2
相對于
x
的梯度。注意:。

這里是計算這個梯度的代碼:

>>> import numpy
>>> import theano
>>> import theano.tensor as T
>>> from theano import pp
>>> x = T.dscalar('x')
>>> y = x ** 2
>>> gy = T.grad(y, x)
>>> pp(gy)  # print out the gradient prior to optimization
'((fill((x ** TensorConstant{2}), TensorConstant{1.0}) * TensorConstant{2}) * (x ** (TensorConstant{2} - TensorConstant{1})))'
>>> f = theano.function([x], gy)
>>> f(4)
array(8.0)
>>> numpy.allclose(f(94.2), 188.4)
True

在這個例子中,我們可以從pp(gy)看到我們正在計算正確的符號梯度。fill((x ** 2), 1.0)表示生成一個與x ** 2相同形狀的矩陣并以1.0填充它。

注意

優(yōu)化器簡化了符號梯度表達式。你可以通過挖掘編譯后的函數(shù)的內(nèi)部屬性來看到這一點。

pp(f.maker.fgraph.outputs[0])
'(2.0 * x)'

優(yōu)化后,圖中只剩下一個Apply節(jié)點,這將使輸入加倍。

我們還可以計算復(fù)雜表達式的梯度,例如上面定義的logistic函數(shù)。事實證明,logistic的導(dǎo)數(shù)是:。

http://deeplearning.net/software/theano/_images/dlogistic.png

logistic函數(shù)的梯度圖,其中x軸為x,y軸為

ds(x)/dx
。

>>> x = T.dmatrix('x')
>>> s = T.sum(1 / (1 + T.exp(-x)))
>>> gs = T.grad(s, x)
>>> dlogistic = theano.function([x], gs)
>>> dlogistic([[0, 1], [-1, -2]])
array([[ 0.25      ,  0.19661193],
 [ 0.19661193,  0.10499359]])

一般來說,對于任何標量表達式s,T.grad(s, w)提供Theano表達式用于計算。這樣,Theano可用于對符號進行高效的微分(由于T.grad返回的表達式將在編譯期間優(yōu)化),即使對于具有多個輸入的函數(shù)也是如此。(有關(guān)符號微分的描述,請參見自動微分)。

注意

T.grad的第二個參數(shù)可以是一個列表,在這種情況下,輸出也是一個列表。兩個列表中的順序很重要:輸出列表的元素iT.grad第一個參數(shù)相對于第二個參數(shù)列表中的第i元素的梯度。T.grad的第一個參數(shù)必須是標量(大小為1的張量)。有關(guān)T.grad參數(shù)的語義的更多信息以及實現(xiàn)的細節(jié),請參見庫的部分。

有關(guān)微分內(nèi)部工作原理的其他信息,也可以在更高級的教程擴展Theano中找到。

計算Jacobian

在Theano的用語中,術(shù)語Jacobian表示函數(shù)相對于其輸入的一階偏導(dǎo)數(shù)的張量。(這是對數(shù)學(xué)中所謂的Jacobian矩陣的泛化。)Theano實現(xiàn)theano.gradient.jacobian()宏,執(zhí)行計算Jacobian所需的所有內(nèi)容。以下內(nèi)容說明如何手動執(zhí)行。

為了手動計算某些函數(shù)y相對于某個參數(shù)x的雅可比矩陣,我們需要使用scan。我們所做的是循環(huán)y中的條目,并計算y [i]相對于x的梯度。

注意

scan是Theano中的通用操作,允許以符號方式寫入各種循環(huán)方程。創(chuàng)建符號循環(huán)(并優(yōu)化它們的性能)是一項艱巨的任務(wù),人們正在努力提高scan的性能。我們將在本教程后面回到scan。

>>> import theano
>>> import theano.tensor as T
>>> x = T.dvector('x')
>>> y = x ** 2
>>> J, updates = theano.scan(lambda i, y, x : T.grad(y[i], x), sequences=T.arange(y.shape[0]), non_sequences=[y, x])
>>> f = theano.function([x], J, updates=updates)
>>> f([4, 4])
array([[ 8.,  0.],
 [ 0.,  8.]])

我們在這段代碼中使用T.arange生成從0y.shape[0]int序列。然后,我們循環(huán)該序列,并且在每個步驟,我們計算元素y[i]相對于x的梯度。 scan自動連接所有這些行,生成對應(yīng)于Jacobian的矩陣。

注意

關(guān)于T.grad,有一些缺陷需要注意。其中一個是你不能重寫上面的Jacobian表達式為theano.scan(lambda y_i,x: T.grad(y_i,x), sequences=y, non_sequences=x),即使從scan的文檔看來是可能的。原因是y_i將不再是x的函數(shù),而y[i]仍然是。

計算Hessian

在Theano中,術(shù)語Hessian具有通常的數(shù)學(xué)概念:它是由函數(shù)的二階偏導(dǎo)數(shù)組成的矩陣,該函數(shù)的輸出為標量和輸入為向量。Theano實現(xiàn)theano.gradient.hessian()宏,完成計算Hessian所需要的所有內(nèi)容。以下內(nèi)容說明如何手動執(zhí)行。

你可以類似于類似于的方式手動計算Hessian?,F(xiàn)在唯一的區(qū)別是,我們計算T.grad(cost,x)的Jacobian,而不是計算某個表達式y的Jacobian,其中cost是某個標量。

>>> x = T.dvector('x')
>>> y = x ** 2
>>> cost = y.sum()
>>> gy = T.grad(cost, x)
>>> H, updates = theano.scan(lambda i, gy,x : T.grad(gy[i], x), sequences=T.arange(gy.shape[0]), non_sequences=[gy, x])
>>> f = theano.function([x], H, updates=updates)
>>> f([4, 4])
array([[ 2.,  0.],
 [ 0.,  2.]])

Jacobian乘以向量

有時我們可以用Jacobians乘以向量或向量乘以Jacobians來表達算法。與求值Jacobians然后進行相乘相比,有方法計算所需的結(jié)果同時避免對Jacobians進行真正的求值。這可以帶來顯著的性能提升。一個這樣的算法的描述可以在這里找到:

  • Barak A. Pearlmutter,“Fast Exact Multiplication by the Hessian”,Neural Computation,1994

雖然原則上我們希望Theano為我們自動識別這些模式,但在實踐中,以通用的方式實現(xiàn)這樣的優(yōu)化是非常困難的。因此,我們提供專門用于這些任務(wù)的特殊函數(shù)。

R操作符

R操作符用于求值Jacobian和向量之間的乘積,即。該公式甚至可以推廣為x是一個矩陣、或者一個普通的張量,在這種情況下Jacobian變?yōu)閺埩坎⑶页朔e變?yōu)槟撤N張量的積。因為在實踐中,我們最終需要根據(jù)權(quán)重矩陣來計算這樣的表達式,所以Theano支持這種更通用的操作形式。為了求值表達式y相對于xR操作,將Jacobian與v相乘,你需要做類似這樣的事情:

>>> W = T.dmatrix('W')
>>> V = T.dmatrix('V')
>>> x = T.dvector('x')
>>> y = T.dot(x, W)
>>> JV = T.Rop(y, W, V)
>>> f = theano.function([W, V, x], JV)
>>> f([[1, 1], [1, 1]], [[2, 2], [2, 2]], [0,1])
array([ 2.,  2.])

實現(xiàn)Rop的操作的列表。

L操作符

類似于R操作符,L操作符將計算向量乘以Jacobian。數(shù)學(xué)公式是

v \frac{\partial
f(x)}{\partial x}
v \frac{\partial f(x)}{\partial x}
。L操作符也支持普通的張量(不僅僅是向量)。類似地,它可以實現(xiàn)如下:

>>> W = T.dmatrix('W')
>>> v = T.dvector('v')
>>> x = T.dvector('x')
>>> y = T.dot(x, W)
>>> VJ = T.Lop(y, W, v)
>>> f = theano.function([v,x], VJ)
>>> f([2, 2], [0, 1])
array([[ 0.,  0.],
 [ 2.,  2.]])

注意

v求值的關(guān)鍵點,其在L操作R操作中不同。對于L操作符,這個求值的關(guān)鍵點需要具有與輸出相同的形狀,而對于R操作符,該點應(yīng)具有與輸入相同的形狀參數(shù)。此外,這兩個操作的結(jié)果不同。L操作符的結(jié)果與輸入?yún)?shù)具有相同的形狀,而R操作符的結(jié)果具有與輸出相似的形狀。

支持R操作的操作的列表。

R操作符

R操作符用于求值Jacobian和向量之間的乘積,即。該公式甚至可以推廣為x是一個矩陣、或者一個普通的張量,在這種情況下Jacobian變?yōu)閺埩坎⑶页朔e變?yōu)槟撤N張量的積。因為在實踐中,我們最終需要根據(jù)權(quán)重矩陣來計算這樣的表達式,所以Theano支持這種更通用的操作形式。為了求值表達式y相對于xR操作,將Jacobian與v相乘,你需要做類似這樣的事情:

>>> W = T.dmatrix('W')
>>> V = T.dmatrix('V')
>>> x = T.dvector('x')
>>> y = T.dot(x, W)
>>> JV = T.Rop(y, W, V)
>>> f = theano.function([W, V, x], JV)
>>> f([[1, 1], [1, 1]], [[2, 2], [2, 2]], [0,1])
array([ 2.,  2.])

實現(xiàn)Rop的操作的列表

L操作符

類似于R操作符,L操作符將計算向量乘以Jacobian。數(shù)學(xué)公式是

v \frac{\partial
f(x)}{\partial x}
v \frac{\partial f(x)}{\partial x}
L操作符也支持普通的張量(不僅僅是向量)。類似地,它可以實現(xiàn)如下:

>>> W = T.dmatrix('W')
>>> v = T.dvector('v')
>>> x = T.dvector('x')
>>> y = T.dot(x, W)
>>> VJ = T.Lop(y, W, v)
>>> f = theano.function([v,x], VJ)
>>> f([2, 2], [0, 1])
array([[ 0.,  0.],
 [ 2.,  2.]])

注意

v求值的關(guān)鍵點,其在L操作R操作中不同。對于L操作符,這個求值的關(guān)鍵點需要具有與輸出相同的形狀,而對于R操作符,該點應(yīng)具有與輸入相同的形狀參數(shù)。此外,這兩個操作的結(jié)果不同。L操作符的結(jié)果與輸入?yún)?shù)具有相同的形狀,而R操作符的結(jié)果具有與輸出相似的形狀。

支持R操作的操作的列表。

Hessian乘以向量

如果你需要計算Hessian乘一個向量,你可以利用上面定義的操作符,它比實際計算精確的Hessian然后執(zhí)行乘積更有效率。由于Hessian矩陣的對稱性,你有兩個選擇將給你相同的結(jié)果,雖然這些選擇可能表現(xiàn)出不同的性能。因此,我們建議在使用以下兩種方法之前分析它們:

>>> x = T.dvector('x')
>>> v = T.dvector('v')
>>> y = T.sum(x ** 2)
>>> gy = T.grad(y, x)
>>> vH = T.grad(T.sum(gy * v), x)
>>> f = theano.function([x, v], vH)
>>> f([4, 4], [2, 2])
array([ 4.,  4.])

或使用R操作符

>>> x = T.dvector('x')
>>> v = T.dvector('v')
>>> y = T.sum(x ** 2)
>>> gy = T.grad(y, x)
>>> Hv = T.Rop(gy, x, v)
>>> f = theano.function([x, v], Hv)
>>> f([4, 4], [2, 2])
array([ 4.,  4.])

最后的要點

  • grad函數(shù)以符號的方式工作:它接收并返回Theano變量。
  • grad可以與宏進行比較,因為它可以重復(fù)應(yīng)用。
  • 標量costs只能由grad直接處理。數(shù)組通過重復(fù)應(yīng)用來處理。
  • 內(nèi)置函數(shù)使得高效地計算向量乘以Jacobian向量乘以Hessian。
  • 優(yōu)化工作還在進行中,包括有效計算完全Jacobian和Hessian矩陣以及Jacobian乘以向量。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容