跟着李沐学AI【07 自动求导】
记录和处理在跟着学习时产生的问题,难点有些多,需要好好消化。
自动求导的实现:
一、 函数y = 2*(xTx)
(1)设置一个列向量x,并对其求导
import torch
x = torch.arange(4.0)
x.requires_grad_(True) #另外需要一个地方来存储梯度
x.grad #默认值是None
显示出x张量如下所示:
(2)计算y = 2*torch.dot(x,x) 即2*x向量的内积
y = 2*torch.dot(x,x)
结果如图:
需要注意的是,此处应该修改一下代码,在后面加上‘.data’保留源数据,否则后期调用反向传播函数计算x分量的梯度时会报错!
改后如下所示:
y = 2*torch.dot(x,x).data
(3)通过调用反向传播函数来自动计算y关于x每个分量的梯度
y.backward() #调用反向传播函数,求导
x.grad #访问导数
结果如下:
y = 2*( xT x ) = 2* || x ||^2 ,当对x求偏导时便等于2* 2* x = 4* x。
因此,当验证 x.grad == 4*x 时结果为True
二、 另一些函数
(1)默认情况下pytorch会累积梯度,需要清除之前的值
x.grad.zero_() #将梯度清零
y = x.sum() # y = x1+x2+...+xn
y.backward() # y分别对x1、x2...xn分量求偏导,得到的结果为全1,并存入x.grad中
x.grad
x的梯度清零后,再对求和函数求偏导,结果为全1:
(2)求批量中每个样本单独计算的偏导数之和
x.grad.zero_() # 对x的梯度清零
y = x * x
y.sum().backward() #等价于y.backward(torch.ones(len(x)))
x.grad
先计算了y.sum()得到一个标量,然后再调用反向传播函数,得到x的梯度为
(3)将某些计算移动到记录的计算图之外
x.grad.zero_ #梯度清零
y = x * x
u = y.detach() #将y看做一个常数赋值于u
z = u * x #z相对于常数u乘x
z.sum().backward()
x.grad == u #z对x求偏导后得常数u
y是关于x 的函数,而u不是,它是常数。故z = u * x视为常数与变量x相乘,最后对x求偏导的值为常数u:
(4)可以计算得到的变量的梯度
#使用很复杂的python控制流,依然能求导
def f(a): #f(a)是a的分段性函数
b = a * 2
while b.norm() < 1000:
b = b * 2
if b.sum() > 0:
c = b
else :
c = 100 * b
return c
a = torch.randn(size=(), requires_grad=True) #给a的梯度分配存储空间
d = f(a)
d.backward() # d对a求偏导
a.grad == d/a
判断a的梯度是否等于函数f(a) / a :