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()
相关推荐
初级炼丹师(爱说实话版)33 分钟前
nn.MultiheadAttention返回的注意力权重和标准的计算注意力权重的区别
人工智能·深度学习·机器学习
roman_日积跬步-终至千里35 分钟前
【人工智能基础】计算机视觉
人工智能·计算机视觉
敲代码不忘补水36 分钟前
Python Matplotlib 经典 3D 绘图类型:从二维到三维的可视化解析
开发语言·python·3d·数据分析·numpy·pandas·matplotlib
努力的小好1 小时前
【python】摄像头调用马赛克恶搞
python
AI小杨1 小时前
【数据分析】一、pandas数据处理指南:100个基于pandas数据预处理方法
python·数据挖掘·数据分析·pandas·pandas使用技巧
weixin_431470861 小时前
文本数据分析(nlp)
开发语言·python·深度学习·自然语言处理
終不似少年遊*1 小时前
数据分析-机器学习-第三方库使用基础
python·机器学习·数据挖掘·数据分析·numpy
天天要nx1 小时前
D79【 python 接口自动化学习】- python基础之HTTP
python
ystraw_ah1 小时前
python 画图例子
python
Francek Chen1 小时前
【大数据分析&深度学习】在Hadoop上实现分布式深度学习
人工智能·hadoop·分布式·深度学习·数据分析