本文为pytorch学习而作(2024.1.28),前期跟随B站土堆老师和吴恩达老师的课程学习,以官方文档作为参考
一、环境配置
已完成,见配置文章(所用IDE:pycharm)
二、基础工具函数与学习提要
help()
函数 获取对象的帮助信息。
dir()
函数用于列出对象的属性和方法。
启动IPython:pycharm终端内输入ipython并按回车即可
文档查看:按住ctrl键并点击模块名
库或者模块的引入:光标悬停在标红位置,点击提示即可引入,或者在代码中光标所在的位置,按下Alt + Enter,
PyCharm会自动分析代码并提供导入模块的选项。
在学习过程中,常要利用官方文档进行学习,要注意:1.关注输入与输出;2.关注参数与参数意义
测试:
三、数据预处理
一、numpy模块
张量:在机器学习中表示一个多维数组或者是标量、向量和矩阵的特例
广播规则:
-
如果两个数组的维度数不同,那么形状较少的数组会在其前面自动补1,直到两个数组的维度数相同。
-
如果两个数组的维度大小不同,那么维度大小为1的数组会在对应维度上进行扩展,使得其大小与另一个数组相同。
-
如果在任何维度上,两个数组的大小不相等且不为1,那么会引发一个广播错误。
import numpy as np # 创建两个数组 a = np.array([1, 2, 3]) b = np.array([[4], [5], [6]]) # 广播两个数组并相加 result = a + b # 打印结果 print(result) #结果为[[5 6 7] [6 7 8] [7 8 9]]
NumPy数组支持多维数据表示、广播(Broadcasting)、数组操作和函数。且兼容性强
numpy常用函数与方法:①numpy():将PyTorch的张量转换为NumPy数组;
②from_numpy():将NumPy数组转换为PyTorch张量;
③astype():修改PyTorch张量的数据类型;
④张量名.newaxis:为PyTorch张量添加新的维度;
⑤np.random模块:生成随机数。
二、transforms模块:
Tensor数据类型:一种多维数组或矩阵的数据结构,可以具有不同的维度。每个元素在Tensor中都有一个唯一的位置,通过指定索引可以访问和操作Tensor中的元素。
通过transforms操作数据:①Normalize标准化处理,将均值和标准差标准化;
②ToTensor数据形式转化,转化为Tensor对象;
③RandomRotation随机旋转角度;
④RandomHorizontalFlip随机水平翻转图像;
⑤Resize调整目标大小,可以通过插值方法指定调整大小时的像素插值算法,默认为双线性插值;
⑥compose:transforms.Compose
用于将多个图像变换操作组合在一起,以便对图像进行连续的变换。它接受一个变换函数列表作为输入,并返回一个新的变换函数,该函数会按照列表中的顺序对输入图像进行处理。
三、PIL模块(目前主要用于图像处理)
常用方法和函数:①Image.open():打开图像文件,返回一个image对象,以便后续操作。
②resize():调整图像大小。
③rotate():旋转图像,可以指定旋转角度和旋转中心。
④convert():转换图像格式,如转换为灰度图像或者转换为RGBA格式。注意,不同图像文件通道不同,在训练时往往需要加以筛选和保留
⑤crop():裁剪图像。
⑥save():保存图像文件,可以指定保存的文件名和保存的图像格式。
⑦像素操作:可以使用getpixel()
方法获取图像中指定位置的像素值。该方法接受一个包含像素坐标的元组作为参数,并返回该位置的像素值。可以使用putpixel()
方法修改图像中指定位置的像素值。该方法接受一个包含像素坐标和新像素值的元组作为参数,将指定位置的像素值修改为新的像素值。可以使用getdata()
方法获取图像的所有像素值。该方法返回一个可迭代的对象,其中包含图像的所有像素值。可以使用putdata()
方法修改图像的所有像素值。该方法接受一个包含所有新像素值的可迭代对象作为参数,将图像的所有像素值修改为新的像素值。
注:图像相关信息:像素坐标用于标识图像中的像素位置。通常,像素坐标从图像的左上角开始,从左到右递增,从上到下递增。
图像可以由不同的颜色通道组成,常见的包括RGB通道和灰度通道。灰度图像只有一个通道,每个像素的值表示灰度级别,通常范围也是0到255。
图像滤波是一种基于像素的操作,通过对图像的像素进行加权平均或变换来改变图像的外观或特征。
图像增强是一类技术,旨在改善图像的视觉质量或提取图像中的相关信息。
四、Dataest与Dataloader
torch.utils.data
模块是 PyTorch 用于处理数据集的核心模块。它提供了 Dataset
类、DataLoader
类和一些其他实用函数,用于加载、预处理和批处理数据。
一、Dataest
Dataset
是一个抽象类,用于表示数据集。
为了使用 Dataset
,需要继承这个类并实现两个方法:__len__
和 __getitem__
。__len__
方法返回数据集的长度,__getitem__
方法根据索引返回一个样本。根据自己的数据格式和需求创建自定义的数据集类,或使用 PyTorch 提供的预定义数据集类
import torch
from torch.utils.data import Dataset
# 自定义数据集类
class MyDataset(Dataset):
def __init__(self, data_path):
# 从本地文件加载数据
self.data = self.load_data(data_path)
def __len__(self):
return len(self.data)
def __getitem__(self, index):
return self.data[index]
def load_data(self, data_path):
# 加载数据的逻辑
# 这里假设数据文件是一个文本文件,每行包含一个样本
with open(data_path, 'r') as file:
data = [line.strip() for line in file]
return data
# 数据文件路径
data_path = 'data.txt'
# 创建数据集对象
dataset = MyDataset(data_path)
# 打印数据集长度
print(len(dataset))
# 打印第一个样本
print(dataset[0])
二、Dataloader
DataLoader
类则用于加载和批处理数据集。即为dataset所包含的数据集所服务。(数据集被处理后通过指定的的批次大小、顺序和并行性加载到模型中。)
名词注解: Epoch(周期) 就是使用训练数据集中的所有样本对模型进行一次更新的过程。随机洗牌(shuffle)。Normalization(归一化):用于将数据按比例缩放到一个特定的范围,通常是将数据映射到 0 到 1 或 -1 到 1 之间。Augmentation(数据增强):是指通过对原始数据进行随机或确定性的变换来生成更多的训练样本。Batch(批次):指的是将一组样本一起输入模型进行训练或推断的操作。数据加载器对象(DataLoader)是 PyTorch 提供的一个数据加载工具,用于将数据集按照指定的批次大小、顺序和并行性加载到模型中。
Dataloader常用方法与功能:
①创建 DataLoader 对象:
dataloader = DataLoader(dataset, batch_size, shuffle, num_workers, drop_last)
其中 dataest表示要转化的数据集对象,batch_size表示每个批次的样本数量,shuffle(bool值)表示是否打乱数据集,num_workers表示数据加载的子进程数目,drop_last(bool值)决定是否丢弃余下的不完整批次
五点五、OpenCV库
五、TensorBoard
可视化工具,用于可视化和分析模型的训练过程和结果。
模型内创建可视化图形:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.tensorboard import SummaryWriter
# 创建一个模型
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.fc = nn.Linear(10, 1)
def forward(self, x):
return self.fc(x)
# 准备训练数据
train_data = torch.randn(100, 10)
train_labels = torch.randn(100, 1)
# 创建模型实例和优化器
model = MyModel()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 创建SummaryWriter对象,指定日志保存路径
log_dir = "./logs"
writer = SummaryWriter(log_dir)
# 训练模型
num_epochs = 10
#初始化step标签
step = 0
for epoch in range(num_epochs):
# 前向传播和损失计算
outputs = model(train_data)
loss = nn.MSELoss()(outputs, train_labels)
# 反向传播和参数更新
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 将训练过程中的损失写入TensorBoard
writer.add_scalar("Loss/train", loss.item(), epoch,step)
# 写入标签并自增
step += 1
# 关闭SummaryWriter
writer.close()
而后需在终端内输入示例代码以打开tensorboard
tensorboard --logdir=path
#此处的path为创建数据文件的路径
而后输出结果会有端口号(一般为:http://localhost:6006)打开即可
注意,若创造的是图像可能要在网站内选择images才可见
六、torchvision
torchvision
是 PyTorch 的一个独立软件包,提供了一系列用于计算机视觉任务的工具和数据集。它包含了许多常用的数据集、模型架构以及图像处理函数,方便用户进行计算机视觉相关的任务,如图像分类、目标检测、图像生成等。
以下为torchvision常用的功能和模块:
①数据集:torchvision.datasets
模块提供了许多常用的计算机视觉数据集,可以使用 torchvision.datasets.<dataset_name>
来访问这些数据集;
②数据转换:torchvision.transforms
模块提供了一系列图像和张量的转换函数,用于数据预处理和增强。例如使用transforms.ToTensor()
将图像转换为张量,使用 transforms.Normalize()
进行归一化;
③模型:torchvision.models
模块包含了许多经典的计算机视觉模型,如 AlexNet、VGG、ResNet、DenseNet 等。可以使用 torchvision.models.<model_name>
来访问这些模型;
④图像工具:torchvision.utils
模块提供了一些实用的函数,用于图像的显示、保存和可视化。例如可以使用 torchvision.utils.make_grid()
创建一个图像网格,使用 torchvision.utils.save_image()
将张量保存为图像文件。
四、nn.Module 基本模型构建
nn.Module
是 PyTorch 中定义神经网络模型的基类。它是构建神经网络模型的核心组件之一。它提供了一些基本功能,使得我们可以方便地定义、组织和管理神经网络的各个组件,如层(layers)、激活函数(activation functions)、损失函数(loss functions)等。
还没懂...这一段会了再回来注解
官方文档示例
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 20, 5) # 定义卷积层
self.conv2 = nn.Conv2d(20, 20, 5) # 定义另一个卷积层
def forward(self, x): # 定义前向传播过程
x = F.relu(self.conv1(x)) # 将输入(x)传给卷积层conv1进行卷积操作,然后对返回的结果
# 应用激活函数ReLU
return F.relu(self.conv2(x)) # 将前一步操作的结果x传给conv2进行卷积操作,并对结果应
# 用激活函数ReLU,并且将其结果返回,作为该模型的输出
接下来先讲常见的各个层级(注:功能上因本人技术问题,难以具象的描述,故以描述技术性知识点为重)
一、输入层
即数据输入时进行数据处理的那一些层级
二、隐含层
这个概念貌似不够具象,其实就是各种操作层的综合
三、输出层
四、卷积层
作用:对输入数据进行特征提取,并生成输出特征图。减少网络的参数数量,并提高模型的效率和泛化能力。提高网络对平移、旋转和缩放等变换的鲁棒性。
原理:通过卷积操作来提取输入数据中的特征。卷积操作是一种局部感知机制,它通过将输入数据与一组学习到的卷积核(也称为滤波器)进行逐元素的乘积和求和运算来生成输出特征图。
基本过程:a. 定义卷积核;b. 滑动窗口计算卷积;c. 应用激活函数;d. 输出特征图
输入:卷积层的输入通常是上一层的输出特征图。输入特征图的维度通常是四维的,即[批次大小,通道数,高度,宽度]。
输出:输出特征图的维度也是四维的,与输入特征图的批次大小和通道数相同,但高度和宽度根据卷积操作和步幅进行调整。
五、池化层
作用:用于减小特征图的空间尺寸并提取主要特征。且因池化操作具有一定的平移不变性,有助于提高网络对输入的鲁棒性。
原理:在输入特征图上使用池化窗口进行滑动,然后根据窗口内的数值与池化方式进行聚合操作,得到池化层的输出值。常见的池化操作包括最大池化和平均池化,在池化窗口内选择最大值或计算平均值作为输出。
基本过程:a. 定义池化窗口大小和步幅;b. 滑动窗口并进行聚合;c. 输出池化结果。
输入:池化层的输入通常是卷积层的输出特征图。输入特征图的维度通常是四维的,即[批次大小,通道数,高度,宽度]。
输出:池化层根据池化窗口的大小和步幅对输入特征图进行下采样,生成相应的输出特征图。输出特征图的维度与输入特征图的通道数相同,但高度和宽度根据池化窗口大小和步幅进行调整。
五点五、非线性激活
非线性激活并不是网络的一个独立层,而是应用于每个神经元的一种操作。它作用于神经元级别,而不是网络级别。
作用:非线性激活函数在卷积层和全连接层之后的作用是引入非线性变换,以增加神经网络的表达能力和模型的拟合能力,增强特征表达。
原理:非线性激活函数通过对神经元输出进行非线性的变换,将线性变换的结果映射到非线性空间中。这种非线性变换可以通过引入非线性的数学函数来实现。
基本过程:a. 对输入数据线性变换;b. 非线性映射;c. 输出结果
输入:输入的一般直接就是上一层的结果,可以是一个特征图(特征映射)。
输出:非线性激活函数的输出是经过非线性变换后的结果。输出的数据格式与输入的数据格式相同,即在全连接层中仍然是向量,而在卷积神经网络中仍然是特征图。
六、全连接层(线性层)
常用于神经网络的最后一层,用于将高维特征映射到最终的输出类别或回归值。
全连接层中的每个神经元都与上一层的所有神经元相连,即每个输入特征都与每个神经元进行权重相乘和求和的操作。
作用:进行特征提取、特征组合,用于将输入数据映射到输出空间,并引入非线性变换。
原理:全连接层的原理基于线性变换和非线性激活函数的组合。
基本过程:
输入:全连接层接收一个大小为n的输入向量,表示为X = [x1, x2, ..., xn]。
输出:全连接层生成一个大小为m的输出向量,表示为Y = [y1, y2, ..., ym]。
七、循环层
八、标准化层
作用:标准化的作用是消除不同特征之间的量纲差异,使得数据在相同的尺度范围内进行比较和分析,同时有助于加速机器学习算法的收敛速度和提高模型的性能。
原理:对每个特征进行线性变换,将其转换为均值为0、标准差为1的标准正态分布(也称为Z分数)或者将特征缩放到一个指定的范围。
基本过程:a.均值归零:从每个特征的原始值中减去该特征的均值,使得特征的均值为0。这可以通过计算每个特征的均值(mean)并将其从原始值中减去来实现。b.方差缩放:将均值归零后的特征除以其标准差,以使得特征的标准差为1。
输入:通常是一个张量,具体维度和形状看具体需求;
输出:通常与输入数据格式相同,但是数据进行了标准化,可以直观的感受到量纲的统一
九、丢弃层
十、填充层
作用:在输入数据周围添加额外的边缘值(通常为0),以扩展输入的尺寸。有助于控制输出特征图的尺寸和感受野大小。
原理:填充层在输入特征图的周围添加额外的边缘值。填充的方式可以是在特征图的边缘添加一圈零值像素,也可以是在特征图的边缘复制原始像素值。
基本过程:a. 填充层接受一个输入张量,通常为BCHW的四维张量;b. 在输入的特征图周围进行填充操作;c. 输出一个形状为 (B, C, H', W') 的张量
输入:通常是一个形状为 (B, C, H, W) 的四维张量,其中 B 是批次大小(batch size),C 是通道数,H 和 W 分别是输入特征图的高度和宽度。
输出:填充层的输出是一个形状为 (B, C, H', W') 的张量,其中 H' 和 W' 是填充后的特征图的高度和宽度。
十一、正则化层
作用:用于控制模型的复杂度、减少过拟合并提高模型的泛化能力。正则化层通过在损失函数中引入额外的正则化项来约束模型的参数,防止模型过度拟合训练数据。
原理:在模型的损失函数中引入正则化项,以惩罚模型的复杂度。L1正则化通过将参数的L1范数(绝对值之和)添加到损失函数中,促使模型参数变得稀疏。L2正则化通过将参数的L2范数(平方和)添加到损失函数中,限制模型参数的大小。
基本过程:在损失函数中添加正则化项,并对其进行加权。并不改变输入数据的维度或形状。
输入与输出:没有显式的输入和输出。它是在模型的损失函数中引入正则化项,因此其输入是损失函数的其他部分,如预测值和真实标签。正则化层不改变输入数据的维度或形状。
五、损失函数与反向传播、优化器
这一堆适合结合代码,控制台结果,文档去看
一、损失函数
在神经网络中,损失函数用于衡量模型的预测输出与实际目标之间的差异。通过最小化损失函数,我们可以调整神经网络的参数,使其能够更好地拟合训练数据。
原理方面:损失函数的选择取决于任务类型和模型的特性。不同的损失函数有不同的原理和计算方式,但它们的共同目标是衡量模型的预测与真实值之间的差异。
常见的损失函数:均方误差、交叉熵损失、二分类交叉熵损失、逻辑损失、KL 散度等等
基本过程:a. 输入数据;b. 计算损失;c. 反向传播;d.参数更新;e. 重复迭代
输入:损失函数的输入通常包括两部分:模型的预测输出和实际目标。
输出:损失函数计算这两者之间的差异,并生成一个标量值作为输出,表示模型的预测误差。
二、反向传播
反向传播是计算神经网络中每个参数的梯度的过程。它从网络的输出层开始,通过链式法则逐层向后传递梯度,计算每个参数对于整体损失的贡献。具体而言,对于每个参数,反向传播计算其梯度,并将梯度传递给前一层的参数,直到达到网络的输入层。这样,反向传播能够有效地计算网络中每个参数的梯度。
三、梯度下降
梯度下降是一种基于梯度的优化算法,用于更新模型参数以最小化损失函数。在反向传播计算完参数的梯度之后,梯度下降根据梯度的反方向更新每个参数的值。
四、优化器
根据损失函数返回的损失值与梯度等对训练模型中的参数进行调整,以起到降低损失值的作用
(重点在于其算法,api使用不多讲)
优化器设置的一般语法:
#这里的tui是训练模型名
learning_rate = 1e-2
optimizer = torch.optim.SGD(tui.parameters(), lr=learning_rate)
六、基本迁移学习
一、模型与数据集导入
#导入模型
#pretrained参数表示是否经过预训练
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)
#导入数据集
#train参数表示要训练集还是测试集
train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(), download=True)
二、模型的增删改查(这里以vgg16为例)
#看整个模型
print(vgg16_true)
#添加层
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
#修改层
vgg16_false.classifier[6] = nn.Linear(4096, 10)
#删除层(一般都是修改模型中的某个模块,然后在修改时去掉不要的)
#下为保留法:创建一个新的模型,只包含要保留的层
new_model = nn.Sequential(*list(model.features.children())[:-1])
三、模型与数据的保存和加载
1.模型的保存和加载
①模型保存:使用torch.save()
函数将模型保存到文件(通常是以pth为后缀的文件)中。这种方法保存的是“带参数的模型”,使用时要新建一个对象进行加载,即:引入后还要把定义模型的那一堆类复制过来
Tui.load_state_dict("path")
class Tui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.relu1 = ReLU()
self.sigmoid1 = Sigmoid()
def forward(self, input):
output = self.sigmoid1(input)
return output
#后面无需 TUI = Tui() 这一句话
torch.save(model, 'model.pth')
②模型加载:使用torch.load()
函数加载保存的模型、张量或数据集。
tensor = torch.load('data.pth')
③模型状态的保存,仍使用save,但是有后缀
这种方法则是将模型的“状态”以字典形式保存下来,使用时要先建好模型然后将状态赋值给该模型
torch.save(model.state_dict(), 'model.pth')
④模型状态的加载,在加载模型后赋入状态
model = YourModel()
model.load_state_dict(torch.load('model.pth'))
2.数据集或张量的保存和加载
①保存:也是使用save函数,但要注意确保当前环境中有与数据集相关的类定义或模块导入,以便正确加载和使用数据集。
②载入:仍使用load函数载入
# 保存
torch.save(dataset_list, 'dataset.pth')
# 载入
dataset_list = torch.load('dataset.pth')
七、一般模型训练套路
一、数据集准备
训练数据集与测试数据集分别准备(此处涉及数据库知识,仅写出模型内的代码)
import torchvision
traindata = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=torchvision.transforms.ToTensor())
testdata = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=torchvision.transforms.ToTensor())
#检查
traindata_size = len(traindata)
testdata_size = len(testdata)
traindata_first = traindata[0]
import matplotlib.pyplot as plt
plt.imshow(traindata)
plt.axis('off') # 可选:关闭坐标轴显示
plt.show()
二、数据集预处理与加载
一般使用dataset和dataloader去处理数据并且生成对象以传入模型之中
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0,drop_last=True)
# 这里接收Tensor数据类型的数据集,返回一个数据加载器对象
for inputs, labels in dataloader:
# 在这里执行模型训练、推理等操作
pass
三、神经网络搭建
继承父类并定义结构
class Tui(nn.Module):
def __init__(self):
super(Tui, self).__init__()
self.module1 = nn.Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.module1(x)
return x
'''这些层的配置如下:
输入通道数为3(RGB图像)。
第一个卷积层:输入通道数为3,输出通道数为32,卷积核大小为5,填充为2。
第一个池化层(最大池化):池化窗口大小为2。
第二个卷积层:输入通道数为32,输出通道数为32,卷积核大小为5,填充为2。
第二个池化层:池化窗口大小为2。
第三个卷积层:输入通道数为32,输出通道数为64,卷积核大小为5,填充为2。
第三个池化层:池化窗口大小为2。
扁平化(reshape)层:将输入展平为一维向量。
第一个全连接层:输入大小为1024,输出大小为64。
第二个全连接层:输入大小为64,输出大小为10(假设有10个类别)。'''
四、验证模型
class Tui(nn.Module):
def __init__(self):
super(Tui, self).__init__()
self.module1 = nn.Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.module1(x)
return x
#进行运行验证
if __name__ =='__main__':
model = Tui()
input = torch.ones((64, 3, 32, 32))
output = model(input)
print(output.shape)
#由计算得出应该是[64, 10],如果是就说明数学过程上暂时没问题
五、损失函数定义
损失函数和优化器若需要参数,建议另外创建变量而非直接在函数内定义,以方便修改
loss_fn = nn.CrossEntropyLoss()
六、优化器定义
learning_rate = 0.01 #也有 1e-2 这种写法,其实也是0.01
optimizer = torch.optim.SGD(modelname.parameters(), lr=learning_rate)
七、训练网络设置
模型定义好之后先想好哪些参数想要实时观察,或者哪些参数想要初始化
#常设置的参数和观测量
#记录训练次数
total_train_step = 0
#记录测试的次数
total_test_step = 0
#想要训练的总轮数
epoch = 0
八、训练流程设计
使用循环开始模拟训练,调试训练步骤
for i in range(epoch):
print("-----{}-----".format(i+1))
for data in dataloader:
# 一次训练流程
imgs, targets = data
output = tui(imgs)
loss = loss_fn(outputs,targets)
# 优化器传入,优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step
# 标签自增与标识
total_train_step = total_train_step +1
print("训练次数:{},loss:{}".format(total_train_step,loss))
九、网络优化
根据需求将参数可视化或者调整参数,以便于模型改进与验证。此外,还需要注意网络模式的转化,如训练时的train语句,验证时的eval语句等等
# 损失函数可视化
#模型内为:loss = loss_fn(outputs,targets)
total_test_loss = total_test_loss + loss.item()
print("整体测试集上的loss:{}".format(total_test_loss))
#实际上还可以用if语句去限定,每十轮才打印一次也行
#数据可视化,添加tensorboard
writer = SummaryWriter("../logs_relu")
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images("input", imgs, global_step=step)
output = tui(imgs)
writer.add_images("output", output, step) #这一栏中的参数还可以显示测试次数之类的
step += 1
#最后记得要writer.close()
#保存每一轮的模型
#在循环内添加save语句
torch.save(tui,"tui_{}.pth".format(step))
print('模型已保存')
十、模型正确性验证
在PyTorch中,可以使用以下几种方法来验证模型的正确性
①准确率:可以使用测试数据集对模型进行推理,并将模型预测的结果与真实标签进行比较,然后计算预测正确的样本占总样本数量的比例。
#准确率使用示例
accuracy = (outputs.argmax(1) == targets).sum())
total_accuracy = total_accuracy +accuracy
print("模型上的正确率为:{}".format(total_accuracy/test_data_size))
②混淆矩阵:混淆矩阵是一种可视化工具,用于衡量分类模型在各个类别上的表现。它将真实标签与模型预测的标签进行交叉比较,并计算出每个类别的预测正确和错误的数量。
③精确率、召回率和F1分数:这些指标用于衡量分类模型在不同类别上的精确性、召回率和综合性能。
④ROC曲线和AUC:这些指标常用于评估二分类模型的性能。ROC曲线是以模型的真正例率(True Positive Rate)为纵轴,假正例率(False Positive Rate)为横轴的曲线图。AUC是ROC曲线下的面积,用于衡量模型的分类准确性。
⑤损失函数值:较低的损失值通常表示模型更接近真实结果。
⑥argmax:常用于分类任务中,用来确定具有最高概率或最高得分的类别。其中的dim参数决定比较的维度方向
模型验证的完整套路:一般思路是将测试数据载入训练好的模型中,使用eval模式进行操作并输出预测结果,再将预测结果与真实结果进行比较。不过不同的模型按情况分析啦
model = torch.load("tudui_29_gpu.pth", map_location=torch.device('cpu'))
print(model)
image = torch.reshape(image, (1, 3, 32, 32))
model.eval() #测试模式不一定要用,但是这是习惯——
with torch.no_grad():
output = model(image)
print(output)
print(output.argmax(1)) #这里的索引最大值就是预测的结果
八、GPU加速使用
方法一:训练时把模型和数据集什么的依次移入GPU,也就是在数据集,模型,损失函数后加入cuda方法
#此处移用土堆老师的示例代码
import torch
import torchvision
from torch.utils.tensorboard import SummaryWriter
# from model import *
# 准备数据集
from torch import nn
from torch.utils.data import DataLoader
train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 如果train_data_size=10, 训练数据集的长度为:10
print("训练数据集的长度为:{}".format(train_data_size))
print("测试数据集的长度为:{}".format(test_data_size))
# 利用 DataLoader 来加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 创建网络模型
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
tudui = Tudui()
#gpu加速可用性验证与将模型移入GPU
if torch.cuda.is_available():
tudui = tudui.cuda()
# 损失函数
loss_fn = nn.CrossEntropyLoss()
if torch.cuda.is_available():
loss_fn = loss_fn.cuda() #后面那些要独立定义的函数都要移入GPU
# 优化器
# learning_rate = 0.01
# 1e-2=1 x (10)^(-2) = 1 /100 = 0.01
learning_rate = 1e-2
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
#优化器没有cuda方法就不用了
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10
# 添加tensorboard
writer = SummaryWriter("../logs_train")
for i in range(epoch):
print("-------第 {} 轮训练开始-------".format(i+1))
# 训练步骤开始
tudui.train()
for data in train_dataloader:
imgs, targets = data
if torch.cuda.is_available():
imgs = imgs.cuda() # 看好,这些都要移入
targets = targets.cuda()
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
if total_train_step % 100 == 0:
print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 测试步骤开始
tudui.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的Loss: {}".format(total_test_loss))
print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size))
writer.add_scalar("test_loss", total_test_loss, total_test_step)
writer.add_scalar("test_accuracy", total_accuracy/test_data_size, total_test_step)
total_test_step = total_test_step + 1
torch.save(tudui, "tudui_{}.pth".format(i))
print("模型已保存")
writer.close()
方法二:定义训练设备再移入,除设备定义之外与法一类似
# 定义训练的设备
device = torch.device("cuda")
# 模型调用时
tudui = Tudui()
tudui = tudui.to(device)
# 损失函数定义时
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device)
#模型训练时
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
注:在GPU不一定可用时也可使用条件语句:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
方法三:在谷歌的云平台Google Colaboratory上面运行法一、法二中的代码,会去使用谷歌平台上的GPU