torch中的自动求导为什么要传个与被求导量同形状的gradient

torch中的自动求导为什么要传个与被求导量同形状的gradient

y.backward(torch.ones(y.size())) 初学深度学习接触到自动求导时,被这个东西深深困惑住了,反向传播就反向传播呗,为啥要传入一个torch.ones(y.size())呢,这个与y相同形状的矩阵是啥玩意呢,带着思考,我查询了pytorch文档,心中有了答案。 参考自动求导文档

实际上,torch中的自动求导只对标量进行,如果你要backward的对象是一个矢量,那么就需要传入一个和这个矢量形状一样的矢量,作为它的gradient,原文档中用gradient肯定是有原因的:

比如我现在有 l o s s = g ( y ⃗ ) loss=g(\vec{y}) loss=g(y ) y ⃗ = f ( x ⃗ ) \vec{y}=f(\vec{x}) y =f(x ),那么Loss对y的导数和y对x的导数就是:
∂ l o s s ∂ y ⃗ = v ⃗ = ( ∂ ℓ ∂ y 1 ⋯ ∂ ℓ ∂ y m ) T ∂ y ∂ x ⃗ = J = ( ∂ y ∂ x 1 ⋯ ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 ⋯ ∂ y m ∂ x n ) \begin{aligned}\frac{\partial loss}{\partial\vec{y}}&=\vec{v}=\begin{pmatrix}\frac{\partial\ell}{\partial y_1}&\cdots&\frac{\partial\ell}{\partial y_m}\end{pmatrix}^T\\\frac{\partial y}{\partial\vec{x}}&=J=\begin{pmatrix}\frac{\partial\mathbf{y}}{\partial x_1}&\cdots&\frac{\partial y_1}{\partial x_n}\\\vdots&\ddots&\vdots\\\frac{\partial y_m}{\partial x_1}&\cdots&\frac{\partial y_m}{\partial x_n}\end{pmatrix}\end{aligned} y lossx y=v =(y1ym)T=J= x1yx1ymxny1xnym
于是乎, ∂ l o s s ∂ x ⃗ \frac{\partial loss}{\partial \vec x} x loss就可以使用链式法则了(注意J是使用的分子布局,就是优先按照分子的维度排列,换成分母法则的话,下面就不用转置了,其实本质一样)
∂ l o s s ∂ x ⃗ = ( ∂ y ⃗ ∂ x ⃗ ) T ∂ l o s s ∂ y ⃗ = J T v ⃗ = ( ∂ y 1 ∂ x 1 ⋯ ∂ y m ∂ x 1 ⋮ ⋱ ⋮ ∂ y 1 ∂ x n ⋯ ∂ y m ∂ x n ) ( ∂ l ∂ y 1 ⋮ ∂ l ∂ y n ) \frac{\partial loss}{\partial\vec{x}}=(\frac{\partial\vec{y}}{\partial\vec{x}})^T\frac{\partial loss}{\partial\vec{y}}=J^T\vec{v}=\begin{pmatrix}\frac{\partial y_1}{\partial x_1}&\cdots&\frac{\partial y_m}{\partial x_1}\\\vdots&\ddots&\vdots\\\frac{\partial y_1}{\partial x_n}&\cdots&\frac{\partial y_m}{\partial x_n}\end{pmatrix}\begin{pmatrix}\frac{\partial l}{\partial y_1}\\\vdots\\\frac{\partial l}{\partial y_n}\end{pmatrix} x loss=(x y )Ty loss=JTv = x1y1xny1x1ymxnym y1lynl

正常来说,对于标量值l,我们可以直接backward()并查看x的grad属性所存储的梯度 ∂ l o s s ∂ x ⃗ \frac{\partial loss}{\partial \vec x} x loss,没问题,很顺利;

但是对于y呢,我要是相对y求导,并查看x的梯度 ∂ y ⃗ ∂ x ⃗ \frac{\partial \vec y}{\partial \vec x} x y 怎么办呢,很遗憾,不支持这么做!!!,需要我们自己传入一个 v ⃗ \vec v v 才行,因为pytorch的只支持标量值的求导,而我们一般来说,损失函数loss也确实是个标量。

-----------------------------我会尽快给出代码
----------------------------------------------------------------------23.11.8.17:07

代码验证

假设x就是一个列向量,k是一个矩阵,y=k*x(矩阵相乘),得到的y也是个向量,loss=(y-0).sum(),就是y与零向量的差距,所以很明显loss是一个标量

这就可以简单模拟一下,我们分别从loss和y求导,因为梯度的累加,我下面写了个x用于测试loss对x的导数,x1用于测试y对x的导数

运行一下,是和我们上述的结论一样的

x=torch.tensor([[1.],[2.],[3.]],requires_grad=True)
print(x)

tensor([[1.],
        [2.],
        [3.]], requires_grad=True)
k=torch.tensor([[1.,2,3],[1.,2,3]])
print(k)

y=torch.matmul(k,x)
print(y)

l=y.sum()
l

l.backward()
print(x.grad)
tensor([[1., 2., 3.],
        [1., 2., 3.]])
tensor([[14.],
        [14.]], grad_fn=<MmBackward0>)
tensor([[2.],
        [4.],
        [6.]])
x1=torch.tensor([[1.],[2.],[3.]],requires_grad=True)
print(x1)
tensor([[1.],
        [2.],
        [3.]], requires_grad=True)
k=torch.tensor([[1.,2,3],[1.,2,3]])
print(k)

y=torch.matmul(k,x1)
print(y)

# y.backward() 
# 不传入一个与y同形状的矩阵就会报错:RuntimeError: grad can be implicitly created only for scalar outputs

y.backward(torch.ones(2,1)) 
print(x1.grad)
tensor([[1., 2., 3.],
        [1., 2., 3.]])
tensor([[14.],
        [14.]], grad_fn=<MmBackward0>)
tensor([[2.],
        [4.],
        [6.]])
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊熊想读研究生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值