计算梯度
现在让我们使用Theano来完成一个稍微复杂的任务:创建一个函数,该函数计算相对于其参数x的某个表达式y的导数。为此,我们将使用宏T.grad
。例如,我们可以计算相对于的梯度
import theano
import numpy as np
import theano.tensor as T
from theano import pp
x=T.dscalar('x')
y=x**2
gy=T.grad(y,x)
print pp(gy)#输出优化前的梯度
f=theano.function([x],gy)
print f(4)
((fill((x ** TensorConstant{2}), TensorConstant{1.0}) * TensorConstant{2}) * (x ** (TensorConstant{2} - TensorConstant{1})))
8.0
In this example, we can see from pp(gy)
that we are computing the correct symbolic gradient. fill((x ** 2), 1.0)
means to make a matrix of the same shape as x ** 2 and fill it with 1.0.
注意:
优化器简化了符号梯度表达式。你可以通过挖掘编译后的函数的内部属性来看到这一点。
print pp(f.maker.fgraph.outputs[0]) (TensorConstant{2.0} * x)
优化后,图中只剩下一个Apply节点,其使输入加倍。
我们还可以计算复杂表达式的梯度,例如上面定义的logistic函数。
x=T.dmatrix('x')
s=T.sum(1/(1+T.exp(-x)))
gs=T.grad(s,x)
dlogistic=theano.function([x],gs)
print dlogistic([[0,1],[-1,-2]])
一般来说,对于任何标量表达式s,T.grad(s, w)
提供Theano表达式用于计算。这样,Theano可用于对符号进行高效的微分(由于T.grad
返回的表达式将在编译期间优化),即使对于具有多个输入的函数也是如此。
注意:
T.grad的第二个参数可以是一个列表,在这种情况下,输出也是一个列表。两个列表中的顺序很重要:输出列表的元素i是T.grad
第一个参数相对于第二个参数列表中的第i元素的梯度。T.grad
的第一个参数必须是标量(大小为1的张量)。
计算Jacobian
在Theano的用语中,术语Jacobian表示函数相对于其输入的一阶偏导数的张量。(这是对数学中所谓的Jacobian矩阵的泛化。)Theano实现theano.gradient.jacobian宏,执行计算Jacobian所需的所有内容。以下内容说明如何手动执行。
为了手动计算某些函数y相对于某个参数x的雅可比矩阵(Jacobian),我们需要使用scan
。我们所做的是循环y中的条目,并计算y[i]相对于x的梯度。
注意:
scan
是Theano中的通用操作,允许以符号方式写入各种循环方程。创建符号循环(并优化它们的性能)是一项艰巨的任务,人们正在努力提高scan
的性能。