第八课 自动求导
理论部分
求导主要是有两种方式,第一种是符号求导,第二种是数值求导。
自动求导的内部其实是计算图,计算图是将代码分解成操作子,将计算表示成一个无环图。
计算图的显式构造:(mxnet)
计算图的隐式构造:(mxnet)
反向累计是比较常用的。
总结:
构造计算图
前向:执行图,存储中间结果(设置中间变量)
反向:从相反的方向执行图
去除不需要的枝
深度学习耗GPU资源体现在内存复杂度上面。因为求梯度时需要存储前面的结果。
实践部分
#自动求导
import torch
x=torch.arange(4.0) #requires_grad=True'''
#x=tensor([0., 1., 2., 3.])
x.requires_grad = True #计算y关于x的梯度之前需要存储梯度,等价于上句话的最后一个参数.
#print(x.grad) #x.grad 默认值是none
########################################################################################
#计算y
y = 2 * torch.dot(x,x) #结果是个标量
print('###################################################')
print('torch.dot(x,x):\t',torch.dot(x,x))
print('y:\t',y)
y.backward() #通过反向传播函数自动计算y关于x每个分量的梯度
print('x.grad:\t',x.grad)
print('x.grad==4*x?\t',x.grad==4*x)
print('###################################################')
#在默认情况下,torch会累积梯度,所以需要对之前的值进行清零
########################################################################################
x.grad.zero_() #下划线是指进行重写操作,也就是对x的梯度进行重写为0
#print('x.grad:\t',x.grad)
y=x.sum() #结果是个标量
#print(y)
y.backward()
print('x.grad:\t',x.grad) #如果把16行注释,则结果是x.grad:tensor([ 1., 5., 9., 13.])
#求和,梯度是1
print('###################################################')
########################################################################################
#在深度学习中,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和
x.grad.zero_()
y=x*x #结果是个向量
y.sum().backward() #向量的求导转换成对标量求导(先求和再求导)
print('x.grad:\t',x.grad)
print('###################################################')
########################################################################################
#将某些计算移动到记录的计算图之外,用于固定某些网络参数
x.grad.zero_()
y=x*x #结果是个向量
u=y.detach() #把u做成常数,而非关于y的函数
z=u*x #结果是个向量
z.sum().backward() #向量的求导转换成对标量求导(先求和再求导)
print('x.grad==u:\t',x.grad==u)
x.grad.zero_()
y.sum().backward()
print('x.grad==2*x?\t',x.grad==2*x)
print('###################################################')
########################################################################################
#即使构建函数的计算图需要通过Python控制流(例如条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度
def f(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)
d = f(a)
d.backward()
print('a.grad==d/a\t',a.grad==d/a)
print('###################################################')
###################################################
torch.dot(x,x): tensor(14., grad_fn=<DotBackward0>)
y: tensor(28., grad_fn=<MulBackward0>)
x.grad: tensor([ 0., 4., 8., 12.])
x.grad==4*x? tensor([True, True, True, True])
###################################################
x.grad: tensor([1., 1., 1., 1.])
###################################################
x.grad: tensor([0., 2., 4., 6.])
###################################################
x.grad==u: tensor([True, True, True, True])
x.grad==2*x? tensor([True, True, True, True])
###################################################
a.grad==d/a tensor(True)
###################################################进程已结束,退出代码0