目录
[1. 梯度基本计算](#1. 梯度基本计算)
[2. 控制梯度计算](#2. 控制梯度计算)
[3. 梯度计算注意](#3. 梯度计算注意)
[4. 小节](#4. 小节)
个人主页:Icomi
专栏地址:PyTorch入门
在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架,为构建和训练神经网络提供了高效且灵活的平台。神经网络作为人工智能的核心技术,能够处理复杂的数据模式。通过 PyTorch,我们可以轻松搭建各类神经网络模型,实现从基础到高级的人工智能应用。接下来,就让我们一同走进 PyTorch 的世界,探索神经网络与人工智能的奥秘。本系列为PyTorch入门文章,若各位大佬想持续跟进,欢迎与我交流互关。
咱们已经见识了 PyTorch 为张量封装的众多实用计算函数,这些函数就像我们在数据处理旅程中的得力助手,帮我们解决了不少计算难题。但深度学习的探索之旅永无止境,接下来,我们要踏入一个更为关键且神奇的领域 ------ 自动微分(Autograd)模块。
想象一下,我们构建的神经网络就像一台超级复杂的智能机器,它能从海量的数据中学习规律,做出精准的预测。而在这台 "智能机器" 的运行过程中,参数的调整就如同精细地调校机器的各个零部件,让它能不断优化性能。这时,自动微分(Autograd)模块就如同一位无比精准的 "调校大师",对张量做了进一步的封装,赋予了它们一项极为强大的能力 ------ 自动求导。
自动微分模块可不是一个普通的工具,它可是构成神经网络训练的必要模块,就如同发动机对于汽车的重要性一样。在神经网络的训练过程中,我们可以把它看作是一个幕后英雄,默默地推动着整个网络的优化进程。
具体来说,在神经网络的反向传播过程中,这个 "调校大师" Autograd 模块会基于正向计算的结果对当前的参数进行微分计算。这就好比在我们驾驶一辆汽车时,根据当前行驶的路线和目的地,通过精确计算来调整方向盘的角度和油门的力度。Autograd 模块通过这种微分计算,精确地算出每个参数对最终结果的影响程度,从而实现网络权重参数的更新,让神经网络能够不断学习和进步,变得越来越 "聪明"。
接下来我们要深入学习这个自动微分(Autograd)模块,掌握它的原理和使用方法,这将为我们理解神经网络的训练机制打开一扇关键的大门。
1. 梯度基本计算
我们使用 backward 方法、grad 属性来实现梯度的计算和访问.
python
import torch
# 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)
# 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)
# 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)
# 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()
2. 控制梯度计算
我们可以通过一些方法使得在 requires_grad=True 的张量在某些时候计算不进行梯度计算。
python
import torch
# 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. 注意: 累计梯度
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)
# 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()
3. 梯度计算注意
当对设置 requires_grad=True 的张量使用 numpy 函数进行转换时, 会出现如下报错:
Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
此时, 需要先使用 detach 函数将张量进行分离, 再使用 numpy 函数.
注意: detach 之后会产生一个新的张量, 新的张量作为叶子结点,并且该张量和原来的张量共享数据, 但是分离后的张量不需要计算梯度。
python
import torch
# 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()) # 正确
# 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()
4. 小节
本小节主要讲解了 PyTorch 中非常重要的自动微分模块的使用和理解。我们对需要计算梯度的张量需要设置 requires_grad=True 属性,并且需要注意的是梯度是累计的,在每次计算梯度前需要先进行梯度清零。