(一)Dataset
Dataset 是一个抽象类,用于表示数据集。它封装了数据的加载和预处理逻辑,使得数据的读取和处理更加灵活和易于管理。在PyTorch中,torch.utils.data.Dataset
是一个基类,用户可以继承并实现自己的数据集。
import os
from torch.utils.data import Dataset
from PIL import Image
class CustomDataset(Dataset):
def __init__(self, root_dir, label_dir):
self.root_dir = root_dir
self.label_dir = label_dir
self.path = os.path.join(self.root_dir, self.label_dir)
self.img_path = os.listdir(self.path)
def __len__(self):
return len(self.img_path)
def __getitem__(self, idx):
img_name = self.img_path[idx]
img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
img = Image.open(img_item_path)
label = self.label_dir
return img, label
root_dir = 's-data/train'
ants_label_dir = 'ants_img'
bees_label_dir = 'bees_img'
ants_dataset = CustomDataset(root_dir, ants_label_dir)
bees_dataset = CustomDataset(root_dir, bees_label_dir)
img, label = bees_dataset[0]
img.show()
(二)TensorBoard
TensorBoard 是一个强大的可视化工具,它主要用于可视化和分析神经网络的训练过程,包括损失和准确率的变化、模型结构、图像数据、激活值等。
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image
writer = SummaryWriter('logs')
img_path = 's-data/train/bees_img/16838648_415acd9e3f.jpg'
img_PIL = Image.open(img_path)
img_array = np.array(img_PIL)
# global_step 表示写入的步数(类似索引)
writer.add_image('test', img_array, 2, dataformats='HWC')
for i in range(100):
writer.add_scalar('y=2x', 3 * i, i)
writer.close()
运行:
tensorboard --logdir=logs --port=6007
(三)常见的 Transforms
1. ToTensor
它将形状为 (H, W, C) 且像素值在 [0, 255] 范围的 PIL 图像或 NumPy 数组转换为形状为 (C, H, W) 且像素值在 [0.0, 1.0] 范围的PyTorch张量。
from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('logs')
img = Image.open('data/train/bees/17209602_fe5a5a746f.jpg')
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
writer.add_image('ToTensor', img_tensor)
writer.close()
2. Normalize
标准化的目的是使数据的各个特征具有相似的尺度,从而提高模型的训练效率和稳定性。用于标准化张量图像(tensor image)的每个通道。该变换接受两个参数:均值(mean)和标准差(std),并使用以下公式进行标准化:
output[channel] = (input[channel] - mean[channel]) / std[channel]
from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('logs')
img = Image.open('data/train/bees/17209602_fe5a5a746f.jpg')
trans_norm = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
img_norm = trans_norm(img_tensor)
writer.add_image('Normalize', img_norm, 1)
writer.close()
3. Resize
用于将输入的PIL图像调整到模型所需的固定尺寸。
接受目标尺寸作为参数,可以是一个整数或一个二元元组:
- 二元元组:如果提供一个二元元组,例如 (height, width),Resize 将图像调整为指定的高度和宽度。
- 整数:如果提供一个整数,Resize 将保持图像的长宽比不变,并将图像的较短边调整为该整数长度,较长边则根据原始比例自动调整。
from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('logs')
img = Image.open('data/train/bees/17209602_fe5a5a746f.jpg')
trans_resize = transforms.Resize((300, 300))
trans_resize2 = transforms.Resize(500)
img_resize = trans_resize(img)
img_resize2 = trans_resize2(img)
writer.add_image('Resize', trans_totensor(img_resize), 1)
writer.add_image('Resize', trans_totensor(img_resize2), 2)
writer.close()
4. RandomCrop、CenterCrop
RandomCrop 可以从输入图像中随机裁剪出一个指定大小的区域。
CenterCrop 可以 从图像中心裁剪出指定大小的区域。
trans_random = transforms.RandomCrop(200)
img_random = trans_random(img)
writer.add_image('RandomCrop', trans_totensor(img_random), 1)
trans_center = transforms.CenterCrop(200)
img_center = trans_center(img)
writer.add_image('CenterCrop', trans_totensor(img_center), 1)
5. ToPILImage
用于将 tensor 张量或 NumPy 数组转换为 PIL 图像。
trans_pil = transforms.ToPILImage()
img_pil = trans_pil(img_tensor)
print(type(img_pil)) # <class 'PIL.Image.Image'>
6. Compose
它将一系列图像变换按顺序应用于输入图像,形成一个统一的变换流水线。
from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('logs')
img = Image.open('data/train/bees/17209602_fe5a5a746f.jpg')
transform = transforms.Compose([
transforms.Resize((300, 300)),
transforms.CenterCrop(200),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
img_compose = transform(img)
writer.add_image('Compose', img_compose, 2)
writer.close()
7. ColorJitter
随机更改图像的亮度、对比度、饱和度和色调。
trans_color = transforms.ColorJitter(brightness=1, contrast=1, saturation=1, hue=0.5)
img_color = trans_color(img)
writer.add_image('ColorJitter', trans_totensor(img_color), 1)
8. Grayscale
将图像转换为灰度。默认是1
trans_gray = transforms.Grayscale(3)
img_gray = trans_gray(img)
writer.add_image('Grayscale', trans_totensor(img_gray), 1)
(四)DataLoader
用于加载和批量处理数据集。它提供了常见的数据操作功能,如批量加载、数据洗牌、并行数据加载等,从而简化了数据预处理和模型训练流程。
dataloader = DataLoader(dataset, batch_size=64, shuffle=True, num_workers=4)
- batch_size:每个批次的数据量大小。比如batch_size=64 表示每次从数据集中取 64 个样本。
- shuffle:在每个 epoch 结束后,是否对数据进行洗牌。shuffle=True 可以打乱数据的顺序,有助于提高模型的泛化能力。
- num_workers:用于数据加载的子进程数。比如 num_workers=4 表示使用 4 个子进程加载数据。增加 num_workers 数量可以加速数据加载过程,尤其是在数据预处理复杂或数据集较大时。
- drop_last:如果数据集大小不能被批量大小整除,drop_last=True 则会丢弃最后一个不完整的批次。
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
test_data = torchvision.datasets.CIFAR10(root='./CIFA10', train=False, download=True,
transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0, drop_last=True)
writer = SummaryWriter('CIFA-logs')
step = 0
for data in test_loader:
imgs, targets = data
writer.add_images('testdata_loader', imgs, step)
step = step + 1
writer.close()
(五)神经网络
1. 基本骨架 — nn.Module的使用
在 PyTorch 中,nn.Module 是构建神经网络的基础类。所有的神经网络模块都应继承自 nn.Module,并实现 forward 方法。在该类中,定义了网络的层以及前向传播的方式。
- 继承 nn.Module:定义一个类来表示你的神经网络,并使它继承 nn.Module。
- 定义网络层:在
__init__
方法中定义网络的各个层,并使用 nn.Module 的子模块(如 nn.Linear,nn.Conv2d 等)。 - 实现前向传播:在 forward 方法中定义输入数据如何通过网络层计算输出。这是网络前向传播的逻辑。
import torch
from torch import nn
class Module(nn.Module):
def __init__(self):
super().__init__()
def forward(self, input):
output = input + 1
return output
x = torch.tensor(1.0)
model = Module()
output = model(x)
print(output) # tensor(2.)
2. 卷积操作 — nn.functional.conv
torch.nn.functional.conv 是 PyTorch 中用于直接调用卷积操作的函数。它有多个变体,如 conv1d、conv2d、conv3d,分别对应一维、二维和三维卷积。与 nn.Conv2d 等模块化接口不同,nn.functional.conv 需要手动指定所有的参数,包括权重和偏置,因此更灵活,但也需要更多手动管理。
- input:输入张量,形状为
(minibatch, in_channels, iH, iW)
,其中minibatch
是批量大小,in_channels
是输入通道数,iH
和iW
是输入的高度和宽度。 - weight:卷积核的权重张量,形状为
(out_channels, in_channels/groups, kH, kW)
,其中out_channels
是输出通道数,in_channels/groups
是每组的输入通道数,kH
和kW
是卷积核的高度和宽度。 - bias:偏置项,形状为
(out_channels)
。 - stride:卷积操作的步幅,默认值为1。可以是一个整数或一个元组
(sH, sW)
。 - padding:输入的填充大小,默认值为0。可以是一个整数或一个元组
(padH, padW)
,用于控制输出的空间维度。 - dilation:卷积核元素之间的间距,默认值为1。可以是一个整数或一个元组
(dH, dW)
。 - groups:控制输入和输出的连接方式,默认值为1。
groups=1
表示标准卷积,groups=in_channels
表示深度卷积,每个输入通道有一个单独的滤波器。
import torch
import torch.nn.functional as F
# 输入 5x5 图像
input = torch.tensor([[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]])
# 卷积核
kernel = torch.tensor([[1, 2, 1],
[0, 1, 0],
[2, 1, 0]])
# input – input tensor of shape(batch_size, in_channels, 𝑖𝐻, 𝑖𝑊)
# 转成四维张量
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))
output = F.conv2d(input, kernel, stride=1)
print(output)
output2 = F.conv2d(input, kernel, padding=1)
print(output2)
3. 卷积层 — nn.Conv2d
在 PyTorch 中,卷积层可以通过 torch.nn.Conv 模块实现。根据不同的卷积类型,PyTorch 提供了多个类,包括 nn.Conv1d、nn.Conv2d 和nn.Conv3d,分别用于一维、二维和三维卷积操作。最常用的是 nn.Conv2d,用于处理二维图像数据。
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils import tensorboard
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10('./CIFA10', train=False, download=True,
transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=64)
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = Conv2d(3, 3, 3, 1, 0)
def forward(self, x):
x = self.conv1(x)
return x
cnn = CNN()
writer = tensorboard.SummaryWriter('CIFA-logs')
step = 0
for data in dataloader:
imgs, targets = data
output = cnn(imgs)
writer.add_images('input', imgs, step)
writer.add_images('output', output, step)
step += 1
writer.close()
4. 池化层 — 最大池化 nn.MaxPool2d 的使用
nn.MaxPool2d 是 PyTorch 中的一个池化层,用于在卷积神经网络(CNN)中执行二维的最大池化操作。最大池化的目的是通过取局部窗口的最大值来减小特征图的尺寸,从而减少计算量、参数数量和过拟合的风险,同时保留重要的特征信息。
- kernel_size:窗口的大小,可以是单个整数(表示高度和宽度相同)或元组(分别指定高度和宽度)。
- stride:步幅,指定窗口移动的步长。可以是单个整数或元组。默认情况下等于 kernel_size。
- padding:填充,指定在输入张量的边缘填充的大小。默认值为 0。
- dilaton:控制窗口中元素的间距。默认值为 1。
- ceil_mode:为 True 时数据不够就取,为 False 数据不够就丢弃。
import torch
from torch import nn
from torch.nn import MaxPool2d
input = torch.tensor([[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]])
input = torch.reshape(input, (1, 5, 5))
class MaxPool(nn.Module):
def __init__(self):
super(MaxPool, self).__init__()
self.maxpool1 = MaxPool2d(3, ceil_mode=True)
self.maxpool2 = MaxPool2d(3, ceil_mode=False)
def forward(self, x):
output1 = self.maxpool1(x)
output2 = self.maxpool2(x)
return output1, output2
model = MaxPool()
output1, output2 = model(input)
print(output1)
print(output2)
import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils import tensorboard
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10('CIFA10', train=False, transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset, batch_size=64)
class MaxPool(nn.Module):
def __init__(self):
super(MaxPool, self).__init__()
self.maxpool = MaxPool2d(3, ceil_mode=True)
def forward(self, x):
output = self.maxpool(x)
return output
model = MaxPool()
writer = tensorboard.SummaryWriter('CIFA-logs')
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images('input', imgs, step)
output = model(imgs)
writer.add_images('maxpool_output', output, step)
step += 1
writer.close()
5. 非线性激活函数
用于在神经网络中引入非线性特性。它们的作用是在输入数据经过线性变换后,施加一个非线性的映射,从而使得神经网络能够学习到复杂的模式和特征。
import torch
import torchvision
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils import tensorboard
from torch.utils.data import DataLoader
input = torch.tensor([[1, -0.5],
[-1, 3]])
dataset = torchvision.datasets.CIFAR10('CIFA10', train=False, download=True,
transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=64)
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.relu = ReLU()
self.sigmoid = Sigmoid()
def forward(self, x):
output1 = self.relu(x)
output2 = self.sigmoid(x)
return output1, output2
model = Model()
output1, output2 = model(input)
print(output1)
print(output2)
writer = tensorboard.SummaryWriter('CIFA-logs')
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images('非线性-input', imgs, step)
ouput1 = model(imgs)[0]
writer.add_images('relu', ouput1, step)
output2 = model(imgs)[1]
writer.add_images('sigmoid', output2, step)
step += 1
writer.close()
6. 线性层 — nn.linear
nn.Linear 是 PyTorch 中用于实现线性变换的层,通常用于全连接神经网络(也称为前馈神经网络)的隐藏层和输出层。它执行的操作是对输入数据进行线性变换,即输入向量乘以权重矩阵并加上偏置项。
import torch
from torch import nn
from torch.nn import Linear
class LinearLayer(nn.Module):
def __init__(self):
super(LinearLayer, self).__init__()
self.linear = Linear(4, 3, bias=True)
# 随机初始化
print('weights:', self.linear.weight)
print('Bias:', self.linear.bias)
def forward(self, x):
return self.linear(x)
model = LinearLayer()
input = torch.tensor([[1.0, 2.0, 3.0, 4.0]])
output = model(input)
print(output)
7. 搭建小实战和Sequential的使用
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils import tensorboard
class CIFAR(nn.Module):
def __init__(self):
super(CIFAR, self).__init__()
# self.conv1 = Conv2d(3, 32, 5, padding=2)
# self.maxpool1 = MaxPool2d(2)
# self.conv2 = Conv2d(32, 32, 5, padding=2)
# self.maxpool2 = MaxPool2d(2)
# self.conv3 = Conv2d(32, 64, 5, padding=2)
# self.maxpool3 = MaxPool2d(2)
# # 它的作用是将多维的输入数据展平成一维。通常在卷积神经网络(CNN)中使用,用于将卷积层或池化层的输出转换为全连接层的输入格式。
# self.flatten = Flatten()
# # 全连接层
# self.linear1 = Linear(64 * 4 * 4, 64)
# self.linear2 = Linear(64, 10)
# 第二种写法:
self.model = 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(64 * 4 * 4, 64),
Linear(64, 10)
)
def forward(self, x):
# x = self.conv1(x)
# x = self.maxpool1(x)
# x = self.conv2(x)
# x = self.maxpool2(x)
# x = self.conv3(x)
# x = self.maxpool3(x)
# x = self.flatten(x)
# x = self.linear1(x)
# x = self.linear2(x)
x = self.model(x)
return x
cifar = CIFAR()
print(cifar)
input = torch.ones((64, 3, 32, 32))
output = cifar(input)
print(output.shape)
writer = tensorboard.SummaryWriter("CIFA-logs")
writer.add_graph(cifar, input)
writer.close()
8. 损失函数与反向传播
损失函数是一个函数,用于衡量模型的预测输出与实际目标之间的差距。它的值越小,表示模型的预测越接近真实值。损失函数的选择取决于具体的任务类型,如回归、分类等。
L1Loss(也称为 绝对值损失 或 平均绝对误差,MAE):它用于测量预测值和真实值之间的差异,通过计算预测值与真实值之差的绝对值来量化这种差异。
MSELoss(Mean Squared Error Loss,均方误差损失):特别是在回归任务中。它通过计算预测值和真实值之间差异的平方来量化预测误差,并取所有样本误差的平均值。
CrossEntropyLoss(交叉熵损失): 是深度学习中常用于分类任务的一种损失函数。它结合了 softmax 激活函数和负对数似然损失,用于衡量模型的预测概率分布与真实标签分布之间的差异。
import torch
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
# L1Loss
loss_fn1 = L1Loss() # 默认是mean,求均值损失
res1 = loss_fn1(inputs, targets)
print(res1) # tensor(0.6667)
loss_fn2 = L1Loss(reduction='sum')
res2 = loss_fn2(inputs, targets)
print(res2) # tensor(2.)
# MSELoss
loss_fn3 = MSELoss()
res3 = loss_fn3(inputs, targets)
print(res3) # tensor(1.3333)
loss_fn4 = MSELoss(reduction='sum')
res4 = loss_fn4(inputs, targets)
print(res4) # tensor(4.)
# CrossEntropyLoss
loss_fn5 = CrossEntropyLoss()
# 未经过 softmax 的原始分数(logits)
# CrossEntropyLoss 自动将 y_pred 经过 softmax 后计算负对数似然损失,因此不需要手动应用 softmax 函数
y_pred = torch.tensor([[2.0, 1.0, 0.1]])
y_true = torch.tensor([0])
res5 = loss_fn5(y_pred, y_true)
print(res5) # tensor(0.4170)
反向传播(Backpropagation)是神经网络训练过程中用于计算和更新模型参数的算法。它基于梯度下降的思想,通过计算损失函数关于模型参数的梯度来更新参数,使损失函数的值最小化。
(六)优化器
torch.optim 是 PyTorch 中用于实现各种优化算法的模块。优化器的作用是更新模型参数,以最小化损失函数。它们基于梯度下降法,但每种优化器有不同的更新策略。
1. SGD (随机梯度下降)
基本的优化算法,逐步更新参数。支持动量项来加速收敛。
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential, CrossEntropyLoss
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10('CIFA10', train=False, transform=torchvision.transforms.ToTensor(),
download=True)
dataloader = DataLoader(dataset, batch_size=64)
class CIFAR(nn.Module):
def __init__(self):
super(CIFAR, self).__init__()
self.model = 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(64 * 4 * 4, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
loss = CrossEntropyLoss()
cifar = CIFAR()
optimizer = torch.optim.SGD(cifar.parameters(), lr=0.001)
for epoch in range(20):
running_loss = 0
for data in dataloader:
imgs, targets = data
outputs = cifar(imgs)
res = loss(outputs, targets)
optimizer.zero_grad()
res.backward()
optimizer.step()
running_loss += res
print(running_loss)
(七)现有网络模型的使用及修改
import torchvision
from torch import nn
from torchvision.models import VGG16_Weights, vgg16
vgg16_false = vgg16(weights=None)
vgg16_true = vgg16(weights=VGG16_Weights.DEFAULT)
train_data = torchvision.datasets.CIFAR10('CIFA10', train=True, download=True,
transform=torchvision.transforms.ToTensor())
# 添加一个线性层
vgg16_true.classifier.add_module('7', nn.Linear(1000, 10))
print(vgg16_true)
# 修改一个线性层
vgg16_true.classifier[6] = nn.Linear(4096, 10)
print(vgg16_true)
(八)模型的保存与读取
1. 保存整个模型
import torch
import torchvision
vgg16 = torchvision.models.vgg16(weights=torchvision.models.VGG16_Weights.DEFAULT)
# 保存方式1(保存模型结构 + 参数)
torch.save(vgg16, 'vgg16.pth')
import torch
model = torch.load('vgg16.pth')
print(model)
2. 保存模型的参数(推荐)
import torch
import torch.nn as nn
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.fc = nn.Linear(10, 1)
def forward(self, x):
return self.fc(x)
model = Model()
torch.save(model.state_dict(), 'myModel_path') # 假设保存的是已经训练好的参数
model = Model() # 重新实例化(参数是随机初始化的)
model.load_state_dict(torch.load('myModel_path')) # 将训练好的参数加载进来
print(model)
(九)完整的模型训练套路
model:
import torch
from torch import nn
class CIFAR_Net(nn.Module):
def __init__(self):
super(CIFAR_Net, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64 * 4 * 4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
return self.model(x)
if __name__ == '__main__':
cifar = CIFAR_Net()
input = torch.ones((64, 3, 32, 32))
output = cifar(input)
print(output.shape)
import torch
import torchvision
from torch.nn import CrossEntropyLoss
from torch.utils import tensorboard
from torch.utils.data import DataLoader
from model import *
# 准备数据集
train_data = torchvision.datasets.CIFAR10('CIFA10', train=True, download=True,
transform=torchvision.transforms.ToTensor())
test_data = torchvision.datasets.CIFAR10('CIFA10', train=False, download=True,
transform=torchvision.transforms.ToTensor())
# 数据集大小
train_data_size = len(train_data)
test_data_size = len(test_data)
print(f"train_data_size: {train_data_size}, test_data_size: {test_data_size}")
# 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_loader = DataLoader(test_data, batch_size=64)
# 创建网络模型
cifar = CIFAR_Net()
# 损失函数
loss_fn = CrossEntropyLoss()
# 优化器
learning_rate = 0.01 # 也可以写成 1e-2 = 1 * 10的-2次方
optimizer = torch.optim.SGD(cifar.parameters(), lr=learning_rate)
# 设置训练网络的一些参数
# 设置训练的次数
total_train_step = 0
# 设置测试的次数
total_test_step = 0
# 设置训练的轮数
epoch = 10
writer = tensorboard.SummaryWriter('CIFA-logs')
for i in range(epoch):
print(f'第{i + 1}轮训练开始')
# 训练步骤开始
cifar.train()
total_train_loss = 0
for data in train_dataloader:
imgs, targets = data
outputs = cifar(imgs)
loss = loss_fn(outputs, targets)
total_train_loss += loss.item()
# 反向传播
optimizer.zero_grad()
loss.backward()
# 更新权重
optimizer.step()
# 记录训练的次数
total_train_step += 1
if total_train_step % 100 == 0:
print(f'第{total_train_step}步训练,loss为{loss.item()}')
# 添加到tensorboard
writer.add_scalar('train_loss', loss.item(), total_train_step)
print(f'第{i + 1}轮训练结束,loss为{total_train_loss}')
# 测试步骤开始
cifar.eval()
total_test_loss = 0
total_accuracy = 0
# 上下文管理器,用于在不需要计算梯度的情况下运行PyTorch张量。
with torch.no_grad():
for data in test_loader:
imgs, targets = data
outputs = cifar(imgs)
loss = loss_fn(outputs, targets)
total_test_loss += loss.item()
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy += accuracy.item()
# 记录测试的次数
total_test_step += 1
if total_test_step % 100 == 0:
print(f'第{total_test_step}步测试,loss为{loss.item()}')
# 添加到tensorboard
writer.add_scalar('test_loss', loss.item(), total_test_step)
print(f'第{i + 1}轮测试结束,loss为{total_test_loss}')
print(f'第{i + 1}轮测试结束,accuracy为{total_accuracy / test_data_size}')
writer.add_scalar('test_accuracy', total_accuracy / test_data_size, i + 1)
torch.save(cifar, f'cifar_{i + 1}.pth')
writer.close()
(十)利用GPU训练
1. 第一种方法
在下方三处加入 cuda()
- 网络模型
- 数据(输入、标注)
- 损失函数
2. 第二种方法
(十一)完整的模型验证套路
import torch
import torchvision
from PIL import Image
img_path = 'imgs/cat5.png'
image = Image.open(img_path)
transform = torchvision.transforms.Compose([
torchvision.transforms.Resize((32, 32)),
torchvision.transforms.ToTensor()
])
image = transform(image)
model = torch.load('cifar_30.pth', weights_only=False, map_location=torch.device('cpu'))
image = torch.reshape(image, (1, 3, 32, 32))
model.eval()
with torch.no_grad():
output = model(image)
idx = output.argmax(dim=1)
classes = ('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
print(classes[idx])