★ 完成深度学习的必要部分
mindmap
深度学习
数据预处理
统一格式、去除异常值、变换数据
数据集划分(训练集、验证集、测试集)
划分方法:随机分割和 KFold 方法
数据加载机制(batch)
模型选择和实现
“定制化”方式构建
逐层组装
预定义模块组合
设定损失函数和优化方法以及超参数
模型训练和评估
训练前,模型和数据转移到 GPU
多 GPU 训练时,模型与数据的分配与整合
训练和评估完成后,需将数据转回 CPU 以计算性能指标
- 数据预处理:数据分割可使用 sklearn 的的
test_train_split和kfold实现。 - 模型选择和实现:深度学习需要构建多层神经网络,可能包括卷积层、池化层、批正则化层、LSTM 层等特定功能层。
- 设定损失函数:设定损失函数与优化器时,需确保它们能够在自定义模型结构中实现反向传播。
- 模型训练
- 训练前:将模型和数据转移到 GPU 上进行计算,并确保损失函数与优化器在 GPU 环境下工作。
- 多 GPU 训练时:考虑模型与数据的分配与整合问题。
- 按批处理数据:每次训练一个批次数据,并通过损失函数的反向传播调整网络参数。整个过程需要各模块协同工作。
- 训练和评估完成后:将数据转回 CPU 以计算性能指标。
〇 使用 PyTorch 前的基本配置
★ 数据导入
核心概念
- 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: 是否丢弃最后不完整批次
使用 next 和 iter 读取 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 模型的具体实现。
★ 模型初始化
初始化重要性
- 权重初始值对训练至关重要
- 好的初始值可提高收敛速度和准确率
- 避免使用全 0 初始值
PyTorch 提供的初始化方法
初始化函数的使用
- 根据模型类型选择初始化方法
- 使用 isinstance()判断模型层类型
- 对不同层设置特定初始化方法
初始化函数的封装
- 定义
initializeweights()函数 - 遍历模型层,根据类型初始化
- 应避免全 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 提供了多种损失函数,如下图所示。
★ 模型训练和评估
在PyTorch中,模型的训练和评估是一个统一的流程,但在训练时需要更新模型参数,而在评估时则不需要。
首先,需要设置模型的状态,使用model.train()进入训练模式,使用model.eval()进入验证/测试模式。在训练过程中,通过DataLoader加载数据,使用for循环遍历所有数据,并将数据转移到GPU上进行处算。在每次迭代中,应该将优化器的梯度清零。
然后将数据输入模型进行前向传播,计算损失,执行反向传播,并用优化器更新模型参数。评估过程与训练类似,但不需要更新梯度,也不需要反向传播损失,且应该使用torch.no_grad来避免计算图的构建。
核心知识点
- 在PyTorch中,训练模型时需要进行参数更新,而评估模型时不更新参数。
- 模型的状态设置对于训练和评估至关重要,可以通过
model.train()和model.eval()来切换。 - 在训练过程中,应该在每次迭代前清零优化器的梯度,以避免梯度累积。
- 训练过程中,数据应该被放置在GPU上以加速计算,这可以通过
.cuda()实现。 - 在执行反向传播之前,需要先计算损失函数。
- 评估模型时,应该关闭梯度计算,可以通过
torch.no_grad()实现。 - 在验证/测试阶段,不需要执行反向传播和参数更新。
- 可以通过计算模型在验证集上的损失和准确率来评估模型的性能。
- 使用
sklearn.metrics中的classification_report可以获得模型的详细分类性能指标。 torcheval和torchmetric是进行模型评估的有用工具。
示例
模型训练
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 优化器
深度学习的目标是通过调整网络参数,使得模型能够对输入进行非线性变换。
核心知识点
- 优化器是深度学习中用于更新网络参数(根据梯度)的关键组件,它根据反向传播的梯度信息来降低loss函数的值,使得模型输出更加接近真实标签。
- PyTorch提供了多种优化器,每些优化器都继承自
Optimizer基类,拥有统一的接口和属性,方便用户进行参数优化。 Optimizer基类中的defaults属性存储了优化器的超参数,如学习率(lr)、动量(momentum)等;state属性用于缓存参数的状态信息;param_groups属性管理着参数组。Optimizer基类提供了一系列方法,如zero_grad()用于清空参数的梯度,step()用于执行梯度更新,add_param_group()用于添加新的参数组,load_state_dict()和state_dict()用于保存和加载优化器的状态。- 在实际操作中,优化器需要与神经网络的参数绑定,并在每个epoch中进行梯度的置零和更新操作。
- 可以为网络的不同层设置不同的优化器参数,以适应不同层的优化需求。
- 优化器的选择应该根据具体的模型和任务进行选择和调整,通过实验比较不同优化器的性能,找到最适合当前任务的优化器。
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():获取当前状态信息字典