当前位置: 首页 > news >正文

自动微分模块

自动微分模块

自动微分就是自动计算梯度值,也就是计算导数。
  • 什么是梯度
    • 对函数求导的值就是梯度
  • 什么是梯度下降法
    • 是一种求最优梯度值的方法,使得损失函数的值最小
  • 梯度经典语录
    • 对函数求导得到的值就是梯度
(在数值上的理解)
(在方向上理解)
训练神经网络时,最常用的算法就是反向传播。在该算法中,参数(模型权重)会根据损失函数关于对应参数的梯度进行调整。为了计算这些梯度,PyTorch内置了名为 torch.autograd 的微分模块。它支持任意计算图的自动梯度计算:
接下来我们使用这个结构进行自动微分模块的介绍,我们使用 backward 方法、grad 属性来实现梯度的计算和访问。

梯度基本计算

  • pytorch不支持向量张量对向量张量的求导,只支持标量张量对向量张量的求导
    • x如果是张量,y必须是标量(一个值)才可以进行求导
  • 计算梯度: y.backward(), y是一个标量
  • 获取x点的梯度值: x.grad, 会累加上一次的梯度值
  • 标量张量梯度计算
# 定义一个标量张量(点)
# requires_grad=:默认为False,不会自动计算梯度;为True的话是将自动计算的梯度值保存到grad中
x = torch.tensor(10, requires_grad=True, dtype=torch.float32)
print("x-->", x)

# 定义一个曲线
y = 2 * x ** 2
print("y-->", y)
# 查看梯度函数类型,即曲线函数类型
print(y.grad_fn)

# 计算x点的梯度
# 此时y是一个标量,可以不用使用y.sum()转换成标量
print("y.sum()-->", y.sum())
# y'|(x=10) = (2*x**2)'|(x=10) = 4x|(x=10) = 40
# 因为y是一个标量,所以不需要调用y.sum().backward()方法会自动计算y关于x的梯度,并将结果存储在x.grad中。

#y.sum().backward()
y.backward()

# 打印x的梯度值
print("x的梯度值是:", x.grad)

"""
x--> tensor(10., requires_grad=True)
y--> tensor(200., grad_fn=<MulBackward0>)
<MulBackward0 object at 0x000002042A749420>
y.sum()--> tensor(200., grad_fn=<SumBackward0>)
x的梯度值是: tensor(40.)
"""

y.sum()函数用于计算张量y中所有元素的总和。这个函数非常有用,特别是在处理多维张量时,可以将多维张量缩减为一个标量值。这在许多深度学习任务中非常常见,例如在计算损失函数时,通常需要将模型的输出张量缩减为一个标量损失值,以便进行反向传播。
  • 向量张量梯度计算
# 定义一个向量张量(点)
x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float32)
print("x-->", x)

# 定义一个曲线
y = 2 * x ** 2
print("y-->", y)

# 计算梯度
# x和y都是向量张量,不能进行求导,需要将y转换成标量张量-->y.sum()
# y'|(x=10) = (2*x**2)'|(x=10) = 4x|(x=10) = 40
# y'|(x=20) = (2*x**2)'|(x=20) = 4x|(x=20) = 80
y.sum().backward()

# 打印x的梯度
print("x.grad-->", x.grad)

"""
x--> tensor([10., 20.], requires_grad=True)
y--> tensor([200., 800.], grad_fn=<MulBackward0>)
x.grad--> tensor([40., 80.])
"""

梯度下降法求最优解

  • 梯度下降法公式: w = w - r * grad (r是学习率, grad是梯度值)
  • 清空上一次的梯度值: x.grad.zero_()
# 求 y = x**2 + 20 的极小值点 并打印y是最小值时 w的值(梯度)
# 1 定义点 x=10 requires_grad=True  dtype=torch.float32
# 2 定义函数 y = x**2 + 20
# 3 利用梯度下降法 循环迭代1000 求最优解
# 3-1 正向计算(前向传播)
# 3-2 梯度清零 x.grad.zero_()
# 3-3 反向传播
# 3-4 梯度更新 x.data = x.data - 0.01 * x.grad


# 1 定义点x=10 requires_grad=True  dtype=torch.float32
x = torch.tensor(10, requires_grad=True, dtype=torch.float32)

# 2 定义函数 y = x ** 2 + 20
y = x ** 2 + 20
print('开始 权重x初始值:%.6f (0.01 * x.grad):无 y:%.6f' % (x, y))

# 3 利用梯度下降法 循环迭代1000 求最优解
for i in range(1, 1001):

    # 3-1 正向计算(前向传播)
    y = x ** 2 + 20

    # 3-2 梯度清零 x.grad.zero_()
    # 默认张量的 grad 属性会累加历史梯度值 需手工清零上一次的提取
    # 一开始梯度不存在, 需要做判断
    if x.grad is not None:
        x.grad.zero_()

    # 3-3 反向传播
    y.sum().backward()
 
    # 3-4 梯度更新 x.data = x.data - 0.01 * x.grad
    # x.data是修改原始x内存中的数据,前后x的内存空间一样;如果使用x,此时修改前后x的内存空间不同
    x.data = x.data - 0.01 * x.grad  # 注:不能 x = x - 0.01 * x.grad 这样写

    print('次数:%d 权重x: %.6f, (0.01 * x.grad):%.6f y:%.6f' % (i, x, 0.01 * x.grad, y))
        
print('x:', x, x.grad, 'y最小值', y)

梯度计算注意点

  • 不能将自动微分的张量转换成numpy数组,会发生报错,可以通过detach()方法实现
# 定义一个张量
x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)

# 将x张量转换成numpy数组
# 发生报错,RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
# 不能将自动微分的张量转换成numpy数组
# print(x1.numpy())

# 通过detach()方法产生一个新的张量,作为叶子结点
x2 = x1.detach()
# x1和x2张量共享数据,但是x2不会自动微分
print(x1.requires_grad)
print(x2.requires_grad)
# x1和x2张量的值一样,共用一份内存空间的数据
print(x1.data)
print(x2.data)
print(id(x1.data))
print(id(x2.data))

# 将x2张量转换成numpy数组
print(x2.numpy())

9.4 自动微分模块应用

import torch

# 输入张量 2*5
x = torch.ones(2, 5)
# 目标值是 2*3    
y = torch.zeros(2, 3)
# 设置要更新的权重和偏置的初始值
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
# 设置网络的输出值
z = torch.matmul(x, w) + b  # 矩阵乘法

# 设置损失函数,并进行损失的计算
loss = torch.nn.MSELoss()
loss = loss(z, y)
# 自动微分
loss.backward()
# 打印 w,b 变量的梯度
# backward 函数计算的梯度值会存储在张量的 grad 变量中
print("W的梯度:", w.grad)
print("b的梯度", b.grad)
"""
W的梯度: tensor([[-1.7421, -3.0443,  0.4371],
        [-1.7421, -3.0443,  0.4371],
        [-1.7421, -3.0443,  0.4371],
        [-1.7421, -3.0443,  0.4371],
        [-1.7421, -3.0443,  0.4371]])
b的梯度 tensor([-1.7421, -3.0443,  0.4371])
"""

PyTorch构建线性回归模型

我们使用 PyTorch 的各个组件来构建线性回归模型。在pytorch中进行模型构建的整个流程一般分为四个步骤:
  • 准备训练集数据
  • 构建要使用的模型
  • 设置损失函数和优化器
  • 模型训练
要使用的API:
  • 使用 PyTorch 的 nn.MSELoss() 代替平方损失函数
  • 使用 PyTorch 的 data.DataLoader 代替数据加载器
  • 使用 PyTorch 的 optim.SGD 代替优化器
  • 使用 PyTorch 的 nn.Linear 代替假设函数

例子

import torch
from torch.utils.data import TensorDataset  # 构造数据集对象
from torch.utils.data import DataLoader  # 数据加载器
from torch import nn  # nn模块中有平方损失函数和假设函数
from torch import optim  # optim模块中有优化器函数
from sklearn.datasets import make_regression  # 创建线性回归模型数据集
import matplotlib.pyplot as plt


plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号


# 构造数据集
def create_dataset():
    # 使用 make_regression 生成回归数据集
    x, y, coef = make_regression(n_samples=100,   # 样本数量
                                 n_features=1,    # 特征数量
                                 noise=10,        # 噪声水平
                                 coef=True,       # 返回系数
                                 bias=14.5,       # y轴截距
                                 random_state=0)  # 随机种子,确保结果可复现

    # 将构建数据转换为张量类型
    x = torch.tensor(x, dtype=torch.float32)  # 确保数据类型为 float32
    y = torch.tensor(y, dtype=torch.float32)  # 确保数据类型为 float32

    return x, y, coef
 
# 训练模型
def train():
    # 构造数据集
    x, y, coef = create_dataset()

    # 构造数据集对象
    dataset = TensorDataset(x, y)

    # 构造数据加载器
    # dataset=:数据集对象
    # batch_size=:批量训练样本数据
    # shuffle=:样本数据是否进行乱序
    dataloader = DataLoader(dataset=dataset, batch_size=16, shuffle=True)

    # 构造模型
    # in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size
    # out_features指的是输出的二维张量的大小,即输出的[batch_size,size]中的size
    model = nn.Linear(in_features=1, out_features=1)

    # 构造平方损失函数
    criterion = nn.MSELoss()

    # 构造优化函数
    # params=model.parameters():训练的参数,w和b
    # lr=1e-2:学习率, 1e-2为10的负二次方
    print("w和b-->", list(model.parameters()))
    print("w-->", model.weight)
    print("b-->", model.bias)
    optimizer = optim.SGD(params=model.parameters(), lr=1e-2)

    # 初始化训练次数
    epochs = 100
    # 损失的变化
    epoch_loss = [] 
    total_loss=0.0 
    train_sample=0.0

    for _ in range(epochs):
        for train_x, train_y in dataloader:
            # 将一个batch的训练数据送入模型
            y_pred = model(train_x.type(torch.float32))
            # 计算损失值
            loss = criterion(y_pred, train_y.reshape(-1, 1).type(torch.float32))
            total_loss += loss.item() 
            train_sample += len(train_y)
            # 梯度清零
            optimizer.zero_grad()
            # 自动微分(反向传播)
            loss.backward()
            # 更新参数
            optimizer.step()
        # 获取每个batch的损失 
        epoch_loss.append(total_loss/train_sample)
        
    # 打印回归模型的w
    print(model.weight)
    # 打印回归模型的b
    print(model.bias)
    
    # 绘制损失变化曲线 
    plt.plot(range(epochs), epoch_loss) 
    plt.title('损失变化曲线') 
    plt.grid() 
    plt.show()

    # 绘制拟合直线
    plt.scatter(x, y)
    x = torch.linspace(x.min(), x.max(), 1000)
    y1 = torch.tensor([v * model.weight + model.bias for v in x])
    y2 = torch.tensor([v * coef + 14.5 for v in x])
    plt.plot(x, y1, label='训练')
    plt.plot(x, y2, label='真实')
    plt.grid()
    plt.legend()
    plt.show()


if __name__ == '__main__':
    train()

TensorDataset

在 PyTorch 中,TensorDataset 是一个非常方便的类,用于将多个张量组合成一个数据集对象。这个数据集对象可以与 DataLoader 一起使用,以便在训练过程中批量加载数据并进行乱序等操作。

TensorDataset 的使用假设你有两个张量 x 和 y,分别表示特征和目标值,你可以使用 TensorDataset 将它们组合成一个数据集对象。

以下是一个完整的示例,展示了如何使用 TensorDataset 和 DataLoader:

import torch
from torch.utils.data import TensorDataset, DataLoader
from sklearn.datasets import make_regression

# 生成回归数据集
x, y, coef = make_regression(n_samples=100, n_features=1, noise=10, coef=True, bias=14.5, random_state=0)

# 将数据转换为 PyTorch 张量
x = torch.tensor(x, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)

# 创建 TensorDataset 对象
dataset = TensorDataset(x, y)

# 创建 DataLoader 对象
dataloader = DataLoader(dataset=dataset, batch_size=16, shuffle=True)

# 使用 DataLoader 加载数据
for batch_idx, (batch_x, batch_y) in enumerate(dataloader):
    print(f"Batch {batch_idx + 1}:")
    print(f"  x: {batch_x}")
    print(f"  y: {batch_y}")
    print()

代码解释

  1. 生成回归数据集:使用 make_regression 生成回归数据集,生成100个样本,每个样本有一个特征,噪声水平为10,y轴截距为14.5,随机种子为0。
  2. 将数据转换为 PyTorch 张量:x 和 y 被转换为浮点型张量。
  3. 创建 TensorDataset 对象:TensorDataset(x, y) 将特征 x 和目标值 y 组合成一个数据集对象 dataset。
  4. 创建 DataLoader 对象:DataLoader(dataset=dataset, batch_size=16, shuffle=True) 创建一个 DataLoader 对象,批量大小为16,数据在每个轮次开始时会进行乱序。
  5. 使用 DataLoader 加载数据:使用 for 循环遍历 DataLoader,每次迭代获取一个批次的数据 batch_x 和 batch_y,并打印出来。

相关文章:

  • SpringSecurity框架入门
  • (自用)WebSocket创建流程
  • oracle批量删除分区
  • 【船舷外机】行业研究
  • 数据结构学习-第一天
  • 【注解简化配置的原理是什么】
  • springboot-ai接入DeepSeek
  • NO.71十六届蓝桥杯备战|搜索算法-递归型枚举与回溯剪枝|枚举子集|组合型枚举|枚举排列|全排列问题(C++)
  • SpringBoot自动装配原理---Spring
  • import cv2 安装失败
  • 语法: value=label_address( label);
  • PyTorch池化层详解:原理、实现与示例
  • ctf-show-micsx
  • 【Kubernetes】StorageClass 的作用是什么?如何实现动态存储供应?
  • TLS 1.2 握手过程,每个阶段如何保证通信安全?​​
  • 古诗词数据集(74602条简体版、繁体版) | 智能体知识库 | AI大模型训练
  • iOS APP集成Python解释器
  • OpenCV 在树莓派上进行实时人脸检测
  • C++ 内存访问模式优化:从架构到实践
  • Redis之布隆过滤器
  • seo优化收费/seo网站诊断报告
  • 阳春网站开发/湖南seo网站多少钱
  • 怎么样查中企动力做的网站/专业seo站长工具全面查询网站
  • 网站建设合同范本/免费友情链接网站
  • 10大免费软件下载网站/合肥网络推广优化公司
  • 500网站建设/宁夏百度公司