自动求梯度 讨论区


#1

http://zh.diveintodeeplearning.org/chapter_prerequisite/autograd.html


#2

对控制流求导部分,定义这个f(a)没看懂是干什么的,是这段代码实现了f(a)=xa?


#3

这段函数就是对输入的a不断的乘以2直到大于1000输出,其实就是一个类似线性函数。


#4
  1. 头梯度是什么?
  2. 为什么要传头梯度?
  3. 头梯度怎么定?

于置顶 #5

#6

传入头梯度后,autograd 会使用传入参数而不会再算dz/dy 了对么,即使传入的是错误的?


#7

头梯度计算的例子没看懂,y=x*2, z = y*x, x= [[1,2],[3,4]], 如果正常计算 dz/dx 的话, dz/dx = 4*x ,所以 输出的结果是 [[4,8],[12,16]]。

一般链式求导假设 z = f(y),y = g(x) , dz/dx = dz/dy *dy/dx ,

如果 dz/dy = [[10, 1.], [.1, .01]] ,那应该有 dz/dx = dz/dy * dy/dx = [[10, 1.], [.1, .01]] * 2 成立才对,可是为什么结果是[[ 40. 8. ] [ 1.20000005 0.16 ]] ? 求解释


#8

对,x是不确定的(取决初始化情况),但 c = x*a是确定的, a.grad == x == c/a是确定并可以验证的


#9

简单来说,头梯度就相当于提供了在前面乘的系数。没有头梯度就当系数为1.

x = nd.array([[1, 2], [3, 4]])
x.attach_grad()
with ag.record():
    y = x * x
    z = y * x * x
z.backward()
print(x.grad)

输出:
[[ 4. 32.]
[ 108. 256.]]
<NDArray 2x2 @cpu(0)>

x = nd.array([[1, 2], [3, 4]])
x.attach_grad()
with ag.record():
    y = x * x
    z = y * x * x
head_gradient1 = nd.array([[1, 1], [1, 1]])
z.backward(head_gradient1)
print(x.grad)

输出:
[[ 4. 32.]
[ 108. 256.]]
<NDArray 2x2 @cpu(0)>

x = nd.array([[1, 2], [3, 4]])
x.attach_grad()
with ag.record():
    y = x * x
    z = y * x * x
head_gradient = nd.array([[10, 1.], [.1, .01]])
z.backward(head_gradient)
print(x.grad)

输出:
[[ 40. 32. ]
[ 10.80000019 2.55999994]]
<NDArray 2x2 @cpu(0)>


#10

为什么要提供头梯度


#11

如果说头梯度就是系数的话可以理解,但是文档中说头梯度是 dz/dy的值,这个就比较费解了 ==


#12

很多算法中都要用到头梯度,比如GAN中G的梯度要经过D计算损失,然后通过D网络backward给G,单纯的G是不能产生梯度的。


#13

def f(x)

x.attach_grad()
z = f(x)
z.backward()
那么z.grad := dz/dx,如果有y(z),已知dy/dz,那么要求dy/dx,只需要
z.backward(head_gradient)
head_gradient = dy/dz
assert head_gradient.shape == z.shape


#14

同问…如果说头梯度是dz/dy的值,在文档中z = y * x,那dz/dy就是x呀,然后给定的nd.array([[10, 1.], [.1, .01]])其实就是x的值,而dz/dx直接用链式法则求的话是4x,那最后的结果应该是[[ 40. 4. ] [ 0.4 0.04 ]] <NDArray 2x2 @cpu(0)>呀…求解


#15

文档原文如下:“当y是一个更大的z函数的一部分,并且我们希望求得dz/dx保存在x.grad中时,我们可以传入头梯度dz/dy的值作为backward()方法的输入参数,系统会自动应用链式法则进行计算。’

我的理解是:这里的z和原文中的“z = y * x”不是一个概念。或者说是u(z),作者的意思应该是当z的函数更为复杂时(也就是u(z)),我们可以将du/dz当做参数传入z.backward。

这时候x.gard即为du/dx。

从某种角度上来说,即为之前的梯度乘上了系数(值为头梯度)


#16

这个理解更准确,很多人应该是被这个迷惑了~


#17

文档可能写的不太清晰


#18

嗯,老哥说的有道理。不过文档这样表述有点迷啊


#19

也就是说,代码中的z其实是公式中的y。


#20

我也觉得好像是如此,等大神给解惑。