Theano2.1.6-基础知识之在thenao中的求导
來自:http://deeplearning.net/software/theano/tutorial/gradients.html
Derivatives in Theano
一、計算梯度
? ? 現在,讓我們使用theano來做稍微更復雜的任務:創建一個函數,用來計算表達式y 關于它的參數x的導數。我們將會用到宏?T.grad?。例如,我們可以計算??關于?的梯度。注意:?.
? ? 下面就是用來計算這個梯度的代碼:
>>> 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 ** 2), 1.0) * 2) * (x ** (2 - 1)))' >>> f = function([x], gy) >>> f(4) array(8.0) >>> f(94.2) array(188.40000000000001)? ? 在這個例子中,我們可以從pp(gy)?中看到我們在計算的符號梯度是正確的。?fill((x?**?2),?1.0)?意思是說創建一個和?x?**?2一樣shape的矩陣,然后用1.0來填充。
note:該優化器簡化了符號梯度的表達式,你可以深挖編譯后的函數的內部屬性來了解細節。
pp(f.maker.fgraph.outputs[0]) '(2.0 * x)'
在優化之后,在graph中只有一個 Apply節點,其輸入是乘以2的。
? ? 我們同樣可以計算復雜表達式的梯度,例如由上面定義的邏輯函數。結果顯示邏輯函數的梯度為:?.? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
該圖是邏輯函數的梯度,x軸表示x的變化,y軸表示梯度??。
>>> x = T.dmatrix('x') >>> s = T.sum(1 / (1 + T.exp(-x))) >>> gs = T.grad(s, x) >>> dlogistic = function([x], gs) >>> dlogistic([[0, 1], [-1, -2]]) array([[ 0.25 , 0.19661193],[ 0.19661193, 0.10499359]])
? ? 通常來說,對于任何標量表達式,?T.grad(s,?w)?提供theano表達式來計算?。這種方式下,甚至對于有著許多輸入的函數來說,theano可以用來高效的計算符號微分?(正如?T.grad?返回的表達式可以在編譯期間進行優化) (?automatic differentiation?有詳細的描述關于符號微分的)。
note:?T.grad?的第二個參數可以是一個列表,這種情況下,輸出也同樣是一個列表。在這兩個列表中的順序都是很重要的:輸出列表的第 i 個元素是T.grad?的第一個參數關于第二個參數的列表的第 i 個元素的梯度。?T.grad?第一個參數必須是一個標量(其tensor size 為1)。更多有關T.grad的參數的語義的信息和實現的細節,可以參考庫的?this?部分。
在內部微分的工作的信息可以在更高級的教程?Extending Theano中找到。
二、計算Jacobian
? ? 在theano中,術語?Jacobian?指定為張量包含函數的輸出關于輸入的第一個偏導數。 (在數學中這就是所謂的Jacobian矩陣) Theano 實現宏theano.gradient.jacobian()?所需要的就是計算Jacobian。下面部分就是解釋如何手動去完成它:
? ? 為了手動計算一些函數 y 關于一些參數 x 的Jacobian,我們需要使用?scan。即在y 中使用循環來遍歷所有元素,然后計算?y[i]?關于x 的梯度。
note:scan?是theano中一個通用的操作,可以以符號方式寫出各種遞歸等式。然而生成一個符號循環是很難的(而且還需要為了性能而去優化它們) ,所以需要努力提升scan.的效果。在后面會接著說?scan?的。
>>> 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 = function([x], J, updates=updates) >>> f([4, 4]) array([[ 8., 0.],[ 0., 8.]])
? ? 在該代碼中所做的就是生成一個int類型的序列,通過使用T.arange來使得其中從0到?y.shape[0]?。然后我們對這個序列進行循環,然后在每一步,滅我們計算元素?y[i]關于x 的梯度。scan?可以自動的連接所有的這些列,生成一個對應于jacobian的矩陣。
note:在使用T.grad的時候記得也有一些陷阱的。?其中一個就是你沒法和這樣theano.scan(lambda?y_i,x:?T.grad(y_i,x),?sequences=y,?non_sequences=x)重寫jacobin的上述表達式,,盡管從文檔上看scan是可以的。原因在于?y_i?不再試x的函數了,而?y[i]仍然是。
三、計算Hessian
? ? 在theano中,術語Hessian?與數學上的概念沒差:是一個矩陣,其中包含著標量輸出和向量輸入的函數的二階偏導數。Theano 實現宏theano.gradient.hessian()?所要做的就是計算Hessian。下面的部分就是介紹如何手動完成。
? ? 你可以可jacobian一樣相似的計算Hessian。唯一的差別在于,我們通過計算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 = function([x], H, updates=updates) >>> f([4, 4]) array([[ 2., 0.],[ 0., 2.]])四、Jacobian乘以一個向量
? ? 有時候我們需要將算法表示成jacobinas乘以向量,或者向量乘以jacobinans。相比較于評估jacobian,然后做乘法,可以直接計算合適的結果從而避免對jacobian的實際計算。這可以帶來明顯的性能的提升。一個這樣的算法可以在下面的文獻中找到:
- Barak A. Pearlmutter, “Fast Exact Multiplication by the Hessian”,?Neural Computation, 1994
? ? 然而在實際中,我們想要theano能夠自動的識別這些模式,不過以通常的方式來實現這樣的優化是非常難的。所以,我們提供了特別的函數來應對這些問題:
R-operator
? ? R 操作符是用來評估介于一個jacobian和一個向量之間的乘積的,即?. 該式子可以擴展成當x是一個矩陣,或者一個張量的形式,這種情況下,jacobian就變成了一個張量,然后乘積就變成了某種張量的積。因為在實際中,我們最后是需要計算權重矩陣這樣的表達式的,theano支持這種操作的更通用形式。為了評估表達式y的R 操作,(關于x的),使用v乘以jacobian,你需要做類似下面的事情:
>>> 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.])實現Rop的操作列表List?。L-operator
? ? 相似于R-操作,L-操作?會計算一個行向量乘積,其數學上的形式為?。該L-操縱?同樣支持通用的張量 (不只是向量)。相思的,它可以按照下面形式實現:
>>> 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.]])note:v, 在L操作和R操作中是不同的。對于L操作來說,該 v?需要有著和輸出一樣的shape,然而,R操作需要和輸入參數一樣的shape。更進一步說,這兩個操作的結果是不同的。L操作的結果有著和輸入參數一樣的shape,而R操作有著和輸出一樣的shape。
五、Hessian乘以一個向量
? ? 如果你需要計算Hessian 乘以一個向量,你就需要用到上面定義的操作,它們通常比實際計算準確的Hessian,然后計算乘積更高效。因為Hessian矩陣的對稱性,你可以用兩種方式得到相同的結果,雖然這些選擇也許會有不同的性能。因此,我們建議在使用它們之前先, 先對它們進行分析:
>>> 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?函數是符號化的工作的:它接受和返回theano變量。
- grad?可以和宏相比較,因為它可以重復使用
- 標量損失只能被直接通過grad進行處理。數組可以通過重復應用的形式來解決
- 內建的函數可以高效的計算向量乘以jacobian和向量乘以Hessian
- 優化需要高效的計算全jacobian和Hessian矩陣,以及jacobian乘以向量。
[1]官網:http://deeplearning.net/software/theano/tutorial/gradients.html
總結
以上是生活随笔為你收集整理的Theano2.1.6-基础知识之在thenao中的求导的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: curl 发送带有Authorizati
- 下一篇: 文艺网名有深度有诗意790个