《深度学习》CNN 数据增强、保存最优模型 实例详解
目录
一、数据增强
1、什么是数据增强
2、目的
3、常用的数据增强方法
4、数据预处理
用法:
5、使用数据增强增加训练数据
二、保存最优模型
1、什么是保存最优模型
2、定义CNN模型
运行结果:
3、设置训练模式
4、设置测试模式、保存最优模型
5、开始训练模型
运行结果:
6、最优模型
一、数据增强
1、什么是数据增强
数据增强(data augmentation)是指通过对原始训练数据进行一系列变换和扩充,生成新的训练样本,以增加训练数据的多样性和数量,从而提升深度学习模型的泛化能力和鲁棒性。
2、目的
模拟真实世界中的不同情况和变化,使模型对各种情况都能准确识别和预测。
3、常用的数据增强方法
图像旋转:随机旋转图像一定角度,模拟不同角度观察物体的情况。
图像翻转:随机水平或垂直翻转图像,模拟不同方向观察物体的情况。
图像缩放:随机调整图像尺寸,模拟物体距离不同的情况。
图像平移:随机平移图像一定距离,模拟物体在不同位置的情况。
图像剪切:随机裁剪图像一部分,模拟物体遮挡的情况。
图像亮度、对比度、饱和度调整:随机调整图像的亮度、对比度和饱和度,模拟不同光照条件下的情况。
噪声添加:随机向图像中添加噪声,模拟真实世界中的噪声干扰。
随机扰动:随机对图像进行拉伸、扭曲等几何变换,模拟物体形状的变化。
4、数据预处理
用法:
import torch
from torch.utils.data import DataLoader,Dataset # 导入打包加载库,Dataset表示数据集的抽象概念,可以被自定义的数据集继承和实现
import numpy as np
from PIL import Image
from torchvision import transformsdata_transforms = {'train': # 训练集 也可以使用PIL库 smote 训练集transforms.Compose([ # transforms.Compose用于将多个图像预处理操作整合在一起transforms.Resize([300,300]), # 使图像变换大小transforms.RandomRotation(45), # 随机旋转,-42到45度之间随机选transforms.CenterCrop(256), # 从中心开始裁剪[256.256]transforms.RandomHorizontalFlip(p=0.5), # 随机水平旋转,随机概率为0.5transforms.RandomVerticalFlip(p=0.5), # 随机垂直旋转,随机概率0.5transforms.ColorJitter(brightness=0.2,contrast=0.1,saturation=0.1,hue=0.1), # 随机改变图像参数,参数分别表示 亮度、对比度、饱和度、色温transforms.RandomGrayscale(p=0.1), # 概率转换成灰度率,3通道就是R=G=Btransforms.ToTensor(), # 将PIL图像或NumPy ndarray转换为tensor类型,并将像素值的范围从[0, 255]缩放到[0.0, 1.0],默认把通道维度放在前面transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]) # 给定均值和标准差对图像进行标准化,前者为均值,后者为标准差,三个值表示三通道图像]),'valid': # 验证集transforms.Compose([ # 整合图像处理的操作transforms.Resize([256,256]), # 缩放图像尺寸transforms.ToTensor(), # 转换为torch类型transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]) # 标准化])
}
注意:做了数据增强不代表模型训练效果一定会变好,大概率会变好
5、使用数据增强增加训练数据
class food_dataset(Dataset): # food_dataset是自己创建的类名称,继承Dataset类def __init__(self,file_path,transform=None): # 类的初始化,解析数据文件txt,file_path表示文件路径,transform可选的图像转换操作self.file_path = file_path # 将文件地址传入self空间self.imgs = []self.labels = []self.transform = transform # 将数据增强操作传入self空间with open(self.file_path) as f: # 打开存放图片地址及其类别的文本文件train.txt,samples = [x.strip().split(' ') for x in f.readlines()] # 遍历文件里的每一条数据,经过处理后存入sample列表,元祖的形式存放for img_path,label in samples: # 遍历列表中的每个元组的每个元素self.imgs.append(img_path) # 将图像的路径存入img列表self.labels.append(label) # 将图片类别标签存入label列表
# 初始化:把图片目录加载到self.def __len__(self): # 类实例化对象后,可以使用len函数测量对象的个数return len(self.imgs) # 返回数据集中样本的总数def __getitem__(self, idx): # 关键,可通过索引idx的形式获取每一个图片数据及标签image = Image.open(self.imgs[idx]) # 使用PIL库中的用法Image打开并识别图像,还不是tensorif self.transform: # 判断是否有图像转换操作,上述定义默认为None,有则将pil图像数据转换为tensor类型image = self.transform(image) # 图像处理为256*256,转换为tenorlabel = self.labels[idx] # label还不是tensorlabel = torch.from_numpy(np.array(label,dtype=np.int64)) # 首先指定标签类型为int型,然后将其转换为numpy数组类型,然后再使用torch.from_numpy转换为torch类型return image,label # 返回处理完的图片和标签# 次数导入上述定义函数中的参数
training_data = food_dataset(file_path = './trainda.txt',transform = data_transforms['train'])
test_data = food_dataset(file_path ='./testda.txt',transform = data_transforms['valid'])
# 判断当前使用的是cpu好事gpu
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device")
其中trainda.txt文件内容为: (全篇博客使用)
testda.txt文件内容:
其中的每个文件地址都有其对应的图片,数据量较大,训练时间会较长,如需使用,可私信发送打包文件。
整篇文章所有代码连接为一份完整代码。
二、保存最优模型
1、什么是保存最优模型
保存最优模型指的是在训练过程中,根据某个指标(如验证集准确率或损失函数值)的表现,选择最好的模型参数并将其保存下来,然后形成一个文件,后缀名为pt\ppt\t7。
在深度学习中,模型的训练过程通常是通过迭代优化算法(如梯度下降)来不断调整模型的参数,以最小化目标函数(如损失函数)。在每个训练周期结束后,会使用验证集或测试集对模型进行评估,计算模型在该指标上的性能。
2、定义CNN模型
from torch import nnclass CNN(nn.Module):def __init__(self): # 翰入大小 (3,256,256)super(CNN,self).__init__()self.conv1 = nn.Sequential( # 将多个层组合成一起。nn.Conv2d( # 2d一般用于图像,3d用于视频数据(多一个时间维度),1d一般用于结构化的序in_channels=3, # 图像通道个数,1表示灰度图(确定了卷积核 组中的个数)out_channels=16, # 要得到几多少个特征图,卷积核的个数.kernel_size=5, # 卷积核大小,5*5stride=1, # 步长padding=2, # 一般希望卷积核处理后的结果大小与处理前的数据大小相同,效果会比较好。那p), # 输出的特征图为 (16,256,256)nn.ReLU(),nn.MaxPool2d(kernel_size=2), # 进行池化操作(2x2 区域),输出结果为:(16,128,128))self.conv2 = nn.Sequential(nn.Conv2d(16,32,5,1,2), # 输出(32,128,128)nn.ReLU(),nn.MaxPool2d(2) # 输出)self.conv3 = nn.Sequential(nn.Conv2d(32,128,5,1,2),nn.ReLU(),)self.out = nn.Linear(128*64*64,20) # 全连接def forward(self,x): # 前向传播x = self.conv1(x)x = self.conv2(x)x = self.conv3(x) # 输出(64,128,64,64)x = x.view(x.size(0),-1)output = self.out(x)return output # 返回输出结果model = CNN().to(device) # 将卷积神经网络模型传入GPU
print(model) # 打印当前模型的构造
运行结果:
3、设置训练模式
def train(dataloader,model,loss_fn,optimizer): # 传入参数 打包的数据,卷积模型,损失函数,优化器model.train() # 表示模型开始训练batch_size_num = 1for x,y in dataloader: # 遍历打包的图片及其对应的标签,其中batch为每一个数据的编号x,y = x.to(device), y.to(device) # 把训练数据集和标签传入cpu或GPUpred = model.forward(x) # 自动初始化 W权值loss = loss_fn(pred, y) # 传入模型训练结果的预测值和真实值,通过交叉熵损失函数计算损失值L0optimizer.zero_grad() # 梯度值清零loss.backward() # 反向传播计算得到每个参数的梯度optimizer.step() # 根据梯度更新网络参数loss = loss.item() # 获取损失值if batch_size_num % 100 == 0:print(f"loss: {loss:>7f}[number:{batch_size_num}]") # 打印损失值,右对齐,长度为7batch_size_num += 1 # 右下方传入的参数,表示训练轮数
4、设置测试模式、保存最优模型
best_acc = 0 # 初始化为0
def test(dataloader, model,loss_fn): # 定义一个test函数,用于测试模型性能global best_acc # 定义一个全局变量size = len(dataloader.dataset) # 返回打包的图片总数num_batches = len(dataloader) # 返回打包的包的个数model.eval() # 表示模型进入测试模式test_loss,correct = 0,0 # 初始化两个值,一个用来存放总体损失值,一个存放预测准确的个数with torch.no_grad(): # 一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()时可以减少for x,y in dataloader: # 遍历数据加载器中测试集图片的图片及其标签x,y = x.to(device),y.to(device) # 传入GPUpred = model.forward(x) # 前向传播,返回预测结果test_loss += loss_fn(pred,y).item() # 计算所有的损失值的和,item表示将tensor类型值转化为python标量correct += (pred.argmax(1)== y).type(torch.float).sum().item() # 判断预测的值是等于真实值,返回布尔值,将其转换为0和1,然后求和# a = (pred.argmax(1)== y) dim=1表示每一行中的最大值对应的索引号,dim=日表示每 b=(pred.argmax(1)==y).type(torch.float)test_loss /= num_batches # 总体损失值除以数据条数得到平均损失值correct /= size # 求准确率print(f"Test result:in Accuracy: {(100*correct)}%, Avg loss: {test_loss}") # 表示准确率机器对应的损失值# acc_s.append(correct)# loss_s.append(test_loss)# 保存最优模型的前2种方法,模型扩展名一般为:py\pth\t7if correct > best_acc: # 如果新训练得到的准确率大于前面已经求出来的准确率best_acc = correct # 将新的准确率传入值best_acc
# 1、保存模型参数方法:保存权重和偏置 torch.save(model.state_dict(),path) (w,b)
# print(model.state_dict().key()) # 输出模型名称 CNN
# torch.save(model.state_dict(),'best.pth')# 2、保存完整模型 保存包括模型的架构和参数(w,b,模型cnn)torch.save(model,'best.pt')loss_fn = nn.CrossEntropyLoss() # 创建交叉熵损失雨数对象,因为食物的类别是20
optimizer = torch.optim.Adam(model.parameters(),lr=0.001) # 创建一个优化器,SGD为随机梯度下降,Adam为一种自适应优化器
5、开始训练模型
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True) # 创建数据加载器,将训练集和测试集图片及其标签打包
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)epochs = 150 # 设置模型训练的轮数,不停更新模型参数,找到最优值
acc_s = [] # 初始化了两个空列表,用于存储模型在每个epoch结束时的准确率和损失值
loss_s = []
for t in range(epochs): # 遍历轮数print(f"Epoch {t+1}\n---------------------------") # 表示轮数展示train(train_dataloader,model,loss_fn,optimizer) # 调用函数train传入训练集数据加载器、初始化的模型、损失函数、优化器test(test_dataloader, model, loss_fn) # 上述训练集训练完后有了初步的模型,现传入测试集然后在对其进行测试,然后保存模型,然后进行迭代轮数,每每遇到最大准确率则重新保存新的模型
运行结果:
6、最优模型
训练结束得到当前训练的最优模型,其为pt\pth\t7文件,此时该文件即为当前模型,可直接调用该文件使用。