11.22Pytorch_自动微分

七、自动微分

自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效。

1. 基础概念

  1. 张量

    Torch中一切皆为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。

  2. 计算图

    torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。

  3. 反向传播

    使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。

  4. 梯度

    计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。

2. 计算梯度

使用tensor.backward()方法执行反向传播,从而计算张量的梯度~

2.1 标量梯度计算

参考代码如下:

python 复制代码
import torch


def test001():
    # 1. 创建张量:必须为浮点类型
    x = torch.tensor(7.0, requires_grad=True, dtype=torch.float32)

    # 2. 操作张量
    y = x**2 + 2 * x + 7

    # 3. 计算梯度,也就是反向传播
    y.backward()

    # 4. 读取梯度值
    # 结果:tensor(16.)
    print(x.grad)


if __name__ == "__main__":
    test001()

2.2 向量梯度计算

错误预警:RuntimeError: grad can be implicitly created only for scalar outputs

百度翻译:RuntimeError:只能为标量输出隐式创建grad

参考代码:

python 复制代码
import torch


def test002():
    # 1. 创建张量:必须为浮点类型
    x = torch.tensor([1.0, 2.0, 5.3], requires_grad=True)

    # 2. 操作张量
    y = x**2 + 2 * x + 7

    # 3. 需要变为标量:求和或者求均值都可以
    # z = y.sum()
    z = y.mean()

    # 4. 计算梯度,也就是反向传播
    z.backward()

    # 5. 读取梯度值
    print(x.grad)


if __name__ == "__main__":
    test002()

2.3 多标量梯度计算

参考代码如下

python 复制代码
import torch


def test003():
    # 1. 创建两个标量
    x1 = torch.tensor(5.0, requires_grad=True, dtype=torch.float64)
    x2 = torch.tensor(3.0, requires_grad=True, dtype=torch.float64)

    # 2. 构建运算公式
    y = x1**2 + 2 * x2 + 7
    
    # 3. 计算梯度,也就是反向传播
    y.backward()
    
    # 4. 读取梯度值
    print(x1.grad, x2.grad)


if __name__ == "__main__":
    test003()

2.4 多向量梯度计算

代码参考如下

python 复制代码
import torch


def test004():
    # 1. 创建两个标量
    x1 = torch.tensor([5.0, 6.0, 7.5], requires_grad=True)
    x2 = torch.tensor([3.0, 5.2, 6.4], requires_grad=True)

    # 2. 构建运算公式
    y = x1**2 + 2 * x2 + 7

    # 3. 向量构建
    # z = y.sum()
    z = y.mean()

    # 4. 计算梯度,也就是反向传播
    z.backward()

    # 5. 读取梯度值
    print(x1.grad, x2.grad)


if __name__ == "__main__":
    test004()

3. 梯度上下文控制

梯度计算的上下文控制和设置对于管理计算图、内存消耗、以及计算效率至关重要。下面我们学习下Torch中与梯度计算相关的一些主要设置方式。

3.1 控制梯度计算

梯度计算是有性能开销的,有些时候我们只是简单的运算,并不需要梯度~

python 复制代码
import torch


def test001():
    x = torch.tensor(10.5, requires_grad=True)
    print(x.requires_grad)  # True

    # 1. 默认y的requires_grad=True
    y = x**2 + 2 * x + 3
    print(y.requires_grad)  # True

    # 2. 如果不需要y计算梯度-with进行上下文管理
    with torch.no_grad():
        y = x**2 + 2 * x + 3
    print(y.requires_grad)  # False

    # 3. 如果不需要y计算梯度-使用装饰器
    @torch.no_grad()
    def y_fn(x):
        return x**2 + 2 * x + 3

    y = y_fn(x)
    print(y.requires_grad)  # False

    # 4. 如果不需要y计算梯度-全局设置,需要谨慎
    torch.set_grad_enabled(False)
    y = x**2 + 2 * x + 3
    print(y.requires_grad)  # False


if __name__ == "__main__":
    test001()

3.2 累计梯度

默认情况下,当我们重复对一个自变量进行梯度计算时,梯度是累加的

python 复制代码
import torch


def test002():
    # 1. 创建张量:必须为浮点类型
    x = torch.tensor([1.0, 2.0, 5.3], requires_grad=True)

    # 2. 累计梯度:每次计算都会累计梯度
    for i in range(3):
        y = x**2 + 2 * x + 7
        z = y.mean()
        z.backward()
        print(x.grad)


if __name__ == "__main__":
    test002()

输出结果:

python 复制代码
tensor([1.3333, 2.0000, 4.2000])
tensor([2.6667, 4.0000, 8.4000])
tensor([ 4.0000,  6.0000, 12.6000])

3.3 梯度清零

大多数情况下是不需要梯度累加的,反向传播之前可以先对梯度进行清零

python 复制代码
import torch


def test002():
    # 1. 创建张量:必须为浮点类型
    x = torch.tensor([1.0, 2.0, 5.3], requires_grad=True)

    # 2. 累计梯度:每次计算都会累计梯度
    for i in range(3):
        y = x**2 + 2 * x + 7
        z = y.mean()
        # 2.1 反向传播之前先对梯度进行清零
        if x.grad is not None:
            x.grad.zero_()
            
        z.backward()
        print(x.grad)


if __name__ == "__main__":
    test002()

3.4 案例-函数最优解

通过梯度下降找到函数最小值~~损失函数是不是要找到最小值???

python 复制代码
import torch

# 导入plt
import matplotlib.pyplot as plt
import numpy as np


def test000():
    x = np.arange(-25, 15, 0.1)
    y = x**2 + 10 * x + 7
    # 绘制网格线表格
    plt.grid()
    plt.plot(x, y)
    plt.show()


def test001():
    # 1. 初始化x值
    x = torch.tensor(20.0, requires_grad=True)
    # 2. 找到损失函数的最小值:迭代进行梯度下降
    for i in range(1000):
        # 2.1 计算损失函数
        y = x**2 + 10 * x + 7
        # 2.2 梯度清零
        if x.grad is not None:
            x.grad.data.zero_()

        # 2.3 计算梯度
        y.backward()

        # 梯度下降
        x.data.sub_(0.01 * x.grad.data)

        # 打印结果
        print(x.item(), y.item())


if __name__ == "__main__":
    test000()
    test001()

4. 梯度计算小结

在进行梯度计算或者类型转换时,有一些细节需要注意下~

4.1 转换错误

当requires_grad=True时,在调用numpy转换为ndarray时报错如下:

RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.

4.2 错误处理

使用detach()方法创建张量的叶子节点即可

python 复制代码
import torch


def test001():
    x = torch.tensor([1, 2, 3], requires_grad=True, dtype=torch.float32)
    print(x.detach().numpy())
    pass


if __name__ == "__main__":
    test001()

4.3 叶子结点

detach()产生的张量是作为叶子结点存在的,并且该张量和原张量共享数据,只是该张量不需要计算梯度。

python 复制代码
import torch


def test001():
    x = torch.tensor([1, 2, 3], requires_grad=True, dtype=torch.float32)
    x_np = x.detach()
    print("是两个东西:", id(x), id(x_np))
    print("数据是共享的:", id(x.data), id(x_np.data))
    
    # 修改其中一个张量的值
    x_np[1:] = 100
    print(x, x_np)


if __name__ == "__main__":
    test001()
相关推荐
IT_陈寒3 小时前
React 18实战:7个被低估的Hooks技巧让你的开发效率提升50%
前端·人工智能·后端
数据智能老司机4 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
逛逛GitHub4 小时前
飞书多维表“独立”了!功能强大的超出想象。
人工智能·github·产品
机器之心4 小时前
刚刚,DeepSeek-R1论文登上Nature封面,通讯作者梁文锋
人工智能·openai
数据智能老司机5 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机5 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机5 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i5 小时前
drf初步梳理
python·django
每日AI新事件5 小时前
python的异步函数
python
这里有鱼汤6 小时前
miniQMT下载历史行情数据太慢怎么办?一招提速10倍!
前端·python