pytorch-backword函数的理解

函数:\(tensor.backward(params)\)

这个params的维度一定要和tensor的一致,因为tensor如果是一个向量y = [y1,y2,y3],那么传入的params=[a1,a2,a3],这三个值是系数,那么是什么的系数呢?
假定对x =[ x1,x2]求导,那么我们知道,
\(dy/dx\) 为:
第一列: \(dy1/dx1,dy2/dx1,dy3/dx1\)
第二列:\(dy1/dx2, dy2/dx2,dy3/dx2\)
从而 \(dy/dx\)是一个3行2列的矩阵,每一列对应了对x1的导数,每一列也就是\(x1\)的梯度向量
而反向计算的时候,并不是返回这个矩阵,而是返回这个矩阵每列的和作为梯度,也就是:\(dy1/dx1+dy2/dx1+dy3/dx1\) 是y对x1的梯度
这就好理解了,系数为\(params=[a1,a2,a3]\)就对应了这加和的三项!也就是,对\(x1\)的梯度实际上是\(a1*dy1/dx1+a2*dy2/dx1+a3*dy3/dx1\)
而输出y是标量的时候,就不需要了,默认的就是\(1.\)

自己重写backward函数时,要写上一个grad_output参数,这个参数就是上面提到的params

这个grad_output参数究竟是什么呢?下面作出解释:
是这样的,假如网络有两层, h = h(x),y = y(h)
你可以计算\(dy/dx\),这样,y.backward(),因为\(dy/dy=1\),那么,backward的参数就可以省略
如果计算h.backward(),因为你想求的是\(dy/dx\),(这才是输出对于输入的梯度),那么,计算图中的y = y(h)就没有考虑到
因为\(dy/dx = dy/dh * dh/dx\),h.backward()求得是\(dh/dx\),那么你必须传入之前的梯度\(dy/dh\)才行,也就是说,h.backward(params=dy/dh)这里面的参数就是\(dy/dh\)

这就好理解了,如果我们自己实现了一层,继承自Function,自己实现静态方法forwardbackward时,backward必须有个grad_output参数,这个参数就是计算图中输出对该自定义层的梯度,这样才能求出对输入的梯度。

另外,假设定义的层计算出的是y,调用的就是y.backward(grad_output),这个里面的参数的维度必须和y是相同的。这也就是为什么前面提到对于输出是多维的,会有个“系数”的原因,这个系数就是后向传播时,该层之前的梯度的累积,这样与本层再累积,才实现了完整的链式法则,最终求出outinput的梯度。

另外,自定义实现forwardbackward时,两函数的输入输出是有要求的,即forward的输入必须和~的return相对应,如forwardinput有个w参数,那么backwardreturn就必须在对应的位置返回grad_w,因为只有这样,才能够对相应的输入参数梯度下降。

05-11 21:59