【theano-windows】学习笔记三——theano中的导数
前言
就一個NN而言,包含梯度、偏置、參數更新,而前面第一篇博客學習了theano中符號變量的定義, 第二篇博客學習了變量的隨機初始化, 變量之間的互相操作(類似于sigmoid(w?x+b)), 但是參數更新還應涉及到損失函數的偏導計算,這一章節就是看看theano的梯度計算函數`tensor.grad(). 此外還有雅可比式(一階導),海森矩陣(二階導),雅克比乘以向量,海森矩陣乘以向量的操作
梯度計算
目前感覺這個函數沒什么需要注意的, 使用方法直接就是 T.grad(y,x), 意思就是y對x求導,而且我們還能用function.marker.fgraph.outputs[0]把得到的導數公式輸出出來,先導入模塊,沒什么好說的
import theano import theano.tensor as T from theano import pp#用于輸出表達式拿y=x^2+x^3+x^4試水
x=T.dscalar('x')#定義一個變量 y=x**2+x**3+x**4#定義一個操作 gy=T.grad(y,x)#將y對x求導 f=theano.function([x],gy)# 執行這個求導函數 pp(f.maker.fgraph.outputs[0])輸出是
Elemwise{Composite{((i0 * i1) + (i2 * sqr(i1)) + (i3 * Composite{(sqr(i0) * i0)}(i1)))}}(TensorConstant{2.0}, x, TensorConstant{3.0}, TensorConstant{4.0})然后怎么依據這個輸出把導數表達式寫出來呢?這里我們把i0,i1,i2,i3分別用2,x,3,4替換(TensorConstant{2.0}意思就是常數張量2.0,’sqr’代表平方),然后帶入到前面的Composite{((i0 * i1) + (i2 * sqr(i1)) + (i3 * Composite{(sqr(i0) * i0)}(i1)))}中, 注意遇到Composite{xxx}(xxx), 就用()中的xxx去替換{}中的xxx,然后我來分析一波
這樣就能得到一個結果2*x+3*x^2+4*(x^2)*x,剛好就是y=x2+x3+x4對x的偏導結果
同樣的例子套入到對率函數中去
y1=1/(1+T.exp(-x)) gy1=T.grad(y1,x) f2=theano.function([x],gy1)# pp(gy1)pp(f2.maker.fgraph.outputs[0])輸出是
Elemwise{Composite{(scalar_sigmoid((-i0)) * scalar_sigmoid(i0))}}(x)按照上面的圖畫一下,然后可以寫出來結果是sigmoid(-x)*sigmoid(x),其實也就是
y?y?x=sigmoid(x)=11+e?x=sigmoid(?x)?sigmoid(x)=11+e?x?11+ex=y(1?y)
注意, T.grad()的第二個參數可以是一個列表,那么輸出也就是列表了,意思應該就是損失函數可以對權重和偏置在一個函數中求導
計算雅克比矩陣
在theano中雅克比就是計算一個函數的輸出相對于每個輸入的一階偏導數。theano中有兩種方法去實現
間接(人工)方法
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.]]) ?```
由于這需要使用到循環(對每個輸入都要計算偏導數), 因而提前接觸到了theano中用于創建循環的函數scan(), 如果想提前了解scan(), 可以去看這位博主的五個例子掌握theano.scan函數這里稍微說一下用到的scan內容:
- 第一個參數默認就是定義的函數了,這里用了個lambda表達式lambda i,y,x : T.grad(y[i],x)意思是我們要計算這個梯度, 而這i,y,x的來源就是后面的幾個參數,賦值順序一般是sequences中的變量,outputs_info的變量,non_sequences中的變量 , 這個順序要記住
- 參數sequences表示我們需要遍歷的量,一般都是T.arange()創建的列表,把它丟給了i
- 參數non_sequences是其他兩個輸入量[y,x],把它丟給了lambda函數中的y,x
- 返回值J是輸出變量列表,updates是字典,表示每個輸出變量列表的更新規則
直接方法
#直接計算hessian矩陣x=T.dvector('x') y=T.dvector('y') input=[x,y] s=T.sum(x**2+y**2) f=theano.gradient.jacobian(expression=s,wrt=input) h=theano.function(input,f) x=[1,2] y=[1,2] h(x,y) ''' [array([ 2., 4.]), array([ 2., 4.])] '''
theano中直接提供了一個函數來實現Jacobian矩陣的計算theano.gradient.jacobian(expression, wrt, consider_constant=None, disconnected_inputs='raise')看樣子計算的就是損失函數expression關于input中變量的偏導數
計算海森矩陣
普遍接受的關于海森矩陣的數學說法是: 它是具有標量輸出和向量輸入的二階偏導函數的矩陣。同樣有間接和直接兩種計算方法
間接(人工)計算方法
#間接計算Hessian矩陣x=T.dvector('x') y=x**2 cost=y.sum() gy=T.grad(cost,x)#先計算損失關于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矩陣不同的是我們不是計算某個表達式的雅克比式,而是計算關于導數T.grad(cost,x)的雅可比式依舊是theano.scan()的應用, 第一個參數是計算梯度的函數, 后面的先把sequences的列表丟給i, 然后將non_sequence中的gy,x分別丟給lambda表達式中的gy和x
直接計算方法
x=T.dvector('x') y=T.dvector('y') input=[x,y] s=T.sum(x**2+y**2) f=theano.gradient.hessian(cost=s,wrt=input) h=theano.function(input,f) x=[1,2] y=[1,2] h(x,y) ''' [array([[ 2., 0.],[ 0., 2.]]), array([[ 2., 0.],[ 0., 2.]])] '''
theano中直接提供了一個函數來實現Hessian矩陣的計算:theano.gradient.hessian(cost, wrt, consider_constant=None, disconnected_inputs='raise')看樣子計算的就是損失函數cost關于input中變量的偏導數
雅可比式與向量乘積
R-操作(右乘)
計算目標
其中 x可以是矩陣或者是張量,主要還是因為我們需要依據權重矩陣做一些表達式的計算#雅可比式*向量 W=T.dmatrix('W') V=T.dmatrix('V') x=T.dvector('x') y=T.dot(x,W) JV=T.Rop(y,W,V)#這里直接調用的就是Rop函數對應右乘操作 f=theano.function([W,V,x],JV) w=[[1, 1], [1, 1]] v=[[2, 2], [2, 2]] x=[0,1] f(w,v,x) ''' array([ 2., 2.]) '''
這干了一件什么事情呢?數學表達式如下:
y?y?W?V=W?x=x?V
L-操作(左乘)
#向量*雅可比式 W = T.dmatrix('W') V = T.dvector('V') x = T.dvector('x') y = T.dot(x, W) VJ = T.Lop(y, W, V)#這里直接調用的就是Rop函數對應右乘操作 f = theano.function([V,x], VJ) f([2, 2], [0, 1]) ''' array([[ 0., 0.],[ 2., 2.]]) '''yV??y?W=W?x=V?x
左乘和右乘的差別
其實從矩陣的乘法規則就能發現他們的不同:
左乘中的v與輸出有相同的shape, 右乘中的v與輸入有相同的shape。
左乘的結果與輸入有相同shape, 右乘的結果與輸出有相似的shape
海森矩陣與向量乘積
由于Hessian矩陣具有對成型, 所以有兩種操作可以得到相同的結果,但是性能可能稍有不同。所以theano建議大家在使用兩個方法時先分析一下
方法一:
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.]) '''方法二:
x=T.dvector('x') v=T.dvector('v') y=T.sum(x**2)#定義操作 gy=T.grad(y,x)#一階導 Hv=T.Rop(gy,x,v)#利用Jacobian矩陣的右乘操作 f=theano.function([x,v],Hv) f([4,4],[2,2]) ''' array([ 4., 4.]) '''注意事項
- grad函數是符號運算: 接受和返回Theano變量
- grad可以被重復使用
- grad操作中, 標量損失可以直接用grad處理, 但是數組類型的需要用循環處理,比如計算Jacobian矩陣中需要對向量中每個值進行梯度的循環計算
- 內置函數能夠高效計算向量*雅可比式、向量*海森矩陣
代碼地址:鏈接: https://pan.baidu.com/s/1eSAIZOu 密碼: wu27
總結
以上是生活随笔為你收集整理的【theano-windows】学习笔记三——theano中的导数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 光大苏宁SUPER联名信用卡年费多少?怎
- 下一篇: 信用卡黑屋什么意思?情节严重会影响征信!