Pytorch自动求梯度

  在深度学习中,我们经常需要对函数求梯度(gradient)。PyTorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行反向传播。
  Tensor 是 autograd 包的核心类,如果将其属性 .requires_grad 设置为 True,它将开始追踪(track)在其上的所有操作(这样就可以利用链式法则进行梯度传播了)。完成计算后,可以调用 .backward() 来完成所有梯度计算。此Tensor的梯度将累积到 .grad 属性中。
  如果不想要被继续追踪,可以调用 .detach() 将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了。此外,还可以用 with torch.no_grad() 将不想被追踪的操作代码块包裹起来,这种方法在评估模型的时候很常用 , 因为在评估模型时 , 我们并不需要计算可训练参数(requires_grad=True)的梯度。
  Function是另外一个很重要的类。Tensor和Function互相结合就可以构建一个记录有整个计算过程的有向无环图(DAG)。每个Tensor都有一个 .grad_fn 属性,该属性即创建该Tensor的Function, 就是说该Tensor是不是通过某些运算得到的,若是,则 grad_fn 返回一个与这些运算相关的对象,否则是None。
 
例子:
  创建一个Tensor并设置 requires_grad=True:
x = torch.ones(2, 2, requires_grad=True)
print(x)
print(x.grad_fn)   #x是直接创建的,所以它没有grad_fn
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
None
 
例子:
y = x + 2
print(y)
print(y.grad_fn)
tensor([[3., 3.], [3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x7fab97795828>

 

更复杂的运算操作:
z = y * y * 3
out = z.mean()
print(z)
print(out)
tensor([[27., 27.], [27., 27.]], grad_fn=<MulBackward0>)
tensor(27., grad_fn = <MeanBackward0>)

 

通过 .requires_grad_() 来用 in-place 的方式改变 requires_grad 属性:
a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False
a = ((a * 3) / (a - 1))
print(a.requires_grad) # False
a.requires_grad_(True)
print(a.requires_grad) # True
b = (a * a).sum()
print(b.grad_fn)
False
True
<SumBackward0 object at 0x7fab977a33c8>

 

梯度
x = torch.ones(2, 2, requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
out.backward() # 等价于 out.backward(torch.tensor(1.))
print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])

      

注意:grad 在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前都需要把梯度清零。 
# 再来反向传播一次,注意grad是累加的
out2 = x.sum()
out2.backward()
print(x.grad)

out3 = x.sum()
x.grad.data.zero_()
out3.backward()
print(x.grad)
梯度未清零,累加梯度
tensor([[5.5000, 5.5000],
[5.5000, 5.5000]])
 
梯度清零后,x的梯度为1
tensor([[1., 1.],
[1., 1.]]) 

 

注意在 y.backward()时,如果 y 是标量,则不需要为 backward() 传入任何参数否则,需要传入一个与 y  同形的 Tensor。这样的原因简单来说就是为了避免向量(甚至更高维张量)对张量求导,因此转换成标量对张量进行求导。 
举例
  假设形状为  $m \times n$  的矩阵 $ \mathrm{X}$  经过运算得到了  $\boldsymbol{p} \times \boldsymbol{q}$  的矩阵  $\mathrm{Y}$,$\mathrm{Y} $ 又经过运算得到了  $s \times t$  的矩阵  $\mathrm{Z}$  。那么按照梯 度计算的规则, $ \frac{d Z}{d Y} $ 应该是一个  $s \times t \times p \times q $  四维张量, $ \frac{d Y}{d X}$  是一个  $p \times q \times m \times n$  的四维张量,而如何对 4 维张量进行计算是一个比较复杂的问题。
  为了避免这个问题,我们不允许张量对张量求导,只允许标量对张量求导,求导结果是和自变量同形的张量。 所以必要时我们要把张量通过将所有张量的元素加权求和的方式转换为标量,举个例子,假设  $y$  由自变量  $x$  计算而来,  $w$  是和  $y$  同形的张量,则 $y. backward  (w)$  的含义是:先计算 $𝒍 = 𝐭𝐨𝐫𝐜𝐡. 𝐬𝐮𝐦(𝒚 ∗ 𝒘)$ , 因此  $l$  是一个标量,然后我们再求  $l$  对自变量  $x$  的导数。
 
posted @ 2021-10-18 16:24  多发Paper哈  阅读(222)  评论(0编辑  收藏  举报
Live2D