2023.04.02
(一)优化器
同样是在pytorch官网找到优化器(optim),查看官方文档。下面以
optim = torch.optim.SGD(nerualnetwork.parameters(), lr=0.01) 为例
optim = torch.optim.SGD(nerualnetwork.parameters(), lr=0.01)
for epoch in range(20):
running_loss = 0.0
for data in dataloader:
imgs, targets = data
output = nerualnetwork(imgs)
# 计算交叉熵损失函数 网络的输出和真实的target之间的差距
result_loss = loss(output, targets)
# 梯度下降(反向传播
optim.zero_grad()
result_loss.backward()
optim.step()
running_loss = running_loss + result_loss
print(running_loss)
使用优化器时要先调用 optim.zero_grad() 将原来的梯度置零,然后再进行反向传播。
一般网络会进行成百上千甚至上万次学习(这里设置的是20次),理论上来说误差函数要越来越小。
(二)Pytorch提供的网络模型及修改使用
以vgg16为例:
vgg16能够将图片分成1000个类别,我们只需要10个类别就足够了,所以修改的办法是:
1. 加入一个线性层(将1000映射到10)
2.直接将最后一个线性层的参数修改(1000修改成10)
1. 加入一个线性层
vgg16_true.add_module('add_linear', nn.Linear(1000, 10))
这么做的结果是在网络最后多了一个名为(add_linear)的 model
那么要是希望将此线性层加在上一个(或任一个)model 中:
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
2. 直接将最后一个线性层的参数修改
vgg16_false.classifier[6] = nn.Linear(4096, 10)
(三)网络模型的保存与读取
1.保存方式1:模型结构+模型参数
torch.save(vgg16, 'vgg16_method1.pth')
加载模型:
model = torch.load('vgg16_method1.pth')
2.保存方式2:模型参数(官方推荐)
torch.save(vgg16.state_dict(), 'vgg16_method2.pth') # 将vgg16保存成字典形式
加载模型:
model = torch.load('vgg16_method2.pth')
结果是由参数组成的字典,没有网络结构的相关信息
要显示网络相关结构的话,需要下面代码:
# model = torch.load('vgg16_method2.pth')
vgg16 = torchvision.models.vgg16(weights=None)
vgg16.load_state_dict(torch.load('vgg16_method2.pth'))
print(vgg16)
(四)完整的模型训练套路
4.1 首先测试网络的正确性
一般网络模型是单独放在一个.py文件里的,并在里面测试网络的正确性
比如有这样一个网络(CIFAR 10):
# 搭建神经网络
class CIFAR10(nn.Module):
def __init__(self):
super(CIFAR10, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2,),
nn.Conv2d(32, 32, 4, 1 ,2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
测试输出结果的正确性:
if __name__ =='__main__'
cifar10 = CIFAR10()
# 创建输入尺寸
input = torch.ones((64, 3, 32, 32))
output = cifar10(input)
print(output)
4.2 进行训练前的一些初始化
# 初始化网络
cifar10 = CIFAR10()
# 创建损失函数 (因为网络处理的是分类问题,所以可以使用交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()
# 定义优化器
learning_rate = 0.01
optim = torch.optim.SGD(cifar10.parameters(), lr=learning_rate)
# 设置训练网络的参数
# 1. total_train_step:记录训练次数
total_train_step = 0
# 2. total_test_step: 记录测试的次数
total_test_step = 0
# 3. epoch:记录训练的轮数
epoch = 10
4.3 训练网络
for i in range(epoch):
print('----------第{}轮训练开始----------'.format(i+1))
# 训练步骤开始
for data in train_dataloader:
imgs, targets = data
outputs= cifar10(imgs)
loss = loss_fn(outputs, targets)
# 调用优化器通过计算出的损失进行优化
optim.zero_grad()
loss.backward()
optim.step()
total_train_step += 1
print('训练次数是:{},loss:{}'.format(total_train_step, loss.item))
4.4检验模型是否训练好或者是否达到需求
首先在训练集上要去除梯度,定义整体的测试loss和准确性,通过调用output的 argmax(1) 属性来检验正确性,代码如下:
# 为了检验模型是否训练达标,需要每训练一次都验证一下,验证时不需要调优
# 测试步骤开始
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): # 表示没有梯度(因为梯度是用来调优的,这里不需要
for data in test_dataloader:
imgs, targets = data
outputs = cifar10(imgs)
loss = loss_fn(outputs, targets)
# 需要的是整个数据集的Loss
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_accuracy', total_accuracy/test_data_size, total_test_step)
writer.add_scalar("test_loss", total_test_loss, total_test_step)
total_test_loss += 1
# 保存每一轮训练的模型
torch.save(cifar10, 'cifar10_{}.pth'.format(i))
print('模型已保存')
4.5 添加tensorboard来实时查看loss
writer = SummaryWriter('logs_train')
writer.add_scalar("train_loss", loss.item(), total_train_step)
...
整体的训练代码:
import torch.optim
from torch import nn
from torch.utils.data import DataLoader
import torchvision.datasets
from torch.utils.tensorboard import SummaryWriter
# 准备数据集
train_data = torchvision.datasets.CIFAR10(root='./new_dataset', train=True, transform=torchvision.transforms.ToTensor(), download=True)
# 准备测试集
test_data = torchvision.datasets.CIFAR10(root='./new_dataset', train=False, transform=torchvision.transforms.ToTensor(), download=True)
# 查看训练/测试数据集的大小(有多少张图片
train_data_size = len(train_data)
test_data_size = len(test_data)
# 利用dataloader加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 搭建神经网络
class CIFAR10(nn.Module):
def __init__(self):
super(CIFAR10, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2,),
nn.Conv2d(32, 32, 4, 1 ,2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 以上为整个网络模型,一般是单独放在一个.py文件里的,并在里面测试网络的正确性
# 初始化网络
cifar10 = CIFAR10()
# 创建损失函数 (因为网络处理的是分类问题,所以可以使用交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()
# 定义优化器
learning_rate = 0.01
optim = torch.optim.SGD(cifar10.parameters(), lr=learning_rate)
# 设置训练网络的参数
# 1. total_train_step:记录训练次数
total_train_step = 0
# 2. total_test_step: 记录测试的次数
total_test_step = 0
# 3. epoch:记录训练的轮数
epoch = 10
# 添加tensorboard
writer = SummaryWriter('logs_train')
for i in range(epoch):
print('----------第{}轮训练开始----------'.format(i+1))
# 训练步骤开始
for data in train_dataloader:
imgs, targets = data
outputs= cifar10(imgs)
loss = loss_fn(outputs, targets)
# 调用优化器通过计算出的损失进行优化
optim.zero_grad()
loss.backward()
optim.step()
total_train_step += 1
# 由于数据太多了,所以只打印逢100的损失
if total_train_step % 100 == 0:
print('训练次数是:{},loss:{}'.format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 为了检验模型是否训练达标,需要每训练一次都验证一下,验证时不需要调优
# 测试步骤开始
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): # 表示没有梯度(因为梯度是用来调优的,这里不需要
for data in test_dataloader:
imgs, targets = data
outputs = cifar10(imgs)
loss = loss_fn(outputs, targets)
# 需要的是整个数据集的Loss
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_accuracy', total_accuracy/test_data_size, total_test_step)
writer.add_scalar("test_loss", total_test_loss, total_test_step)
total_test_loss += 1
# 保存每一轮训练的模型
torch.save(cifar10, 'cifar10_{}.pth'.format(i))
print('模型已保存')
writer.close()
(五)使用GPU训练
方法一:对网络模型、数据(输入和标定)和损失函数进行 ' .cuda '操作,具体看自己电脑上的P30_train_GPU1
方法二:device = torch.device('cuda')
然后同样的进行‘. to(device)’ 操作
(六)完整的模型验证(测试、demo)套路
利用已经训练好的模型,然后给他提供输入。
1.首先网络模型中的图片是32 * 32 大小的,所以要先将图片进行reshape。
2.另外,如果图片是png格式的(png格式是4通道的,RGB和一个透明度通道),要调用下行代码保留其颜色通道,除去透明度通道。
image = image.convert('RGB')
3.搭建神经网络模型,并加载模型
4.这里测试的是胰脏图片,所以batch_size = 1
image = torch.reshape(image, (1, 3, 32, 32))
5.最后有两行很重要的代码,model.eval()表示是用来测试的;with torch.no_grad():目的是将梯度置零(因为我们是测试模型,并不进行调优),这样做还可以节省空间
6.最后就将图片放入网络,再打印其输出就能查看其准确性了
output = model(image)
整体代码:
import torch
import torchvision.transforms
from PIL import Image
from torch import nn
img_path = 'dog.png'
image = Image.open(img_path) # 将图片读取成PIL类型
# 因为网络模型中的图片大小是32 * 32 ,所以要将图片大小进行调整
image = image.convert('RGB') # 由于图片是png形式的,有四个通道,需要调用此行代码进行保留颜色通道除去透明度通道
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
torchvision.transforms.ToTensor()])
image = transform(image)
# 搭建神经网络
class CIFAR10(nn.Module):
def __init__(self):
super(CIFAR10, self).__init__()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2,),
nn.Conv2d(32, 32, 4, 1 ,2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model(x)
return x
# 加载网络模型
model = torch.load('cifar10_0.pth')
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)) # 打印输出对应的类别(通过调试可以看到类别对应的target)