下载 CIFAR-10数据集:
官网:https://www.cs.toronto.edu/~kriz/cifar.html
CIFAR-10的网络结构:
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential#定义网络结构
class Model(nn.Module):def __init__(self):super().__init__()self.model1 = Sequential(Conv2d(3,32,5,stride=1,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.model1(x)return xmodel = Model()
print(model)input = torch.ones((64,3,32,32))
print(input.shape)
output = model(input)
print(output.shape)
早起模型和现在比较:和上面的是等价的
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter#步长的计算方法:
# 例:一个尺寸 a*a 的特征图,经过 b*b 的卷积层,步幅(stride)=c,填充(padding)=d,请计算出输出的特征图尺寸?
# 特征图的尺寸=(a-b+2d)/c+1
#定义模型结构
class Model(nn.Module):def __init__(self) -> None:super().__init__() # 初始化父类属性# self.conv1 = Conv2d(in_channels=3, out_channels=32,# kernel_size=5, padding=2)# self.maxpool1 = MaxPool2d(kernel_size=2)# self.conv2 = Conv2d(in_channels=32,out_channels=32,# kernel_size=5, padding=2)# self.maxpool2 = MaxPool2d(kernel_size=2)# self.conv3 = Conv2d(in_channels=32, out_channels=64,# kernel_size=5, padding=2)# self.maxpool3 = MaxPool2d(kernel_size=2)# self.flatten = Flatten() # 展平为1维向量,torch.reshape()一样效果# # 若是想检验1024是否正确,可以先写前面的层,看样例的输出大小,即可得到1024# self.linear1 = Linear(in_features=1024, out_features=64)# self.linear2 = Linear(in_features=64, out_features=10)# sequential可以替代前面备注掉的代码段self.model1 = Sequential(Conv2d(3, 32, 5, stride=1, padding=2), #输入3张图片,也就是三通道,生成32张,卷积核5*5,不常是1,padding是2MaxPool2d(2), #对输出进行池化2*2Conv2d(32, 32, 5, padding=2), #输入32张,输出32张,卷积核是5*5,padding是2MaxPool2d(2), #池化2*2Conv2d(32, 64, 5, padding=2), #输入是32,输出是64,卷积核是5*5,padding是5*5MaxPool2d(2), #池化2*2Flatten(), #扁平化Linear(1024, 64), #全连接输入1024,输出64Linear(64, 10) #输入64,输出10(表示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.model1(x) #将x传入模型,进行前向网络return xmodel = Model() # 创建一个实例
print(model) # 打印模型结构# 测试模型样例(也可以测试各层的输出是否正确)
input = torch.ones((64, 3, 32, 32)) #输入64张图片,3通道,32*32
print(input.shape) # torch.Size([64, 3, 32, 32])
output = model(input) #模型的输出
print(output.shape) # torch.Size([64, 10]),batch_size=64,10个参数writer = SummaryWriter("./logs_seq") # 创建一个实例
writer.add_graph(model, input) # 显示模型结构
writer.close()# tensorboard命令:tensorboard --logdir=logs_seq --port=6007'''
torch.nn.functional.conv2d 和 torch.nn.Conv2d的区别?
nn.Conv2d是一个类,而F.conv2d()是一个函数,而nn.Conv2d的forward()函数实现是用F.conv2d()实现的(在Module类里的__call__实现了forward()函数的调用,所以当实例化nn.Conv2d类时,forward()函数也被执行了,详细可阅读torch源码),所以两者功能并无区别,那么为什么要有这样的两种实现方式同时存在呢?原因其实在于,为了兼顾灵活性和便利性。在建图过程中,往往有两种层,一种如全连接层,卷积层等,当中有Variable,另一种如Pooling层,Relu层等,当中没有Variable。如果所有的层都用nn.functional来定义,那么所有的Variable,如weights,bias等,都需要用户来手动定义,非常不方便。而如果所有的层都换成nn来定义,那么即便是简单的计算都需要建类来做,而这些可以用更为简单的函数来代替的。
所以在定义网络的时候,如果层内有Variable,那么用nn定义,反之,则用nn.functional定义。'''
本地加载数据:
CPU下训练:
import time
import torch.optim.optimizer
import torchvision
from torch import nn, optim
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from 练习 import Model # 导入CIFAR_model.py里的Model类定义# 1.创建 CIFAR10 数据集的训练和测试实例
#实现本地加载
train_data = torchvision.datasets.CIFAR10(root="../data/CIFAR-10/", train=True,transform=torchvision.transforms.ToTensor(),download=False)
test_data = torchvision.datasets.CIFAR10(root="../data/CIFAR-10/", train=False,transform=torchvision.transforms.ToTensor(),download=False)# 2.利用 DataLoader 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)# 3.搭建神经网络:CIFAR-10
model = Model() # 创建实例# 4.损失函数
loss = nn.CrossEntropyLoss()# 5.优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), learning_rate)# 6.设置训练网络的一些参数
total_train_step = 0 # 记录训练的次数
total_test_step = 0 # 记录测试的次数
epoch = 10 # 训练的轮数for i in range(epoch):print(f"--------第{i}轮训练开始--------")# 训练步骤开始model.train() # 模型进入训练模式for data in train_dataloader: #获取图片和对应的标签imgs, targets = dataoutputs = model(imgs) #模型输入图片result_loss = loss(outputs, targets) # 计算每个参数对应的损失# 优化器优化模型optimizer.zero_grad() # 每个参数对应的梯度清零result_loss.backward() # 反向传播,计算每个参数对应的梯度optimizer.step() # 每个参数根据上一步得到的梯度进行优化total_train_step += 1if total_train_step % 100 == 0:end_time = time.time() # 结束时间print(f"训练次数:{total_train_step},Loss:{result_loss.item()}")# 测试步骤开始(每轮训练好、但不进行优化的模型)model.eval() # 模型进入测试模式(仅针对有Dropout,BatchNorm层的网络结构)total_test_loss = 0 #计算总的梯度损失total_accuracy = 0 #将准确度加起来,计算平均准确度with torch.no_grad(): # 不需要计算梯度,直接将数据放入网络进行测试for data in test_dataloader:imgs, targets = dataoutputs = model(imgs)result_loss = loss(outputs, targets)total_test_loss += result_loss # 损失累加# 计算标签正确数(取得在 1 方向,概率最大的索引,即得标签输出值;对比标签输出值与目标值+求和:True=1,False=0)accuracy = (outputs.argmax(1) == targets).sum()total_accuracy += accuracyprint(f"整体测试集上的Loss:{total_test_loss}")print(f"整体测试集上的正确率:{total_accuracy / len(test_data)}")total_test_step += 1torch.save(model, f"CIFAR_model_{i}.pth")print("模型已保存!")
添加GPU测试:
GPU版本a:网络模型、数据(输入、标签)、损失函数添加 .cuda()。
搭建神经网络:CIFAR-10模型
model = Model() # 创建实例
if torch.cuda.is_available(): # GPU是否可用print("使用GPU训练")model = model.cuda() # GPU(非必须重赋值)# model.cuda() # 与上一句效果一样,不用重赋值损失函数
loss_fn = nn.CrossEntropyLoss()
if torch.cuda.is_available(): # GPU是否可用loss_fn = loss_fn.cuda() # GPU(非必须重赋值)for data in train_dataloader:imgs, targets = dataif torch.cuda.is_available(): # GPU是否可用imgs = imgs.cuda() # GPU(必须重赋值)targets = targets.cuda() # GPU(必须重赋值)outputs = model(imgs)with torch.no_grad(): # 不进行优化的for data in test_dataloader:imgs, targets = dataif torch.cuda.is_available(): # GPU是否可用imgs = imgs.cuda() # GPU(必须重赋值)targets = targets.cuda() # GPU(必须重赋值)outputs = model(imgs)
GPU版本b:网络模型、数据(输入、标签)、损失函数添加.to(device)。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 定义训练的设备GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # ”cuda:0“ 等效 ”cuda“
print(device)搭建神经网络:CIFAR-10模型
model = Model() # 创建实例
model = model.to(device) # GPU(非必须重赋值)
# model.to(device) # 与上一句效果一样,不用重赋值损失函数
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.to(device) # GPU(非必须重赋值)for data in train_dataloader:imgs, targets = dataimgs = imgs.to(device) # GPUtargets = targets.to(device) # GPUoutputs = model(imgs)with torch.no_grad(): # 不进行优化的for data in test_dataloader:imgs, targets = dataimgs = imgs.to(device) # GPUtargets = targets.to(device) # GPUoutputs = model(imgs)
加载模型进行预测结果:
import torch
import torchvision
from PIL import Image
from CIFAR_model import Model # 导入CIFAR_model.py里的Model类定义image_path = "./boss.png"
image = Image.open(image_path) # 加载3通道的图片数据,3阶张量
print(image) # <PIL.Image.Image image mode=RGB size=352x261 at 0x1E61DE1FE50># 将图片 Resize(缩放) 到32x32尺寸,适合模型输入,最后在转化为Tensor实例
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),torchvision.transforms.ToTensor()])
image = transform(image)
# 转化为4阶张量(模型网络的输入要求张量的阶数为4)
image = torch.reshape(image, (1, 3, 32, 32))
print(image.shape) # torch.Size([1, 3, 32, 32])# -----------------1.测试方式a(常用,CPU上测试)-----------------
print("\nCPU上测试:")
# 加载训练好的模型
# 采用GPU训练的模型,要想在CPU上测试,必须映射到CPU上(或者模型不用映射到CPU上,而图片映射到GPU上)
model = torch.load("CIFAR_model_9.pth", map_location=torch.device("cpu"))
# print(model)model.eval() # 模型进入测试模式(仅针对有Dropout,BatchNorm层的网络结构)
with torch.no_grad(): # 有利于节约内存和性能output = model(image)
print(output)
print(output.argmax(1)) # 方向1最大值的索引值# -----------------2.测试方式b(GPU上测试)-----------------
print("\nGPU上测试:")
# 加载训练好的模型
model = torch.load("CIFAR_model_9.pth")
# print(model)model.eval() # 模型进入测试模式(仅针对有Dropout,BatchNorm层的网络结构)
with torch.no_grad(): # 有利于节约内存和性能# 将图片放到GPU上运行(采用GPU训练的模型,图片应该放到GPU上)output = model(image.cuda())# output = model(image.to("cuda:0")) # 等效于上一句
print(output)
print(output.argmax(1)) # 方向1最大值的索引值