autograd-自动求导系统

torch.autograd

权值的更新需要求解梯度,pytorch提供了自动求导系统,我们只需要搭建前向传播计算图,由autograd的方法就可以得到所有张量的梯度。

其中最常用的方法是backward

torch.autograd.backward()

在这里插入图片描述
功能:自动求取各个节点的梯度

  • tensors: 用于求导的张量,如 loss
  • retain_graph :保存计算图(由于pytorch采用动态图机制,在每一次反向传播之后计算图都会被释放,如果后面还需要使用计算图,该参数需要为True)
  • create_graph:创建导数计算图,用于高阶求导
  • grad_tensors:多梯度权重(计算多个loss时,需要设置各个loss的权重)
回顾计算图与梯度求导

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200606143640632.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NhZ2FjaXR5XzExMjU=,size_16,color_FFFFFF,t_70 在这里插入图片描述
通过构建前向传播计算图和自动求导系统,对y执行backward方法,即可得到所有节点的梯度。

retain_graph举例:

w = torch.tensor([1.],requires_grad=True)
x = torch.tensor([2.],requires_grad=True)

a = torch.add(w,x)
b = torch.add(w,1)
y = torch.mul(a,b)

y.backward()
print(w.grad)

结果:

tensor([5.])

张量中的y.backward() 实际上调用了torch.autograd.backward()

假设还需要执行一次反向传播:如果直接在上述代码后面加上y.backward()则会报错,
因为计算图已经被释放。
解决方案:在上一次反向传播中保存计算图则可以解决上述问题,即retain_graph=True

y.backward(retain_graph=True)
print(w.grad) #tensor([5.])
y.backward()

grad_tensors举例:

w = torch.tensor([1.],requires_grad=True)
x = torch.tensor([2.],requires_grad=True)

a = torch.add(w,x)
b = torch.add(w,1)

y0 = torch.mul(a,b) # y0 = (x+w) * (w+1)  dy0/dw = 5
y1 = torch.add(a,b) # y1 = (x+w) + (w+1)  dy1/dw = 2

loss = torch.cat([y0,y1],dim=0) # tensor([6., 5.])
# print(loss)
grad_tensors = torch.tensor([1.,2.]) # 多个梯度的权重

loss.backward(gradient=grad_tensors) # gradient 传入torch.autograd.backward()中的grad_tensors

print(w.grad) # dy0/dw * 1.+ dy1/dw * 2. = 9
tensor([9.])
torch.autograd.grad()

在这里插入图片描述
功能:求取梯度

  • outputs: 用于求导的张量,如 loss
  • inputs : 需要求梯度的张量
  • create_graph : 创建导数计算图,用于高阶求导
  • retain_graph : 保存计算图
  • grad_outputs:多梯度权重
x = torch.tensor([3.],requires_grad=True)
y = torch.pow(x,2) #y = x**2, 二阶导数是2

# 只有在一阶导数中对导数创建计算图,才可以对导数求导
grad_1 = torch.autograd.grad(y,x,create_graph=True) # grad_1 = dy/dx =2x =2*3 =6
print(grad_1)# 元组

grad_2 = torch.autograd.grad(grad_1[0],x)# grad_2 = d(dy/dx)/dx = 2
print(grad_2)

结果:

(tensor([6.], grad_fn=<MulBackward0>),)
(tensor([2.]),)
autograd小贴士:
  1. 梯度不自动清零
  2. 依赖于叶子结点的结点, requires_grad默认为True
  3. 叶子结点不可执行in-place
  1. 梯度在每次反向传播之后会叠加
w = torch.tensor([1.],requires_grad=True)
x = torch.tensor([2.],requires_grad=True)

for i in range(4):
    a = torch.add(w,x)
    b = torch.add(w,1)
    y = torch.mul(a,b)

    y.backward()
    print(w.grad)

结果:

tensor([5.])
tensor([10.])
tensor([15.])
tensor([20.])

为了避免梯度叠加,需要在代码最后加上w.grad.zero_()(_下划线操作叫做原位操作
这样每一次计算完都清零,梯度不会叠加
结果:

tensor([5.])
tensor([5.])
tensor([5.])
tensor([5.])
  1. 依赖于叶子结点的结点, requires_grad默认为True
    在这里插入图片描述
w = torch.tensor([1.],requires_grad=True)
x = torch.tensor([2.],requires_grad=True)

 a = torch.add(w,x)
 b = torch.add(w,1)
 y = torch.mul(a,b)

 # 默认都是True
 print(a.requires_grad,b.requires_grad,y.requires_grad)
  1. 叶子结点不可执行in-place
w = torch.tensor([1.],requires_grad=True)
x = torch.tensor([2.],requires_grad=True)

a = torch.add(w,x)
b = torch.add(w,1)
y = torch.mul(a,b)

w.add_(1)
y.backward()

运行会报错:

RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.

什么是in-place operation

a = torch.ones((1,))
print(id(a),a)

a = a + torch.ones((1,))
print(id(a),a)# 内存地址和上面的a不同

结果:开辟了新的内存地址,不是原位操作

1086097577608 tensor([1.])
1086068988888 tensor([2.])

若改为:

a += torch.ones((1,))
print(id(a),a)# 内存地址不变,也就是原位操作

结果:内存地址不变,也就是原位操作

596027098760 tensor([1.])
596027098760 tensor([2.])

可从计算图的梯度求解过程来理解:
求y对w的梯度,需要先求y对a的梯度,y对a求导得w+1,在反向传播时与叶子张量w有关,而在前向传播时会记录w的地址,在反向传播时,会根据记录的w的地址来得到w的值;如果在反向传播之前改变了w地址中的数据,那么梯度求解则会出错

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值