跟李沐学AI:chapter_preliminaries/index/autograd、自动求导、动态构建计算图、requires_grad_(True)&detach、backward()函数

系列文章目录

跟李沐学AI:chapter_preliminaries/index/autograd下的笔记以及课后练习


一、笔记

1.自动求导&计算图

pytorch处理自动求导时将每一次计算代码表示成一个无环图,并在求导结束后自动销毁。

动态构建计算图: 在执行每一步涉及张量的操作时,PyTorch都会记录这个操作及其输入和输出的关系,形成一个节点在网络计算图中。例如,当执行conv_layer(input_data)时,会创建一个新的节点代表卷积运算,该节点的输入是input_data,输出是经过卷积后的张量。

在这里插入图片描述

实时更新与销毁: 每次前向、反向传播都根据当前输入数据即时构建一个全新的动态计算图,且在完成一次前向传播和相应的反向传播后,系统不会持久保存这次计算图的具体结构,而是会在下次前向传播时重新构建。

注意到:
y=x.sum()
y.backward()
x.grad
执行两次backward操作仍然成功,即期间并未销毁计算图,应该是sum()函数本身的原因,能够不销毁计算图。

2.requires_grad_(True)以及detach

x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)

这里x.requires_grad=True 的作用是让 backward 可以追踪这个参数并且计算它的梯度,且后续关于x的函数也默认requires_grad参数时True,如若detach则不跟踪关于该变量的导数

x.grad.zero_()
y = x * x
u = y.detach()
z = u * x

z.sum().backward()
x.grad == u

3.张量进行反向传播

# 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
# 本例只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x #对应位相乘,y.shape==x.shape
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad

4.backward()函数的说明

刚才说到:对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
用一个例子解释:
以下转载自: https://www.cnblogs.com/bytesfly/p/why-need-gradient-arg-in-pytorch-backward.html
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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, x2.grad, x3.grad)

输出:

tensor(1.1000) tensor(1.4000) tensor(1.)

总结:

backward()不能对向量使用,必须要找一个标量函数进行反向传播求导,往往通过sum函数得到标量结果再回去求导,求和并不影响每个结果向量对各个变量的导数。

二、课后作业

1.题目

在这里插入图片描述

2.解答

1.需要暂存第一阶导数并计算两次。

2.报错提示:

尝试第二次向后遍历图(或者在已释放的张量之后直接访问已保存的张量)。当您调用.backward()或>autograd.grad()时,将释放图中保存的中间值。如果需要第二次向后遍历图,或者在调用backward后需要访>问保存的张量,则指定retain_graph=True。

pytorch处理自动求导时采用计算图机制,将代码分解成操作子,将计算表示成一个无环图
y.backward(retain_graph=True),若不设置,则在backward操作之后该无环图析构无法进行再次求导操作

3.报错:

grad can be implicitly created only for scalar outputs(backward()只能对标量进行操作)

a = torch.randn(size=(2,1), requires_grad=True)
a
d = f(a)
d.sum().backward()
a.grad
a.grad == d / a#对应位相除

输出:

tensor([[-1.3022],
[ 0.0885]], requires_grad=True)
tensor([[1024.],
[1024.]])
tensor([[True],
[True]])

4.修改函数计算标量对矩阵的导数:

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=(2,2), requires_grad=True)
a=torch.tensor([[1,2],[3,4]],dtype=torch.float32,requires_grad=True)
d = f(a)
d.sum().backward()
a.grad
a.grad == d / a

输出:

tensor([[256., 256.],
[256., 256.]])
tensor([[True, True],
[True, True]])

5.结合calculus一章下的作图代码,修改函数如下:

def get_tangent(f,x,h):
    return (f(x+h)-f(x))/h
x = np.arange(-3.15, 3.15, 0.01)
plot(x, [np.sin(x), get_tangent(np.sin,x,0.01)], 'x', 'f(x)', legend=['f(x)', 'Tangent function of sin(x)'])

输出如下:
在这里插入图片描述

较为奇怪的是如果定义get_tangent函数为:(f(x+h)-f(x-h))/2*h图像就会错得离谱(欢迎评论区指教)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值