backward方法中gradient参数的意义

首先,我们要清楚使用backward()的目的,是为了求出某个张量对于某些标量节点的梯度。

举个例子:

x = [ x 1 , x 2 , x 3 ] , z = x 1 2 + x 2 2 + x 3 2 + 6

那么 z.backward()表示的就是张量x对于标量z的梯度,即
[ z x 1 , z x 2 , z x 3 ]
代码则是:

import torch
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x ** 2 + 2
z = torch.sum(y)
z.backward()
print(x.grad)

这个还是比较好理解的。

但是当如果我们想对一个向量使用backward()的时候,又该如何使用呢

首先,我们要清楚,backward()的作用一直都是求出某个张量对于某些标量节点的梯度。若我们使用的对象是向量,我们可以用一些“方法”,来让我们的向量变为标量

举个例子:

x = [ x 1 , x 2 , x 3 ] , y = [ y 1 , y 2 , y 3 ]

y 1 = x 1 x 2 x 3 y 2 = x 1 + x 2 + x 3 y 3 = x 1 + x 2 x 3
自然,我们是无法直接对\(y\)使用backward()的(因为\(y\)是一个向量而非标量)

在这里,我们用一个函数\(a = g(y)\) (我们先不关心函数的具体实现),来把向量变为一个标量,然后,我们就可以对a使用backward(),表示的就是张量x对于标量a的梯度,即
[ a x 1 , a x 2 , a x 3 ]

从数学公式角度去理解:
a x 1 = a y 1 y 1 x 1 + a y 2 y 2 x 1 + a y 3 y 3 x 1 a x 2 = a y 1 y 1 x 2 + a y 2 y 2 x 2 + a y 3 y 3 x 2 a x 3 = a y 1 y 1 x 3 + a y 2 y 2 x 3 + a y 3 y 3 x 3
可以写成一下矩阵形式:

[ a x 1 , a x 2 , a x 3 ] = [ a y 1 , a y 2 , a y 3 ] [ y 1 x 1 y 1 x 2 y 1 x 3 y 2 x 1 y 1 x 2 y 1 x 3 y 3 x 1 y 1 x 2 y 1 x 3 ]
其中, [ y 1 x 1 y 1 x 2 y 1 x 3 y 2 x 1 y 1 x 2 y 1 x 3 y 3 x 1 y 1 x 2 y 1 x 3 ] 是可以计算得到的,而由于a的函数未确定,所以 [ a y 1 , a y 2 , a y 3 ] 是无法计算的。

那么,我们该如何去计算这个梯度呢?

答案是,用gradient参数定义出这个 [ a y 1 , a y 2 , a y 3 ]!

没错!gradient参数的数学意义其实就是这个 [ a y 1 , a y 2 , a y 3 ]!

让我们回到代码当中

# coding utf-8
import torch

x1 = torch.tensor(1, requires_grad=True, dtype=torch.float)
x2 = torch.tensor(2, requires_grad=True, dtype=torch.float)
x3 = torch.tensor(3, requires_grad=True, dtype=torch.float)
x = torch.tensor([x1, x2, x3])
y = torch.randn(3)
y[0] = x1 * x2 * x3
y[1] = x1 + x2 + x3
y[2] = x1 + x2 * x3
y.backward(torch.tensor([0.1, 0.2, 0.3], dtype=torch.float))
print(x1.grad)
print(x2.grad)
print(x3.grad)

'''
运行结果:
tensor(1.1000)
tensor(1.4000)
tensor(1.)
'''

在这代码当中backward()里的参数torch.tensor([0.1, 0.2, 0.3], dtype=torch.float), 其实就是定义了 [ a y 1 , a y 2 , a y 3 ]\([0.1,0.2,0.3]\),那么上面的过程即为:

[ a x 1 , a x 2 , a x 3 ] = [ 0.1 0.2 0.3 ] [ x 2 x 3 x 1 x 3 x 1 x 2 1 1 1 1 x 3 x 2 ] = [ 0.1 0.2 0.3 ] [ 6 3 2 1 1 1 1 3 2 ] = [ 1.1 , 1.4 , 1.0 ]
这么一看是不是恍然大悟了

为了验证我们的合理性,我们这里可以试着给出a的实际定义,让 [ a y 1 , a y 2 , a y 3 ] 变得可以计算。

a = 0.1 y 1 + 0.2 y 2 + 0.3 y 3

这样刚好对应于我们上面代码中定义的gradient,而且a是一个标量,可以直接使用backward()

让我们再重写代码,对a使用backward(),观察是否能得到同样的结果

# coding utf-8
import torch

x1 = torch.tensor(1, requires_grad=True, dtype=torch.float)
x2 = torch.tensor(2, requires_grad=True, dtype=torch.float)
x3 = torch.tensor(3, requires_grad=True, dtype=torch.float)
y = torch.randn(3)
y[0] = x1 * x2 * x3
y[1] = x1 + x2 + x3
y[2] = x1 + x2 * x3
A = 0.1 * y[0] + 0.2 * y[1] + 0.3 * y[2]
# x = torch.tensor([x1, x2, x3])
# y.backward(torch.tensor([0.1, 0.2, 0.3], dtype=torch.float))
A.backward()
print(x1.grad)
print(x2.grad)
print(x3.grad)

'''
运行结果:
tensor(1.1000)
tensor(1.4000)
tensor(1.)
'''

运行结果是一样的,证明我们对gradient的解释是合理的。

参考:https://www.cnblogs.com/zhouyang209117/p/11023160.html

posted @ 2023-04-14 00:02  Davy-Chen  阅读(1087)  评论(4编辑  收藏  举报