《深度学习》——pytorch框架及项目

发布于:2025-02-12 ⋅ 阅读:(13) ⋅ 点赞:(0)

pytorch

PyTorch 是一个开源的深度学习框架,由 Facebook 的人工智能研究团队开发。它在学术界和工业界都得到了广泛的应用,下面从多个方面详细介绍:

特点

  • 动态计算图:与 TensorFlow 的静态计算图不同,PyTorch 使用动态计算图。这意味着在运行时可以动态地改变计算图的结构,使得代码的编写和调试更加直观和灵活。例如,在训练循环中可以根据不同的条件来改变计算流程。
  • Python 优先:PyTorch 深度集成 Python,代码风格简洁易懂,易于上手。开发者可以利用 Python 丰富的库和工具进行数据处理、可视化等操作。
  • 强大的 GPU 支持:PyTorch 能够充分利用 NVIDIA GPU 的并行计算能力,通过简单的代码就可以将张量和模型转移到 GPU 上进行加速计算,大大提高了训练和推理的速度。
  • 丰富的工具和库:提供了许多高级工具和库,如 Torchvision(用于计算机视觉任务)、Torchaudio(用于音频处理任务)等,方便开发者快速搭建和训练模型。

基本概念

  • 一、张量(Tensor):类似于 NumPy 的多维数组,但可以在 GPU 上运行以加速计算。例如,创建一个简单的张量

    import torch
    
    # 创建一个2x3的随机张量
    x = torch.rand(2, 3)
    print(x)
    

    在这里插入图片描述

  • 二、自动求导(Autograd):PyTorch 的自动求导机制可以自动计算张量的梯度,这对于训练神经网络非常重要。在定义张量时,只需要设置requires_grad=True,PyTorch 就会跟踪所有与之相关的操作,并在需要时计算梯度。

    import torch
    # 创建一个需要计算梯度的张量
    x = torch.tensor([2.0], requires_grad=True)
    y = x**2
    # 计算梯度
    y.backward()
    print(x.grad)  # 输出导数 2x,即 4
    
  • 三、模块(Module):torch.nn.Module是所有神经网络模块的基类。通过继承Module类,可以方便地定义自己的神经网络模型。

import torch
import torch.nn as nn

# 定义一个简单的全连接神经网络
class SimpleNet(nn.Module):
    def __init__(self):
        super(SimpleNet, self).__init__()
        self.fc = nn.Linear(10, 1)

    def forward(self, x):
        return self.fc(x)

# 创建模型实例
model = SimpleNet()
  • 四、优化器(Optimizer)
    PyTorch 提供了多种优化器,如 SGD、Adam、RMSprop 等,用于更新模型的参数。优化器根据计算得到的梯度来调整模型的参数,以最小化损失函数。

项目

下面我们使用BP神经网络来实现手写数字识别项目,此项目数据集来自MNIST 数据集由美国国家标准与技术研究所(NIST)整理而成,包含手写数字的图像,主要用于数字识别的训练和测试。该数据集被分为两部分:训练集和测试集。训练集包含 60,000 张图像,用于模型的学习和训练;测试集包含 10,000 张图像,用于评估训练好的模型在未见过的数据上的性能。

  • 图像格式:数据集中的图像是灰度图像,即每个像素只有一个值表示其亮度,取值范围通常为 0(黑色)到 255(白色)。
  • 图像尺寸:每张图像的尺寸为 28x28 像素,总共有 784 个像素点。
  • 标签信息:每个图像都有一个对应的标签,标签是 0 到 9 之间的整数,表示图像中手写数字的值

项目实现

导入所需库

import torch
from torch import nn #导入神经网络模块
from torch.utils.data import DataLoader # 数据包管理工具,打包数据
from torchvision import datasets # 封装了很对与图像相关的模型,数据集
from torchvision.transforms import ToTensor # 数据转换,张量,将其他类型的数据转换成tensor张量

下载训练数据和测试数据

'''下载训练数据集(包含训练集图片+标签)'''
training_data = datasets.MNIST( # 跳转到函数的内部源代码,pycharm 按下ctrl+鼠标点击
    root='data', # 表示下载的手写数字 到哪个路径。60000
    train=True, # 读取下载后的数据中的数据集
    download=True, # 如果你之前已经下载过了,就不用再下载了
    transform=ToTensor(), # 张量,图片是不能直接传入神经网络模型
    # 对于pytorch库能够识别的数据一般是tensor张量
)

'''下载测试数据集(包含训练图片+标签)'''
test_data = datasets.MNIST(
    root='data',
    train=False,
    download=True,
    transform=ToTensor(),# Tensor是在深度学习中提出并广泛应用的数据类型,它与深度学习框架(如pytorch,TensorFlow)
)# numpy数组只能在cpu上运行。Tensor可以在GPU上运行,这在深度学习应用中可以显著提高计算速度。
print(len(training_data))
print(len(test_data))

在这里插入图片描述

训练样本和测试样本的数量
在这里插入图片描述

对训练和测试样本进行分批次

# 创建训练数据的 DataLoader 对象
# DataLoader 是 PyTorch 中用于批量加载数据的实用工具类,它可以帮助我们更高效地处理大规模数据集。
train_dataloader = DataLoader(training_data, batch_size=64)  # 建议用2的指数当作一个包的数量
test_dataloader = DataLoader(test_data, batch_size=64)

展示手写图片

'''展示手写体图片,把训练数据集中的59000张图片展示一下'''

from matplotlib import pyplot as plt
figure = plt.figure()
for i in range(9):
    img,label = training_data[i+59000] # 提取第59000张图片

    figure.add_subplot(3,3,i+1) # 图像窗口中创建多个小窗口,小窗口用于显示图片
    plt.title(label)
    plt.axis('off') # plt.show(I) # 显示矢量
    plt.imshow(img.squeeze(),cmap='gray') # plt.imshow()将numpy数组data中的数据显示为图像,并在图形窗口显示
    a = img.squeeze() # img.squeeze()从张量img中去掉维度为1的。如果该维度的大小不为1则张量不会改变。
plt.show()

在这里插入图片描述

判断pytorch是否支持GPU

'''判断是否支持GPU'''
device = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'Using {device} device')

如果支持GPU输出cuda,map系统则输出map,使用CPU则输出CPU
在这里插入图片描述

定义神经网络模型

'''定义神经网络 类的继承这种方式'''
class NeuralNetwork(nn.Module): # 通过调用类的形式来使用神经网络,神经网络模型nn.module
    def __init__(self): # self类自己本身
        super().__init__() # 继承的父类初始化
        self.flatten = nn.Flatten() # 展开,创建一个展开对象flatten
        self.hidden1 = nn.Linear(28*28,128)
        self.hidden2 = nn.Linear(128,256)
        self.out = nn.Linear(256,10)
    def forward(self,x): # 向前传播,数据的流向
        x = self.flatten(x) # 图像展开
        x = self.hidden1(x)
        x = torch.sigmoid(x)
        x = self.hidden2(x)
        x = torch.sigmoid(x) # 激活函数
        x = self.out(x)
        return x
model = NeuralNetwork().to(device)
print(model)

定义训练函数

def train(dataloader,model,loss_fn,optimizer):
    model.train() # 告诉模型,要开始训练,模型中w进行随机化操作,已经更新w,在训练过程中w会被修改
    # pytorch提供两种方式来切换训练和测试的模式,分别是:model.train()和model.eval()
    # 一般用法:在训练之前写model.train(),在测试时写model.eval()
    batch_size_num = 1
    for x,y in dataloader: # 其中batch为每一个数据的编号
        x,y=x.to(device),y.to(device) # 将训练数据和标签传入gpu
        pred = model.forward(x) # .forward可以被省略,父类中已经对次功能进行了设置。自动初始化w权值
        loss = loss_fn(pred,y) # 通过交叉熵损失函数计算损失值loss
        # Backpropagation 进来个batch的数据,计算一次梯度,更新一次网络
        optimizer.zero_grad() #梯度值清零
        loss.backward() # 反向传播计算每一个参数的梯度值w
        optimizer.step() # 根据梯度更新网络w参数

        loss_value = loss.item() # 从tensor数据中提取数据出来,tensor获取损失值
        if batch_size_num % 100 == 0:
            print(f'loss:{loss_value:7f}  [number:{batch_size_num}]' )
        batch_size_num += 1

定义测试函数

def test(dataloader,model,loss_fn):
    size = len(dataloader.dataset) # 10000
    num_batches = len(dataloader) # 打包的数据
    model.eval() # 测试,w就不能再更新
    test_loss,correct = 0,0
    with torch.no_grad(): # 一个上下文管理器,关闭梯度计算。当你确定不会调用Tensor.backward()的时候。这可以减少计算内存
        for x,y in dataloader:
            x,y = x.to(device),y.to(device)
            pred = model.forward(x)
            test_loss += loss_fn(pred,y).item()#test_loss是会自动累加每一个批次的损失值
            correct  +=(pred.argmax(1) == y).type(torch.float).sum().item()
            a = (pred.argmax(1) == y)#dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号
            b = (pred.argmax(1) == y).type(torch.float)
    test_loss /=num_batches#能来衡量模型测试的好坏。
    correct /= size#平均的正确率

    print(f'Test result: \n Accuracy:{(100*correct)}%,Avg loss:{test_loss}')

创建交叉熵损失函数和优化器

loss_fn = nn.CrossEntropyLoss()#创建交叉熵损失函数对象,因为手写字识别中一共有10个数字,输出会有10个结果
#一会改成adam优化器
optimizer = torch.optim.Adam(model.parameters(),lr=0.001)#创建一个优化器,SGD为随机梯度下降算法
#params:要训练的参数,一般我们传入的都是model.parameters()
# #lr:learning_rate学习率,也就是步长。

通过多轮训练降低损失值得到最终结果

epochs = 20
for t in range(epochs):
    print(f'epoch{t+1}\n--------------------')
    train(train_dataloader,model, loss_fn, optimizer)
print('Done!')
test(test_dataloader,model, loss_fn)

在这里插入图片描述

可通过结果看出在使用ADam优化器步长为0.001时,训练20轮得到的正确率为97.5%,损失值为0.119。

注意

  • 可以通过修改优化器来提高准确率
  • 对于不同的神经网络要使用不同的激活函数,对于sigmoid函数来说隐藏层过多时会产生梯度消失,因为sigmoid函数的偏导在0~0.25之间随着反向传播的进行,梯度会不断累乘。即使初始梯度较大,但经过多层的累乘后,梯度值会迅速变小,趋近于 0,从而导致梯度消失。因此可用ReLU、tanh、P-ReLU、R-ReLU、maxout等来代替sigmoid函数。ReLU函数偏导为1,不会产生梯度消失问题。
  • 当梯度在传递过程中不断增大,变得非常大时,就会导致梯度爆炸现象。此时,模型参数会因为梯度值过大而发生大幅度的更新,使得模型无法收敛,甚至可能导致数值溢出,使得训练过程崩溃。