PyTorch核心模块介绍

125 阅读10分钟

★ 完成深度学习的必要部分

mindmap
      深度学习
          数据预处理
              统一格式、去除异常值、变换数据
              数据集划分(训练集、验证集、测试集)
                  划分方法:随机分割和 KFold 方法
              数据加载机制(batch)
          模型选择和实现
              “定制化”方式构建
                  逐层组装
                  预定义模块组合
          设定损失函数和优化方法以及超参数
          模型训练和评估
              训练前,模型和数据转移到 GPU
              多 GPU 训练时,模型与数据的分配与整合
              训练和评估完成后,需将数据转回 CPU 以计算性能指标   
  1. 数据预处理:数据分割可使用 sklearn 的的test_train_splitkfold实现。
  2. 模型选择和实现:深度学习需要构建多层神经网络,可能包括卷积层、池化层、批正则化层、LSTM 层等特定功能层。
  3. 设定损失函数:设定损失函数与优化器时,需确保它们能够在自定义模型结构中实现反向传播。
  4. 模型训练
    • 训练前:将模型和数据转移到 GPU 上进行计算,并确保损失函数与优化器在 GPU 环境下工作。
    • 多 GPU 训练时:考虑模型与数据的分配与整合问题。
      • 按批处理数据:每次训练一个批次数据,并通过损失函数的反向传播调整网络参数。整个过程需要各模块协同工作。
    • 训练和评估完成后:将数据转回 CPU 以计算性能指标。

〇 使用 PyTorch 前的基本配置

image.png

image.png

★ 数据导入

核心概念

  • Dataset + DataLoader
  • 数据格式与变换定义
  • 批次数据迭代读入

PyTorch 自身的 Dataset 类

  • __init__: 传入参数与样本集定义
  • __getitem__: 读取样本,支持变换
  • __len__: 返回样本数量

数据读取示例

  • 使用ImageFolder读取图片
  • 数据变换(翻转、裁剪等)
import torch 
from torchvision import datasets

#加载训练集和验证集数据。`ImageFolder` 类可以读取指定文件夹中的图像数据,并将文件夹名作为图像的标签
train_data = datasets.ImageFolder(train_path, transform=data_transform)

#`transform=data_transform` 是一个数据预处理变换,通常包括图像的归一化、数据增强等操作,确保输入模型的图像数据是标准化的
val_data = datasets.ImageFolder(val_path, transform=data_transform)

自定义 Dataset 类

  • 继承PyTorch的Dataset类
  • 使用pandas读取标签文件
  • 实现__getitem____len__方法
import os
import pandas as pd
from torchvision.io import read_image

# 定义了一个继承自 `Dataset` 的类 `MyDataset`
class MyDataset(Dataset): 
    def __init__(self, annotations_file, img_dir, transform=None, target_transform=None):
        """
        Args:
            annotations_file (string): 包含图片标签的 CSV 文件路径
            img_dir (string): 图片所在的目录
            transform (callable, optional): 可选参数,对图片进行预处理的函数。
            target_transform (callable, optional): 可选参数,对标签进行预处理的函数。
        """
        self.img_labels = pd.read_csv(annotations_file) #读取 CSV 文件,存储图片标签信息。
        self.img_dir = img_dir #存储图片目录路径
        self.transform = transform #存储图片预处理函数
        self.target_transform = target_transform #存储标签预处理函数

    def __len__(self):
        return len(self.img_labels) #返回数据集中图片的数量

    def __getitem__(self, idx):
        """
        根据索引返回图片和标签
        Args:
            idx (int): Index
        """
        img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0]) #构建图片路径
        image = read_image(img_path) #读取图片
        label = self.img_labels.iloc[idx, 1] # 获取图片对应的标签
        #如果有预处理函数,分别对图片和标签进行预处理
        if self.transform:
            image = self.transform(image)
        if self.target_transform:
            label = self.target_transform(label)
        return image, label

构建完 Dataset 后,可以使用 DataLoader 按批次读入数据。

#创建了两个 DataLoader 对象,一个用于训练数据 (`train_loader`),另一个用于验证数据 (`val_loader`)

from torch.utils.data import DataLoader

#设置了批量大小 (`batch_size`),4 个子进程 (`num_workers=4`) 来加载数据,数据被打乱 (`shuffle=True`),并且如果数据集大小不能被批量大小整除,最后不足一个批量的数据将被丢弃 (`drop_last=True`)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)

#设置了批量大小 (`batch_size`) 和 4 个子进程 (`num_workers=4`),但数据不被打乱 (`shuffle=False`),这意味着验证数据将以固定的顺序被加载
val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, num_workers=4, shuffle=False)

DataLoader 对象参数设置

  • batch_size: 每批样本数
  • num_workers: 读取数据的进程数
  • shuffle: 是否打乱数据
  • drop_last: 是否丢弃最后不完整批次

使用 nextiter 读取 PyTorch 中的 DataLoader

import matplotlib.pyplot as plt

#这里使用`iter()`函数将`val_loader`转换为一个迭代器,然后使用`next()`函数获取下一批数据。这批数据包含图像和对应的标签。
images, labels = next(iter(val_loader))

#形状是一个四维张量,例如`(batch_size, channels, height, width)`
print(images.shape)

#由于PyTorch中的图像数据通常是以`(channels, height, width)`的格式存储的,而Matplotlib需要的格式是`(height, width, channels)`,所以使用`transpose(1,2,0)`进行维度转换。
plt.imshow(images[0].transpose(1,2,0))
plt.show()

★ 模型构建

本章主要介绍了如何在 PyTorch 中构建神经网络模型,包括模型的基本构造、特殊层的定义、以及 LeNet 模型的具体实现。

image.png

★ 模型初始化

初始化重要性

  • 权重初始值对训练至关重要
  • 好的初始值可提高收敛速度和准确率
  • 避免使用全 0 初始值

PyTorch 提供的初始化方法

image.png

初始化函数的使用

  1. 根据模型类型选择初始化方法
  2. 使用 isinstance()判断模型层类型
  3. 对不同层设置特定初始化方法

初始化函数的封装

  1. 定义 initializeweights()函数
  2. 遍历模型层,根据类型初始化
  3. 应避免全 0 初始化,以免梯度消失

示例

定义多层感知机(MLP)模型

#定义一个名为 MLP 的类,它继承自 nn.Module,这是 PyTorch 中所有神经网络模块的基类
class MLP(nn.Module):
  # _init__ 方法是类的构造函数,用于初始化模型层
  def __init__(self, **kwargs):
    # 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
    super(MLP, self).__init__(**kwargs)
    self.hidden = nn.Conv2d(1,1,3) #一个二维卷积层,输入通道和输出通道都是1,卷积核大小为3
    self.act = nn.ReLU()
    self.output = nn.Linear(10,1) #一个全连接层,输入特征维度为10,输出特征维度为1
    
   # 定义了模型的前向传播过程,即如何从输入 `x` 计算得到输出
  def forward(self, x):
    o = self.act(self.hidden(x)) #将输入 `x` 通过卷积层和ReLU激活函数,得到中间结果 `o`
    return self.output(o) #将中间结果 `o` 通过全连接层,得到最终的输出

mlp = MLP()
print(mlp.hidden.weight.data) #打印卷积层 `hidden` 的权重
print("-------初始化-------")

mlp.apply(initialize_weights) #使用 `initialize_weights` 函数对模型的所有参数进行初始化。`apply` 方法会递归地将函数应用到模型的所有子模块上
# 或者initialize_weights(mlp)
print(mlp.hidden.weight.data) #再次打印卷积层 `hidden` 的权重,以查看初始化后的权重值

★ PyTorch 中常见的损失函数

损失函数是模型训练的核心,它为模型的训练提供了负反馈。 PyTorch 提供了多种损失函数,如下图所示。

image.png

★ 模型训练和评估

在PyTorch中,模型的训练和评估是一个统一的流程,但在训练时需要更新模型参数,而在评估时则不需要。 首先,需要设置模型的状态,使用model.train()进入训练模式,使用model.eval()进入验证/测试模式。在训练过程中,通过DataLoader加载数据,使用for循环遍历所有数据,并将数据转移到GPU上进行处算。在每次迭代中,应该将优化器的梯度清零。 然后将数据输入模型进行前向传播,计算损失,执行反向传播,并用优化器更新模型参数。评估过程与训练类似,但不需要更新梯度,也不需要反向传播损失,且应该使用torch.no_grad来避免计算图的构建。

核心知识点

  1. 在PyTorch中,训练模型时需要进行参数更新,而评估模型时不更新参数。
  2. 模型的状态设置对于训练和评估至关重要,可以通过model.train()model.eval()来切换。
  3. 在训练过程中,应该在每次迭代前清零优化器的梯度,以避免梯度累积。
  4. 训练过程中,数据应该被放置在GPU上以加速计算,这可以通过.cuda()实现。
  5. 在执行反向传播之前,需要先计算损失函数。
  6. 评估模型时,应该关闭梯度计算,可以通过torch.no_grad()实现。
  7. 在验证/测试阶段,不需要执行反向传播和参数更新。
  8. 可以通过计算模型在验证集上的损失和准确率来评估模型的性能。
  9. 使用sklearn.metrics中的classification_report可以获得模型的详细分类性能指标。
  10. torchevaltorchmetric是进行模型评估的有用工具。

示例

模型训练

def train(epoch): #`epoch`,表示当前的训练轮次
    model.train() #将模型设置为训练模式
    train_loss = 0
    for data, label in train_loader:
        # 将数据和标签移动到GPU上
        data, label = data.cuda(), label.cuda()
        optimizer.zero_grad() #清空优化器的梯度
        output = model(data) #通过模型前向传播计算输出
        loss = criterion(output, label) #计算损失
        loss.backward() # 反向传播计算梯度
        optimizer.step() #更新模型参数
        train_loss += loss.item()*data.size(0) #累加当前批次的损失到总损失中
        train_loss = train_loss/len(train_loader.dataset) #计算平均训练损失
        print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss)) #打印当前轮次的训练损失

模型验证

def val(epoch):       
    model.eval() #将模型设置为评估模式
    val_loss = 0 #初始化验证损失为0
    
    #使用`torch.no_grad()`上下文管理器,在该块内进行的所有计算都不会计算梯度,减少内存使用并加速计算
    with torch.no_grad():
        #遍历验证数据加载器(`val_loader`),获取数据和标签,并将它们移动到GPU
        for data, label in val_loader:
            data, label = data.cuda(), label.cuda()
            output = model(data) #使用模型生成预测
            preds = torch.argmax(output, 1)
            loss = criterion(output, label)
            val_loss += loss.item()*data.size(0)
            running_accu += torch.sum(preds == label.data)
    val_loss = val_loss/len(val_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, val_loss))

计算平均指标,

from sklearn.metrics import classification_report
"""
将下方代码的labels和preds替换为模型预测出来的所有label和preds,
target_names替换为类别名称,
既可得到模型的分类报告
"""
#`labels` 和 `preds` 需要先通过 `cpu()` 方法移动到 CPU 上,以便 `classification_report` 函数能够处理它们。这是因为 `classification_report` 函数只接受在 CPU 上的数据。
print(classification_report(labels.cpu(), preds.cpu(), target_names=class_names))

★ PyTorch 优化器

深度学习的目标是通过调整网络参数,使得模型能够对输入进行非线性变换。

核心知识点

  1. 优化器是深度学习中用于更新网络参数(根据梯度)的关键组件,它根据反向传播的梯度信息来降低loss函数的值,使得模型输出更加接近真实标签。
  2. PyTorch提供了多种优化器,每些优化器都继承自Optimizer基类,拥有统一的接口和属性,方便用户进行参数优化。
  3. Optimizer基类中的defaults属性存储了优化器的超参数,如学习率(lr)、动量(momentum)等;state属性用于缓存参数的状态信息;param_groups属性管理着参数组。
  4. Optimizer基类提供了一系列方法,如zero_grad()用于清空参数的梯度,step()用于执行梯度更新,add_param_group()用于添加新的参数组,load_state_dict()state_dict()用于保存和加载优化器的状态。
  5. 在实际操作中,优化器需要与神经网络的参数绑定,并在每个epoch中进行梯度的置零和更新操作。
  6. 可以为网络的不同层设置不同的优化器参数,以适应不同层的优化需求。
  7. 优化器的选择应该根据具体的模型和任务进行选择和调整,通过实验比较不同优化器的性能,找到最适合当前任务的优化器。

PyTorch 优化器库

  • torch.optim提供多种优化器

    • SGD
    • ASGD
    • Adadelta
    • Adagrad
    • Adam
    • AdamW
    • Adamax
    • RAdam
    • NAdam
    • SparseAdam
    • LBFGS
    • RMSprop
    • Rprop 而以上这些优化算法均继承于Optimizer

Optimizer

属性

  • defaults:优化器超参数
  • state:参数缓存
  • param_groups:管理的参数组

方法

  • zero_grad():清空参数梯度。PyTorch的特性是张量的梯度不自动清零,因此每次反向传播后都需要清空梯度。
  • step():执行梯度更新
  • add_param_group():添加参数组
  • load_state_dict():加载状态参数字典
  • state_dict():获取当前状态信息字典