美文网首页
PyTorch 简介和示例

PyTorch 简介和示例

作者: 河码匠 | 来源:发表于2025-02-17 09:15 被阅读0次

一、基本概念

1. 张量(Tensor)

张量 是 PyTorch 中用来存储数据的基本单位,它可以看作是一个多维的列表
你可以把它看成是一个容器,用来存放数字,并且可以进行很多数学计算。

  • 标量(Scalar):一个单独的数字,又称为零维张量
    例如:x = torch.tensor(5),这里 5 是一个数字。

  • 向量(Vector):一组数字,可以理解为一个列表。
    例如:x = torch.tensor([1, 2, 3]),这就表示了包含 1、2 和 3 三个数字的列表。

  • 矩阵(Matrix):一个由多个数字组成的二维表格,像一个表格或一张纸上的数字排列。
    例如:x = torch.tensor([[1, 2], [3, 4]]),这表示一个 2 行 2 列的表格。

  • 高维张量:可以理解为包含多个矩阵的数据结构。
    例如:x = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]),这是一个包含 2 个矩阵的数据结构。

这些张量可以在计算时进行加、减、乘、除等操作。它们的大小(例如行数、列数)和形状也是可以调整的。

2. 计算图(Computation Graph)

计算图 是指在进行计算时,PyTorch 会把你做的每一步操作记录下来。

  • 例子:可以想象成你在看着菜谱做饭,切菜、热锅、放油、放入蔬菜、翻炒、加盐、调料、出锅。把这个做饭过程比作一个计算过程,每一步都是一个操作。而计算图就是记录这个做饭过程的一个步骤图。每一步操作(如“切菜”)就是计算图中的一个节点。每个节点都有它的输入(如“切好的菜”),和它产生的输出(如“炒熟的菜”)。

  • 动态图:计算图在你运行时动态生成。这意味着每次你执行计算时,PyTorch 会根据你给出的步骤自动创建新的计算图。

3. 自动微分(Autograd)

自动微分 自动地帮助你计算每个操作的变化。这对于训练神经网络非常重要,因为它可以自动计算出每个操作如何影响最终结果,从而更新模型的参数。

  • 例子:假设你有一个目标(比如想让机器预测某个数字),然后你使用机器进行计算得到一个结果,最后会得到一个“误差”值。通过自动微分,PyTorch 会计算出这个误差如何影响机器的每个部分,然后根据这些计算来调整机器的参数,直到它做得更好。

4. 优化器(Optimizer)

优化器 是用来调整机器模型中参数的工具。在训练模型时,我们会不断调整这些参数,使得模型的预测越来越准确。优化器根据每个参数的“误差”来决定如何调整这些参数,类似于每次练习时改进动作。

  • 例子:假设你在学习骑自行车,开始时可能会不太稳定,但是你根据每次骑行后的“反馈”调整自己的动作,直到可以稳定骑行。优化器就是这个过程的“反馈机制”。

5. 神经网络(Neural Networks)

神经网络 是一个模拟人脑神经元的计算模型,它通过层层处理输入数据来生成输出。在 PyTorch 中,你可以使用 torch.nn 模块来构建神经网络。

  • 例子:想象你正在玩一个“是/否”问题游戏,每一次回答都会根据你的输入(例如:“它是红色的吗?”)来改变下一步的决策过程。神经网络就像这样的决策过程,每一层的处理都会根据前面的输入进行调整。

6. 数据加载(Data Loading)

当你进行深度学习训练时,往往需要处理大量的数据。为了高效地使用这些数据,PyTorch 提供了数据加载器(DataLoader),它可以帮助你从文件中读取数据,并将数据分成小批次送入模型进行训练。

  • 例子:如果你有 1000 本书,你并不会一次性读完所有的书,而是会分成每次读 10 本。这种分批处理方式就像数据加载器,它将所有数据分成小块,以便训练时逐步输入。

二、示例

1. 下载训练数据,测试数据的的信息

import torch
# nn 神经网络模块
from torch import nn
# 批量加载数据的工具。帮助你将数据集按批次(batch)分成小块,并且可以自动化地进行数据的随机打乱、批次分配以及并行加载等操作
from torch.utils.data import DataLoader
# datasets 提供了常见的图像数据集接口,方便我们直接下载和使用数据集
from torchvision import datasets
# 转换器,它用于将图像转换为 PyTorch 张量
from torchvision.transforms import ToTensor

# FashionMNIST: 图像数据集
# train:
#       True  下载训练数据
#       False 下载测试数据
# ToTensor():  将数据转化为 PyTorch 张量格式
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
)

test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor(),
)


batch_size = 64

# 将数据加载成小批次(每批次大小为 batch_size,即 64)
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)


#  X 是每个批次的数据输入,通常是特征数据(features),在图像分类任务中,它通常是图像数据。
#  y 是每个批次的数据标签,通常是对应于输入数据的目标值(labels)。例如,在图像分类任务中,y 可能是每张图像对应的类别标签。
for X, y in test_dataloader:

    #  N 是批次大小(batch size),也就是在每次循环中,X 包含了 64 个样本(因为 batch_size=64)。
    #  C 是通道数(channels)。如果是彩色图像,C 可能是 3(表示 RGB 三个颜色通道);如果是灰度图像,C 可能是 1。
    #  H 是图像的高度(height),即图像的垂直尺寸。
    #  W 是图像的宽度(width),即图像的水平尺寸。
    print(f"Shape of X [N, C, H, W]: {X.shape}")

    # y 的形状会依据任务的不同有所不同。
    #     如果是分类任务,y 可能是每张图像的类别索引(例如,一个数字表示图像的类别)。
    #     对于回归任务,y 可能是连续值。
    # y.shape 会显示 y 的形状,y.dtype 会显示标签的类型。
    print(f"Shape of y: {y.shape} {y.dtype}")
    break

2. 创建模型

# 检查是否有加速器(GPU 或其他硬件加速设备)
if torch.accelerator.is_available():
    # 当前加速器的类型(如 cuda 表示 GPU)
    device = torch.accelerator.current_accelerator().type
else:
    device = "cpu"
print(f"Using {device} device")


# 定义神经网络
class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        # 用 nn.Flatten() 将输入的二维图像(28x28)展平成一维数组。每张图像的像素数是 28 * 28 = 784
        self.flatten = nn.Flatten()

        #  一个 nn.Sequential 容器,顺序地包含了多个层:
        self.linear_relu_stack = nn.Sequential(

            #  nn.Linear(28*28, 512):一个全连接层,将 784 个输入映射到 512 个神经元。
            nn.Linear(28*28, 512),

            #  nn.ReLU():激活函数,增加非线性性。
            nn.ReLU(),

            #  nn.Linear(512, 512):另一个全连接层,输入和输出都是 512。
            nn.Linear(512, 512),

            #  nn.ReLU():激活函数。
            nn.ReLU(),

            #  nn.Linear(512, 10):最终输出层,映射到 10 个神经元,代表 10 个分类(例如,MNIST 数据集中的 10 个数字类别)。
            nn.Linear(512, 10)
        )
    # 定义了数据如何通过网络流动
    # 也就是输入数据从输入层到输出层的传递过程,也就是数据如何在网络中的各个层之间进行运算和变换
    def forward(self, x):
        # 输入数据展平
        x = self.flatten(x)
        # 数据通过定义的层堆进行前向传播,输出 logits(原始的网络输出)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)

返回内容

NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
  )
)

说明:

1. (flatten): Flatten(start_dim=1, end_dim=-1)
  • flatten 层是将输入的数据从多维数组展平成一维数组。
    start_dim=1 表示从第一个维度(即图像的高度或宽度)开始展平
    end_dim=-1 表示直到最后一个维度
  • 对于 MNIST 图像数据集(28x28 像素的图像),每个图像是一个二维数组(28, 28)。flatten 会将其展平为一个一维的数组,形状变为 (784,)(28*28=784)。
  • 作用:将输入的图像展平为一个一维向量,准备输入到全连接层。
2. (linear_relu_stack): Sequential(...)

linear_relu_stack 是一个包含多个层的顺序容器(Sequential)。

每一层按照顺序排列,依次执行计算:

  • 第一个层:(0): Linear(in_features=784, out_features=512, bias=True)

    • Linear 表示一个全连接层,它执行线性变换 y = Wx + b
    • in_features=784:输入特征数为 784(展平后的图像像素数量)。
    • out_features=512:输出特征数为 512,即输出的向量长度是 512。
    • bias=True:表示在计算中会加上偏置项。
  • 第二个层:(1): ReLU()

    • ReLU 是一个激活函数(Rectified Linear Unit),用于引入非线性,帮助神经网络捕捉复杂的模式。
    • ReLU 的作用是对输入值进行非线性变换,使得所有负数变为 0,正数保持不变。
  • 第三个层:(2): Linear(in_features=512, out_features=512, bias=True)

    • 这是另一个全连接层,输入特征数为 512,输出特征数为 512。
    • 同样使用偏置项。
  • 第四个层:(3): ReLU()

    • 这是另一个 ReLU 激活函数层,对输出进行非线性变换。
  • 第五个层:(4): Linear(in_features=512, out_features=10, bias=True)

    • 最后一个全连接层,输入特征数为 512,输出特征数为 10。
    • 输出的 10 个值对应分类任务中的 10 个类别(例如,MNIST 数据集中 10 个数字)。

3. 模型优化和训练


# 损失函数(loss function)
# CrossEntropyLoss。是一个常用于分类任务的损失函数
# 交叉熵损失函数(Cross-Entropy Loss):
#           它衡量的是模型预测的概率分布与真实标签之间的差异。
#           在分类任务中,模型的输出通常是一个概率分布(例如,模型输出每个类别的得分),而标签是实际的类别。
#           接收模型的输出(通常是 logits,即未经激活的原始输出)和实际的标签,并计算损失值。通常用作多类别分类任务中的损失函数
loss_fn = nn.CrossEntropyLoss()

# 优化器(optimizer): 是训练神经网络时用于调整参数(权重和偏置)的算法。目标是通过最小化损失函数,逐步提高模型的预测准确性。
# SGD(Stochastic Gradient Descent,随机梯度下降):
#     它通过计算损失函数相对于模型参数的梯度,并沿着梯度的反方向更新参数,从而使损失最小化。
# model.parameters():
#     这部分返回模型中所有需要训练的参数(即权重和偏置)。
# lr=1e-3:
#    学习率(learning rate),控制每次参数更新的步长。
#    1e-3 表示学习率是 0.001。较小的学习率通常意味着训练过程较慢,但可能更稳定;
#    较大的学习率可能使训练过程更快,但可能导致不稳定
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)


# 训练函数 train,用于训练神经网络模型。
# 作用是遍历数据集,计算损失,进行反向传播并优化模型参数
#
# 参数说明:
#     dataloader:数据加载器,用于按批次加载训练数据。
#     model:神经网络模型。
#     loss_fn:损失函数,用于计算预测结果与真实标签之间的差异。
#     optimizer:优化器,用于更新模型的参数。
def train(dataloader, model, loss_fn, optimizer):
    # 获取数据集的大小
    size = len(dataloader.dataset)
    # 将模型设置为训练模式
    model.train()
    # 遍历数据加载器中的每个批次(batch 批次号),X 是输入数据(通常是图像),y 是对应的标签(通常是图像类别)
    for batch, (X, y) in enumerate(dataloader):
        # 将数据移动到设备(如GPU)
        X, y = X.to(device), y.to(device)

        # 通过将输入 X 传入模型来获取模型的预测结果
        pred = model(X)
        # 计算预测值与真实标签之间的损失
        loss = loss_fn(pred, y)

        # 计算损失函数相对于模型参数的梯度
        loss.backward()
        # 更新模型的参数
        # 根据计算出的梯度和优化算法(如 SGD、Adam 等),优化器会调整模型的参数以减少损失
        optimizer.step()
        # 清除模型的梯度,以便下次计算时不会叠加上次计算的梯度
        # 每次反向传播后必须清零梯度,否则会导致梯度累积
        optimizer.zero_grad()

        #  打印训练进度
        if batch % 100 == 0:
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")


# test,用于评估训练后的模型在测试数据集上的表现
#
# 参数说明:
#        dataloader:数据加载器,用于按批次加载测试数据。
#        model:训练好的神经网络模型。
#        loss_fn:损失函数,用于计算模型在测试集上的误差。
def test(dataloader, model, loss_fn):
    # 测试数据集的总样本数
    size = len(dataloader.dataset)

    # 数据加载器中批次的数量,用来计算平均损失
    num_batches = len(dataloader)

    # 设置模型为评估模式
    model.eval()

    # 初始化测试损失和正确预测数
    # test_loss 用于累积每个批次的损失值,最终用于计算平均损失。
    # correct 用于累积模型在测试集上正确预测的数量,最终计算准确率。
    test_loss, correct = 0, 0

    # 上下文管理器,告诉 PyTorch 在这个代码块中不计算梯度
    with torch.no_grad():
        # 遍历测试数据集的批次
        # X 是输入数据,y 是对应的标签
        for X, y in dataloader:

            # 将数据移动到设备(如GPU)
            X, y = X.to(device), y.to(device)

            # 模型预测。通过将输入数据 X 传入模型,得到预测值 pred
            pred = model(X)

            # 累积损失。
            # loss_fn(pred, y) 计算当前批次的损失值(即预测与真实标签之间的差异)。
            # .item() 会将损失的张量转换为数值,并将其加到 test_loss 中,累计每个批次的损失
            test_loss += loss_fn(pred, y).item()

            # 计算正确预测的数量
            #  pred.argmax(1) 获取每个样本的最大得分对应的类别索引(即预测的类别)。
            #  (pred.argmax(1) == y) 是一个布尔张量,表示预测类别是否与实际类别相同。
            #  .type(torch.float) 将布尔值转换为浮点数(True 为 1,False 为 0)。
            #  .sum() 计算该批次中所有正确预测的样本数量。
            #  .item() 将结果转换为数值,并将其加到 correct 中。
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    # 计算平均损失值
    test_loss /= num_batches

    # 计算模型在测试集上的准确率,size 是测试集的样本数量
    correct /= size
    print(f"测试误差: \n 准确率: {(100*correct):>0.1f}%, 平均损失: {test_loss:>8f} \n")


# 训练的轮数
epochs = 5
for t in range(epochs):
    print(f"第 {t+1} 次\n-------------------------------")
    # 每一轮循环中,调用 train() 函数对模型进行训练
    #
    # 参数说明:
    #     train_dataloader 训练数据加载器
    #     model 模型
    #     loss_fn 损失函数
    #     optimizer 优化器
    train(train_dataloader, model, loss_fn, optimizer)
    # 每轮训练后,使用 test() 函数评估模型的性能,
    #
    # 参数说明:
    #     test_dataloader 测试数据加载器
    #     model 模型
    #     loss_fn 损失函数
    test(test_dataloader, model, loss_fn)

print("结束!")

4. 保存模型

torch.save(model.state_dict(), "model.pth")
print("保存路径:model.pth")

5. 加载模型

# 初始化模型并将其移至指定设备
# device:cpu cuda之类
model = NeuralNetwork().to(device)

# 加载保存的模型参数
model.load_state_dict(torch.load("model.pth", weights_only=True))

相关文章

网友评论

      本文标题:PyTorch 简介和示例

      本文链接:https://www.haomeiwen.com/subject/ecrdpjtx.html