Theano 中文文档 0.9 - 7.2.3 Theano中的导数

7.2.3 Theano中的导数

译者:Python 文档协作翻译小组,原文:Derivatives in Theano

本文以 CC BY-NC-SA 4.0 协议发布,转载请保留作者署名和文章出处。

Python 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。

计算梯度

现在让我们使用Theano来完成一个稍微复杂的任务:创建一个函数,该函数计算相对于其参数x的某个表达式y的导数。为此,我们将使用宏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填充它。

注意

优化器简化了符号梯度表达式。你可以通过挖掘编译后的函数的内部属性来看到这一点。

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

优化后,图中只剩下一个Apply节点,这将使输入加倍。

我们还可以计算复杂表达式的梯度,例如上面定义的logistic函数。事实证明,logistic的导数是:。

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

logistic函数的梯度图,其中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]])

一般来说,对于任何标量表达式sT.grad(s, w)提供Theano表达式用于计算。这样,Theano可用于对符号进行高效的微分(由于T.grad返回的表达式将在编译期间优化),即使对于具有多个输入的函数也是如此。(有关符号微分的描述,请参见自动微分)。

注意

T.grad的第二个参数可以是一个列表,在这种情况下,输出也是一个列表。两个列表中的顺序很重要:输出列表的元素iT.grad第一个参数相对于第二个参数列表中的第i元素的梯度。T.grad的第一个参数必须是标量(大小为1的张量)。有关T.grad参数的语义的更多信息以及实现的细节,请参见库的部分。

有关微分内部工作原理的其他信息,也可以在更高级的教程扩展Theano中找到。

计算Jacobian

在Theano的用语中,术语Jacobian表示函数相对于其输入的一阶偏导数的张量。(这是对数学中所谓的Jacobian矩阵的泛化。)Theano实现theano.gradient.jacobian()宏,执行计算Jacobian所需的所有内容。以下内容说明如何手动执行。

为了手动计算某些函数y相对于某个参数x的雅可比矩阵,我们需要使用scan。我们所做的是循环y中的条目,并计算y [i]相对于x的梯度。

注意

scan是Theano中的通用操作,允许以符号方式写入各种循环方程。创建符号循环(并优化它们的性能)是一项艰巨的任务,人们正在努力提高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序列。然后,我们循环该序列,并且在每个步骤,我们计算元素y[i]相对于x的梯度。 scan自动连接所有这些行,生成对应于Jacobian的矩阵。

注意

关于T.grad,有一些缺陷需要注意。其中一个是你不能重写上面的Jacobian表达式为theano.scan(lambda y_i,x: T.grad(y_i,x), sequences=y, non_sequences=x),即使从scan的文档看来是可能的。原因是y_i将不再是x的函数,而y[i]仍然是。

计算Hessian

在Theano中,术语Hessian具有通常的数学概念:它是由函数的二阶偏导数组成的矩阵,该函数的输出为标量和输入为向量。Theano实现theano.gradient.hessian()宏,完成计算Hessian所需要的所有内容。以下内容说明如何手动执行。

你可以类似于类似于的方式手动计算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 = theano.function([x], H, updates=updates)
>>> f([4, 4])
array([[ 2.,  0.],
 [ 0.,  2.]])

Jacobian乘以向量

有时我们可以用Jacobians乘以向量或向量乘以Jacobians来表达算法。与求值Jacobians然后进行相乘相比,有方法计算所需的结果同时避免对Jacobians进行真正的求值。这可以带来显着的性能提升。一个这样的算法的描述可以在这里找到:

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

虽然原则上我们希望Theano为我们自动识别这些模式,但在实践中,以通用的方式实现这样的优化是非常困难的。因此,我们提供专门用于这些任务的特殊函数。

R操作符

R操作符用于求值Jacobian和向量之间的乘积,即。该公式甚至可以推广为x是一个矩阵、或者一个普通的张量,在这种情况下Jacobian变为张量并且乘积变为某种张量的积。因为在实践中,我们最终需要根据权重矩阵来计算这样的表达式,所以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.])

实现Rop的操作的列表

L操作符

类似于R操作符L操作符将计算向量乘以Jacobian。数学公式是

v \frac{\partial
f(x)}{\partial x}
v \frac{\partial f(x)}{\partial x}
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.]])

注意

v求值的关键点,其在L操作R操作中不同。对于L操作符,这个求值的关键点需要具有与输出相同的形状,而对于R操作符,该点应具有与输入相同的形状参数。此外,这两个操作的结果不同。L操作符的结果与输入参数具有相同的形状,而R操作符的结果具有与输出相似的形状。

支持R操作的操作的列表

R操作符

R操作符用于求值Jacobian和向量之间的乘积,即。该公式甚至可以推广为x是一个矩阵、或者一个普通的张量,在这种情况下Jacobian变为张量并且乘积变为某种张量的积。因为在实践中,我们最终需要根据权重矩阵来计算这样的表达式,所以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.])

实现Rop的操作的列表

L操作符

类似于R操作符L操作符将计算向量乘以Jacobian。数学公式是

v \frac{\partial
f(x)}{\partial x}
v \frac{\partial f(x)}{\partial x}
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.]])

注意

v求值的关键点,其在L操作R操作中不同。对于L操作符,这个求值的关键点需要具有与输出相同的形状,而对于R操作符,该点应具有与输入相同的形状参数。此外,这两个操作的结果不同。L操作符的结果与输入参数具有相同的形状,而R操作符的结果具有与输出相似的形状。

支持R操作的操作的列表

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可以与宏进行比较,因为它可以重复应用。
  • 标量costs只能由grad直接处理。数组通过重复应用来处理。
  • 内置函数使得高效地计算向量乘以Jacobian向量乘以Hessian
  • 优化工作还在进行中,包括有效计算完全Jacobian和Hessian矩阵以及Jacobian乘以向量
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,082评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,231评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,047评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,977评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,893评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,976评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,605评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,888评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,906评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,732评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,513评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,980评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,027评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,232评论 2 339

推荐阅读更多精彩内容