PyTorch使用自动微分模块的方法和理解
作者:小言从不摸鱼
自动微分(Autograd)模块对张量做了进一步的封装,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,在神经网络的反向传播过程中,Autograd 模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新。
🍔 梯度基本计算
我们使用 backward 方法、grad 属性来实现梯度的计算和访问.
import torch
1.1 单标量梯度的计算
# y = x**2 + 20 def test01(): # 定义需要求导的张量 # 张量的值类型必须是浮点类型 x = torch.tensor(10, requires_grad=True, dtype=torch.float64) # 变量经过中间运算 f = x ** 2 + 20 # 自动微分 f.backward() # 打印 x 变量的梯度 # backward 函数计算的梯度值会存储在张量的 grad 变量中 print(x.grad)
1.2 单向量梯度的计算
# y = x**2 + 20 def test02(): # 定义需要求导张量 x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64) # 变量经过中间计算 f1 = x ** 2 + 20 # 注意: # 由于求导的结果必须是标量 # 而 f 的结果是: tensor([120., 420.]) # 所以, 不能直接自动微分 # 需要将结果计算为标量才能进行计算 f2 = f1.mean() # f2 = 1/2 * x # 自动微分 f2.backward() # 打印 x 变量的梯度 print(x.grad)
1.3 多标量梯度计算
# y = x1 ** 2 + x2 ** 2 + x1*x2 def test03(): # 定义需要计算梯度的张量 x1 = torch.tensor(10, requires_grad=True, dtype=torch.float64) x2 = torch.tensor(20, requires_grad=True, dtype=torch.float64) # 经过中间的计算 y = x1**2 + x2**2 + x1*x2 # 将输出结果变为标量 y = y.sum() # 自动微分 y.backward() # 打印两个变量的梯度 print(x1.grad, x2.grad)
1.4 多向量梯度计算
def test04(): # 定义需要计算梯度的张量 x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64) x2 = torch.tensor([30, 40], requires_grad=True, dtype=torch.float64) # 经过中间的计算 y = x1 ** 2 + x2 ** 2 + x1 * x2 print(y) # 将输出结果变为标量 y = y.sum() # 自动微分 y.backward() # 打印两个变量的梯度 print(x1.grad, x2.grad) if __name__ == '__main__': test04()
1.5 运行结果💯
tensor(20., dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)
tensor(40., dtype=torch.float64) tensor(50., dtype=torch.float64)
tensor([1300., 2800.], dtype=torch.float64, grad_fn=<AddBackward0>)
tensor([50., 80.], dtype=torch.float64) tensor([ 70., 100.], dtype=torch.float64)
🍔 控制梯度计算
我们可以通过一些方法使得在 requires_grad=True 的张量在某些时候计算不进行梯度计算。
import torch
2.1 控制不计算梯度
def test01(): x = torch.tensor(10, requires_grad=True, dtype=torch.float64) print(x.requires_grad) # 第一种方式: 对代码进行装饰 with torch.no_grad(): y = x ** 2 print(y.requires_grad) # 第二种方式: 对函数进行装饰 @torch.no_grad() def my_func(x): return x ** 2 print(my_func(x).requires_grad) # 第三种方式 torch.set_grad_enabled(False) y = x ** 2 print(y.requires_grad)
2.2 注意: 累计梯度
def test02(): # 定义需要求导张量 x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64) for _ in range(3): f1 = x ** 2 + 20 f2 = f1.mean() # 默认张量的 grad 属性会累计历史梯度值 # 所以, 需要我们每次手动清理上次的梯度 # 注意: 一开始梯度不存在, 需要做判断 if x.grad is not None: x.grad.data.zero_() f2.backward() print(x.grad)
2.3 梯度下降优化最优解
def test03(): # y = x**2 x = torch.tensor(10, requires_grad=True, dtype=torch.float64) for _ in range(5000): # 正向计算 f = x ** 2 # 梯度清零 if x.grad is not None: x.grad.data.zero_() # 反向传播计算梯度 f.backward() # 更新参数 x.data = x.data - 0.001 * x.grad print('%.10f' % x.data) if __name__ == '__main__': test01() test02() test03()
2.4 运行结果💯
True
False
False
False
tensor([ 5., 10., 15., 20.], dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)
🍔 梯度计算注意
当对设置 requires_grad=True 的张量使用 numpy 函数进行转换时, 会出现如下报错:
Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
此时, 需要先使用 detach 函数将张量进行分离, 再使用 numpy 函数.
注意: detach 之后会产生一个新的张量, 新的张量作为叶子结点,并且该张量和原来的张量共享数据, 但是分离后的张量不需要计算梯度。
import torch
3.1 detach 函数用法
def test01(): x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64) # Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead. # print(x.numpy()) # 错误 print(x.detach().numpy()) # 正确
3.2 detach 前后张量共享内存
def test02(): x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64) # x2 作为叶子结点 x2 = x1.detach() # 两个张量的值一样: 140421811165776 140421811165776 print(id(x1.data), id(x2.data)) x2.data = torch.tensor([100, 200]) print(x1) print(x2) # x2 不会自动计算梯度: False print(x2.requires_grad) if __name__ == '__main__': test01() test02()
3.3 运行结果💯
10. 20.]
140495634222288 140495634222288
tensor([10., 20.], dtype=torch.float64, requires_grad=True)
tensor([100, 200])
False
🍔 小节
本小节主要讲解了 PyTorch 中非常重要的自动微分模块的使用和理解。我们对需要计算梯度的张量需要设置 requires_grad=True 属性,并且需要注意的是梯度是累计的,在每次计算梯度前需要先进行梯度清零。
到此这篇关于PyTorch使用自动微分模块的文章就介绍到这了,更多相关PyTorch自动微分模块内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!