1 环境配置
参考链接
2. dir 和 help函数
dir():用于查看某一模块函数的方法
help(): 用于查看某方法的使用方法
3. dataset类实战
利用Image对象打开图片,利用os模块的地址拼接组成图片路径
当我们用方括号访问元素对象时,实际上是调用了这个对象的__getitem__方法
from torch.utils.data import Dataset
from PIL import Image
import os
class MyData(Dataset):
# 初始化,根目录的相对地址和标签label
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) # 将对应路径下的元素名转换为列表
# 当我们用方括号访问元素对象时,实际上是调用了这个对象的__getitem__方法
def __getitem__(self, index): # 传入下标,获取对应图片
img_name = self.img_path[index] # 从元素名的列表获取需要的元素名
# 拼接所需图片的路径
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
def __len__(self): # 获取类中元素的数量
return len(self.img_path)
root_dir = "E:\\pythonProject\\dataset\\hymenoptera_data\\train"
ants_label_dir = "ants"
bees_label_dir = "bees"
ants_dataset = MyData(root_dir,ants_label_dir)
bees_dataset = MyData(root_dir,bees_label_dir)
# 整体数据集可以使用两个数据集相加
train_dataset = ants_dataset + bees_dataset
然后可以数据集图片对象进行操作
4.TensorBoard的使用(一)
创建SummaryWriter实例,并生成一个图像,保存与当前路径下的 ”logs”文件夹中
SummaryWriter是PyTorch中的一个工具,用于将模型训练过程中的日志保存到TensorBoard中。可以通过使用SummaryWriter的add_scalar()方法来保存损失函数、准确率等标量信息,使用add_histogram()方法来保存权重、梯度等张量信息,使用add_image()方法来保存图像信息等。
from torch.utils.tensorboard import SummaryWriter
# 传入某一文件夹的路径,默认为当前路径下的文件夹
writer = SummaryWriter("logs")
for i in range(10000):
# 参数1:tag(图表的名称/label) , 参数2:y轴的数值,参数3:x轴的数值
writer.add_scalar("y = x", i ,i)
writer.close()
在对应虚拟环境的终端启动TensorBoard: logdir = 保存对应图像的文件夹名;port为打开的端口号
结果:
5. Tensorboard的使用(二)
使用numpy格式,添加文件图片。
numpy格式有两种:opencv打开文件和使用numpy直接格式转换
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image
# 传入某一文件夹的路径,默认为当前路径下的文件夹
writer = SummaryWriter("logs")
img_path = "dataset/hymenoptera_data/train/bees/16838648_415acd9e3f.jpg"
img = Image.open(img_path) # img 为PIL的jpeg类型
img_array = np.array(img) # 将img转换为numpy类型
# numpy的类型的方法二 是用opencv打开
# global—step 为步骤顺序,dataformats 为 数据格式
writer.add_image("test",img_array,1,dataformats='HWC')
for i in range(100):
# 参数1:tag(图表的名称/label) , 参数2:y轴的数值,参数3:x轴的数值
writer.add_scalar("y = x", i ,i)
writer.close()
6. Transfroms的使用(一)
from torchvision import transforms
from PIL import Image
# transforms如何使用(python)
img_path = "dataset/hymenoptera_data/train/ants/0013035.jpg"
img = Image.open(img_path) # 打开文件,得到一个图片类的实例
# 获取一个ToTensor实例
tensor_trans = transforms.ToTensor()
# 由于存在__call__方法,调用该实例,调用该方法
# 由图片实例转换为 tensor类型
tensor_img = tensor_trans(img)
print(tensor_img)
结果:
注: call 是 Python 中一个魔术方法(magic method),它用于定义对象的函数调用行为。换句话说,当你尝试调用一个具有 call 方法的对象时,Python 会自动调用该方法。
ToTensor类的__call__方法:
7. Transforms的使用(二)
将图片转换为tensor对象,再将该对象利用tensorboard的SummaryWriter打开。
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
from PIL import Image
# transforms如何使用(python)
img_path = "dataset/hymenoptera_data/train/ants/0013035.jpg"
img = Image.open(img_path) # 打开文件,得到一个图片类的实例
# 获取一个ToTensor实例
tensor_trans = transforms.ToTensor()
# 由于存在__call__方法,调用该实例,调用该方法
# 由图片实例转换为 tensor类型
tensor_img = tensor_trans(img)
writer = SummaryWriter("logs")
# 参数 : self, tag, img_tensor, global_step=None, walltime=None, dataformats="CHW"
writer.add_image("Tensor_img",tensor_img) # 保存图像信息
writer.close()
启动tensorboard:
结果:
8.常见的Transform(一)
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
writer = SummaryWriter("logs")
img = Image.open("images/123.jpg")
# ToTensor,转换后张量的范围均为0-1
trans_totensor = transforms.ToTensor() # 创建ToTensor实例
img_tensor = trans_totensor(img) #调用内置__call__方法
writer.add_image("ToTensor",img_tensor)
# normalize 归一化
# 创建Normalize实例 ,输入分别为 均值 和 标准差
trans_norm = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
img_norm = trans_norm(img_tensor) # 其父类具有__call__方法,img_norm为tensor类型
writer.add_image("normalize", img_norm)
writer.close()
归一化会改变tensor的数据的范围,img_tensor中的值为[0,1] ,而使用标准差与均值均为0.5的归一化后,其值的范围变为[-1,1]。若标准差与均值改变,则范围也会改变
运行结果:
9. 常见的Transform(二)
(1)Resize
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
writer = SummaryWriter("logs")
img = Image.open("images/321.jpg")
# ToTensor,转换后张量的范围均为0-1
trans_totensor = transforms.ToTensor() # 创建ToTensor实例
img_tensor = trans_totensor(img) #调用内置__call__方法
writer.add_image("ToTensor",img_tensor)
# normalize 归一化
# 创建Normalize实例 ,输入分别为 均值 和 标准差
trans_norm = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
img_norm = trans_norm(img_tensor) # 其父类具有__call__方法,img_norm为tensor类型
writer.add_image("normalize", img_norm)
writer.close()
# Resize
trans_resize = transforms.Resize([256,256]) # 传入要缩放的长和宽的列表
img_resize = trans_resize(img) # 传入一个PIL数据类型,返回值也为PIL类型
img_resize_tensor = trans_totensor(img_resize) # 转换为tensor类型
writer.add_image("Resize",img_resize_tensor,0)
# Resize 写法2,使用compose,传入transform的列表,表示要进行的transform的操作
trans_resize2 = transforms.Resize(256) # 等比缩放
trans_compose = transforms.Compose([trans_resize2,trans_totensor]) # 先改变大小,再转换为tensor
img_resize_tensor2 = trans_compose(img)
writer.add_image("Resize",img_resize_tensor2,1)
运行结果:
(2) RandomCrop:随即裁剪
# RandomCrop 随机裁剪,如果输入一个值n,则会减为x*x;若输入两个值,则裁剪为x*y
trans_randowcrop = transforms.RandomCrop(256)
trans_compose2 = transforms.Compose([trans_randowcrop,trans_totensor])
for i in range(10): # 随机裁剪十次
img_randomcrop = trans_compose2(img)
writer.add_image("RandomCrop",img_randomcrop,i)
writer.close()
运行结果:
10. torchvision中的数据集使用
数据集常用的参数:
import torchvision
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
# 自定义使用的transform
dataset_transforms = transforms.Compose([
transforms.ToTensor() # 转化为tensor
])
# 数据集的路径,是否为训练数据集,使用的transform ,是否需要下载
train_set = torchvision.datasets.CIFAR10("./dataset1",train=True,transform=dataset_transforms,download=True)
test_set = torchvision.datasets.CIFAR10("./dataset1",train=False,transform=dataset_transforms,download=True)
writer = SummaryWriter("logs")
for i in range(10):
img, target = test_set[i] # img为图片的tensor类型
writer.add_image("test_set",img, i)
writer.close()
运行结果:
11. DataLoader的使用
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
test_set = torchvision.datasets.CIFAR10("./dataset1",train=False,transform=transforms.ToTensor(),download=True)
# batch_size: 批大小, shuffle表示是否打乱(一个epoch打乱一次),num_workers表示多进程数(0表示主进程进行)
# drop_last表示当数据总数与批大小不成比例,剩余不够一批的数据是否舍弃
test_loader = DataLoader(test_set,batch_size=64,shuffle=True,num_workers=0,drop_last=False)
writer = SummaryWriter("dataload")
for epoch in range(2): # epoch的数量设置为2
step = 0
for data in test_loader:
imgs, targets = data # imgs 是batch(一批)的图片张量
# 注意是add_images 不是 add_image
writer.add_images(f"Epoch:{epoch}",imgs, step)
step += 1
writer.close()
运行结果:
12. nn.Module的基本使用
import torch
from torch import nn
class Test(nn.Module):
def __init__(self):
super().__init__()
# 正向传播
def forward(self, x):
out = x + 1
return out
test = Test()
x = torch.tensor(2.0)
# 使用实例传参会调用__call__函数,在nn.Module下会自动调用forward方法
print(test(x)) # 输出3
13. 神经网路-卷积层
卷积层stride默认为1.而后面池化层的stride默认为窗口长/宽。而卷积层的滤波器的初始值是随机值(在一定初始化方法下的随机,例如:Kaiming初始化和Xavier初始化)
卷积层作用: 从输入数据中提取特征,形成特征图
池化层作用: 对特征图进行降维处理
import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
class MyConv(nn.Module):
def __init__(self):
super().__init__()
self.conv2 = nn.Conv2d(3,6,3)
def forward(self, x):
# 使用一个2d卷积,输入通道数为3,输出通道数为6,滤波器为3*3
x = self.conv2(x)
return x
test_data = torchvision.datasets.CIFAR10("./dataset1",train=False,
transform=torchvision.transforms.ToTensor(),download=True)
dataLoader1 = DataLoader(test_data,batch_size=64)
writer = SummaryWriter("logs")
myconv = MyConv()
step = 0
for data in dataLoader1:
imgs, targets = data # img.shape = 64,3,32,32
writer.add_images("imgs",imgs,step)
out = myconv(imgs) # out.shape = 64,6,30,30,不能直接被识别为图像
# 因此化为两个三通道图片
out = torch.reshape(out,(-1,3,30,30))
writer.add_images("out",out,step)
step += 1
writer.close()
结果:
14. 最大池化层的使用
import torchvision.datasets
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
class MyMaxpool(nn.Module):
def __init__(self):
super().__init__()
# ceil_mode表示是否保存 当池化窗口不满时,窗口中的最大值
self.maxpool = nn.MaxPool2d(3,ceil_mode=True)
def forward(self, x):
x = self.maxpool(x)
return x
test_data = torchvision.datasets.CIFAR10("./dataset1",train=False,
transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(test_data, batch_size=64)
writer = SummaryWriter("logs_maxpool")
mymaxpool = MyMaxpool()
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images("imgs",imgs, step)
out = mymaxpool(imgs)
writer.add_images("out",out,step)
step += 1
writer.close()
执行结果:
15. 非线性激活
非线性变换的目的: 为网络引入非线性特征
激活函数作用: 将神经网络的线性模型变为非线性的
ReLU中,inplace的用法
ReLU与Sigmold的简单使用
import torchvision.datasets
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
class MyRelu(nn.Module):
def __init__(self):
super().__init__()
# inplace 表示是否用输入的变量接收输出;若为false,则input不变,存在返回值output
self.relu = ReLU(inplace=False)
self.sigmoid = Sigmoid()
def forward(self, x):
x = self.sigmoid(x)
return x;
writer = SummaryWriter("logs_relu")
test_data = torchvision.datasets.CIFAR10("./dataset1",train=False,
transform=torchvision.transforms.ToTensor(),download=True)
dataLoader1 = DataLoader(test_data,batch_size=64)
myrelu = MyRelu()
step = 0
for data in dataLoader1:
imgs, targets = data
writer.add_images("imgs",imgs,step)
out = myrelu(imgs)
writer.add_images("out",out,step)
writer.close()
运行结果:
16. 线性层及其他层的介绍
线性层,也就等同于全连接层,将一个多维数据拉伸映射为一维
17. 搭建小实战Sequential的使用
需要分别计算每个卷积核的padding和stride(卷积后H,W不变,一般padding=(5卷积核宽度-1)的一半),利用公式计算,dilation默认为1。
import torch
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
class MySeq(nn.Module):
def __init__(self):
super().__init__()
self.module = Sequential(
# 卷积过后尺寸不变, padding = (kernel_size - 1) / 2
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2), # 目前为64*4*4,需flatten成线性的
Flatten(), # => 1024
Linear(1024,64), # 1024=>64
Linear(64,10),# 64=> 10
)
def forward(self, x):
x = self.module(x)
return x
myseq = MySeq()
x = torch.ones((64,3,32,32)) # 训练后 batch_size 不变
print(myseq(x).shape)
18. 损失函数和反向传播
交叉熵:
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("dataset1",False,
torchvision.transforms.ToTensor(),download=True)
class MySeq(nn.Module):
def __init__(self):
super().__init__()
self.module = Sequential(
# 卷积过后尺寸不变, padding = (kernel_size - 1) / 2
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2), # 目前为64*4*4,需flatten成线性的
Flatten(), # => 1024
Linear(1024,64), # 1024=>64
Linear(64,10),# 64=> 10
)
def forward(self, x):
x = self.module(x)
return x
dataloader = DataLoader(dataset,batch_size=1)
loss = nn.CrossEntropyLoss() # 交叉熵损失函数
myseq = MySeq()
for data in dataloader:
imgs, targets = data
outputs = myseq(imgs)
result_loss = loss(outputs,targets)
result_loss.backward() # 反向传播
# loss样例
x = torch.tensor([0.1,0.2,0.3])
x = torch.reshape(x,(1,3))
target = torch.tensor([1])
print(loss(x,target))
运行结果: 与预想一致
19. 优化器
优化器的作用:
PyTorch的优化器在深度学习模型的训练过程中起着至关重要的作用。它们的主要功能是根据计算得到的梯度信息来更新模型的参数,以最小化损失函数。以下是PyTorch优化器的主要作用:
- 参数更新:优化器负责根据计算得到的梯度信息更新模型的参数(包括权值)。梯度表示了损失函数关于每个参数的变化率,通过将梯度与学习率相乘,优化器可以决定参数在每个训练步骤中的更新幅度。
- 学习率调整:优化器还可以控制学习率的调整。学习率决定了参数更新的步长,过大或过小的学习率都可能导致训练不稳定或收敛速度缓慢。一些优化器提供了自适应调整学习率的功能,根据训练进程或其他因素自动调整学习率的大小。
- 参数优化算法:优化器实现了不同的参数优化算法,如随机梯度下降(SGD)、Adam、Adagrad、RMSprop等。这些算法在计算参数更新时使用不同的策略和规则,以提高训练效果和收敛速度。
- 动量:一些优化器支持动量的概念,通过引入动量项来加速参数更新。动量可以帮助优化器在参数空间中更快地搜索,并有助于克服局部最小值的困境。
总而言之,PyTorch的优化器在深度学习模型的训练中起着关键的作用,负责根据梯度信息更新模型参数、调整学习率,并利用不同的优化算法和技术来提高训练效果和收敛速度。选择合适的优化器要根据具体的问题和模型特性进行调整和实验
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.optim import SGD
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("dataset1",False,
torchvision.transforms.ToTensor(),download=True)
class MySeq(nn.Module):
def __init__(self):
super().__init__()
self.module = Sequential(
# 卷积过后尺寸不变, padding = (kernel_size - 1) / 2
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2), # 目前为64*4*4,需flatten成线性的
Flatten(), # => 1024
Linear(1024,64), # 1024=>64
Linear(64,10),# 64=> 10
)
def forward(self, x):
x = self.module(x)
return x
dataloader = DataLoader(dataset,batch_size=1)
loss = nn.CrossEntropyLoss() # 交叉熵损失函数
myseq = MySeq()
optim = SGD(myseq.parameters(), lr=0.01)
for epoch in range(20):
epoch_loss = 0.0
for data in dataloader:
imgs, targets = data
outputs = myseq(imgs)
result_loss = loss(outputs,targets)
optim.zero_grad() # 将梯度清零
result_loss.backward() # 反向传播,获取梯度
optim.step() # 利用优化器优化参数
epoch_loss = epoch_loss + result_loss
print(epoch_loss) # 输出一个epoch总的损失率
20. 现用模型的使用及修改
from torch import nn
from torchvision.models import vgg16
vgg16_false = vgg16()
# 迁移学习
# 1. 在最后添加一个线性层,将1024=》10
# vgg16_false.classifier.add_module('add_linear',nn.Linear(1000,10))
print(vgg16_false)
# 2. 修改最后一个线性层
vgg16_false.classifier[6] = nn.Linear(4096,10)
print(vgg16_false)
21. 模型保存和模型加载
模型保存
import torch
import torchvision.models
vgg16 = torchvision.models.vgg16()
# 保存方式1:保存整个模型(不推荐)
torch.save(vgg16,"vgg16_method1.pth")
# 保存方式2:仅保存参数(官方推荐)
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
模型加载
import torch
import torchvision
# 加载保存方式1,整个模型
model = torch.load("vgg16_method1.pth")
# print(model)
# 加载保存方式2,导入参数
# torch.load("vgg16_method2.pth") 导入模型的参数
vgg16_2 = torchvision.models.vgg16()
vgg16_2.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16_2)
运行结果:
22. 完整的模型训练套路(一)
训练的代码:
import torchvision.datasets
from torch.nn import CrossEntropyLoss
from torch.optim import SGD
from torch.utils.data import DataLoader
from torchvision import transforms
from model import MyModel
dataset_train = torchvision.datasets.CIFAR10("dataset1",True,transforms.ToTensor(),
download=True)
dataset_test = torchvision.datasets.CIFAR10("dataset1",False,transforms.ToTensor(),
download=True)
train_dataloader = DataLoader(dataset_train,batch_size=64)
test_dataloader = DataLoader(dataset_test,batch_size=64)
# length长度
train_length = len(dataset_train)
test_length = len(dataset_test)
print(f"训练集的长度:{train_length}")
print(f"测试集的长度:{test_length}")
# 创建网络模型
myModel = MyModel()
# 损失函数
loss_func = CrossEntropyLoss()
# 优化器
learning_rate = 1e-2
optimizer = SGD(myModel.parameters(), lr=learning_rate)
# 设置训练网络的一些参数
# 记录训练的总次数(训练一个batch算一次)
total_train_num = 0
# 记录测试的总次数
total_test_num = 0
# 训练的轮次
epoch = 10
for i in range(10):
print(f"-----第{i+1}轮训练开始-----")
for data in train_dataloader:
imgs, targets = data
outputs = myModel(imgs)
loss = loss_func(outputs, targets) # 输入预测结果和真实结果,得到损失率
# 优化器优化模型
optimizer.zero_grad() # 将梯度清零
loss.backward() # 反向传播计算梯度
optimizer.step() # 利用优化器优化参数
total_train_num = total_train_num + 1
print(f"训练次数:{total_train_num}, ;loss:{loss.item()}")
model.py
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
class MyModel(nn.Module):
def __init__(self):
super().__init__()
self.module = Sequential(
# 卷积过后尺寸不变, padding = (kernel_size - 1) / 2
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2), # 目前为64*4*4,需flatten成线性的
Flatten(), # => 1024
Linear(1024,64), # 1024=>64
Linear(64,10),# 64=> 10
)
def forward(self, x):
x = self.module(x)
return x
运行结果:
23.完整的模型训练套路(二)
with: 上下文管理器,with语句可以自动管理上下文资源,不论是什么原因跳出with块都能确保文件可以正确的关闭 ,以此来达到释放资源的目的。
with torch.no_grad() : 是一个用于临时禁用梯度计算的上下文管理器。
add_scalar: add_scalar() 用于展示标量,数
train()和eval() : 只针对某些层有用,例如drop_out层
代码:
import torch
import torchvision.datasets
from torch.nn import CrossEntropyLoss
from torch.optim import SGD
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
from model import MyModel
dataset_train = torchvision.datasets.CIFAR10("dataset1",True,transforms.ToTensor(),
download=True)
dataset_test = torchvision.datasets.CIFAR10("dataset1",False,transforms.ToTensor(),
download=True)
train_dataloader = DataLoader(dataset_train,batch_size=64)
test_dataloader = DataLoader(dataset_test,batch_size=64)
# length长度
train_length = len(dataset_train)
test_length = len(dataset_test)
print(f"训练集的长度:{train_length}")
print(f"测试集的长度:{test_length}")
# 创建网络模型
myModel = MyModel()
# 损失函数
loss_func = CrossEntropyLoss()
# 优化器
learning_rate = 1e-2
optimizer = SGD(myModel.parameters(), lr=learning_rate)
# 设置tensorboard可视化
writer = SummaryWriter("logs")
# 设置训练网络的一些参数
# 记录训练的总次数(训练一个batch算一次)
total_train_num = 0
# 记录测试的总次数
total_test_num = 0
# 训练的轮次
epoch = 10
for i in range(10):
print(f"-----第{i+1}轮训练开始-----")
# 训练步骤开始
myModel.train()
for data in train_dataloader:
imgs, targets = data
outputs = myModel(imgs)
loss = loss_func(outputs, targets) # 输入预测结果和真实结果,得到损失率
# 优化器优化模型
optimizer.zero_grad() # 将梯度清零
loss.backward() # 反向传播计算梯度
optimizer.step() # 利用优化器优化参数
total_train_num = total_train_num + 1
if total_train_num % 100 == 0:
print(f"训练次数:{total_train_num}, ;loss:{loss.item()}")
# 测试步骤开始
myModel.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
outputs = myModel(imgs)
loss = loss_func(outputs, targets)
total_test_loss = total_test_loss + loss.item() # 计算一epoch中的总损失
# argmax中 0 表示列比较 1 表示行比较 => 最后得到一行
accuracy = (outputs.argmax(1) == targets).sum() # 准确个数 为 输出与目标相等数量的总和
total_accuracy = total_accuracy + accuracy # 计算本轮总命中数
print(f"整体测试集上的loss:{total_test_loss}")
print(f"整体测试集上的准确率:{total_accuracy/test_length}")
writer.add_scalar("test_loss",total_test_loss,i+1)
writer.add_scalar("test_accuracy", total_accuracy/test_length, i+1)
# 保存模型
torch.save(myModel,f"MyModel_{i+1}.pth")
print("模型已保存")
writer.close()
运行结果:
24. 利用GPU训练(一)
使用方法1:
# 创建网络模型
myModel = MyModel()
if torch.cuda.is_available():
myModel = myModel.cuda()
# 损失函数
loss_func = CrossEntropyLoss()
if torch.cuda.is_available():
loss_func = loss_func.cuda()
imgs, targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
不使用GUP的时间:
使用GPU的时间:
使用GPU的方法二:
# 单显卡中cuda:0 == cuda
# device = torch.device("cuda") 写法1
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 常用写法
# 创建网络模型
myModel = MyModel()
myModel = myModel.to(device)
# 损失函数
loss_func = CrossEntropyLoss()
loss_func.to(device)
imgs, targets = data
imgs = imgs.to(device)
targets = targets.to(device)
运行结果:
25. 完整的模型验证套路
测试的图片:
import torch
import torchvision.transforms
from PIL import Image
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
class MyModel(nn.Module):
def __init__(self):
super().__init__()
self.module = Sequential(
# 卷积过后尺寸不变, padding = (kernel_size - 1) / 2
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2), # 目前为64*4*4,需flatten成线性的
Flatten(), # => 1024
Linear(1024,64), # 1024=>64
Linear(64,10),# 64=> 10
)
def forward(self, x):
x = self.module(x)
return x
image = Image.open("images/img_1.png")
print(image)
image = image.convert('RGB') # 将图片转为三通道
# 裁剪为32*32,再转换为Tensor类型
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape) # 3,32,32
# 加载训练好的模型
# 当在cpu上运行gpu上训练的模型时,需添加map_location参数
model = torch.load("MyModel_30_gpu.pth",map_location="cpu")
print(model)
# 输入应为 B C H W 四维
image = torch.reshape(image,(1,3,32,32))
model.eval()
with torch.no_grad():
output = model(image)
print(output)
print(output.argmax(1))
运行结果:
索引与分类的对应关系:
可知识别正确