深度学习之优化模型(数据预处理,数据增强,调整学习率)

一、模型的准备

这次我们使用的数据集是一共有20种的食物图片

其中各种食物文件夹中食物图片

现在我们对这个文件生成对应的train.txt和test.txt

python 复制代码
'''功能:创建训练集/测试集的标签文件
参数:
root:数据集根目录
dir:子目录名
'''
import os#导入操作系统模块,用于处理文件和路径
def train_test_file(root,dir):
    file_txt = open(dir+'.txt','w')#创建txt文件,w表示写入,会覆盖原有内容
    path=os.path.join(root,dir)#拼接完整路径
    for roots,directories,files in os.walk(path):#遍历目录树,os.walk返回三个值,当前目录路径,子目录列表,文件列表
        if len(directories) !=0:#如果第一层有子目录,记录所有类别名
            dirs=directories
        else:
            now_dir=roots.split('\\')#到达图片文件所在层,最底层,分割路径获取当前类别文件夹名,用反斜杠分割路径
            for file in files:#拼接图片完整路径
                path_1=os.path.join(roots,file)
                print(path_1)#打印路径(调试能用到)
                file_txt.write(path_1+' '+str(dirs.index(now_dir[-1]))+'\n')#写入txt文件
                #dirs.index(now_dir[-1]):获取当前类别的数字标签,也就是类别
    file_txt.close()
root=r"D:\filedata\food_dataset"#数据集根目录
train_dir='train'#训练集文件夹名
test_dir='test'#测试集文件夹名
train_test_file(root,train_dir)#生成训练集标签文件train.txt
train_test_file(root,test_dir)#生成测试集文件

训练集文件

二、数据预处理和数据增强

数据预处理就是对所有数据进行数据增强,标准化,归一化,去噪等操作,而其中的数据增强指增加数据多样性,是对数据裁剪,大小变换,旋转,亮度调整等操作。

数据增强是数据预处理的一部分,数据增强专门用来产生一些新的数据。

这些操作能让机器学会一张图片的几种呈现状态而不是只认识原图,这样模型在现实中遇到各种情况的图片时,都能正确识别。

数据预处理不宜少也不宜多,不做数据预处理,模型就会比较死板,也就是泛化能力差。过度的数据增强会让模型过拟合,所以需要自己调整添加适量的预处理。

预处理(特别是 ResizeToTensor)能省很多麻烦,不然图片大小不一、格式不对,模型根本没法训练。

python 复制代码
class USE_getitem():
    def __init__(self,text):
        self.text=text
    def __getitem__(self, index):
        result=self.text[index].upper()
        return result
    def __len__(self):
        return len(self.text)
p= USE_getitem("pytorch")
print(p[0],p[1])
print(len(p))
#让对象能像列表一样用下标访问和获取长度

import torch
from torch.utils.data import Dataset,DataLoader
import numpy as np
from PIL import Image
from torchvision import transforms
import torch.nn as nn
#导入会用到的库


'''数据预处理'''
data_transforms={
    'train':
        transforms.Compose([#数据增强
            transforms.Resize([280,280]),#先把图片缩放到280x280
            transforms.RandomCrop(256),#随机裁剪到256x256
            transforms.RandomHorizontalFlip(p=0.5),#50%概率水平翻转
            transforms.ColorJitter(brightness=0.1,contrast=0.1),#调整亮度#,saturation=0.1,hue=0.1
            transforms.ToTensor(),#转成张量
    ]),
    'valid':
        transforms.Compose([
            transforms.Resize([256,256]),#测试时就直接缩放到256x256
            transforms.ToTensor(),#转成张量
    ]),
}

#数据集,读取食物图片
class food_dataset(Dataset):
    def __init__(self,file_path,transform=None):#从train.txt或test.txt读取图片路径和标签
        self.file_path=file_path
        self.imgs=[]
        self.labels=[]
        self.transform=transform
        with open(self.file_path) as f :
            samples=[x.strip().split() for x in f.readlines()]
            for img_path,label in samples:
                self.imgs.append(img_path)
                self.labels.append(label)

    def __len__(self):#返回数据集大小
        return len(self.imgs)
    def __getitem__(self, idx):#读取图片,如果有transform就处理,返回图片和标签
        image=Image.open(self.imgs[idx])
        if self.transform:
            image=self.transform(image)

        label=self.labels[idx]
        label=int(label)
        label=torch.from_numpy(np.array(label,dtype=np.int64))
        return image,label

#数据加载
training_data=food_dataset(file_path=r'.\train.txt',transform=data_transforms['train'])
test_data=food_dataset(file_path=r'.\test.txt',transform=data_transforms['valid'])
train_dataloader=DataLoader(training_data, batch_size=64,shuffle=True)
test_dataloader=DataLoader(test_data, batch_size=64,shuffle=True)

#自定义cnn模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
        self.conv1=nn.Sequential(
            nn.Conv2d(
                in_channels=3,
                out_channels=16,
                kernel_size=5,
                stride=1,
                padding=2,
            ),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
        )
        self.conv2=nn.Sequential(
            nn.Conv2d(16,32,5,1,2),
            nn.ReLU(),
            nn.Conv2d(32,32,5,1,2),

            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.conv3=nn.Sequential(
            nn.Conv2d(32,128,5,1,2),

            nn.ReLU(),
        )
        self.dropout=nn.Dropout(0.3)
        self.out=nn.Linear(128*64*64,20)#三层卷积层,最后输出20个类别

    def forward(self,x):#前向传播:卷积->展平->全连接
        x=self.conv1(x)
        x=self.conv2(x)
        x=self.conv3(x)
        x=x.view(x.size(0),-1)
        output=self.out(x)
        return output

#设备设置和模型初始化
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else 'cpu'
model = CNN().to(device)


#训练函数
def train(dataloader,model,loss_fn,optimizer):
    model.train()
    batch_size_num=1#统计训练的batch数量

    for X,y in dataloader:
        X,y = X.to(device),y.to(device)  # 把训练数据集和标签传入cpu或GPU
        pred = model.forward(X)#前向计算
        loss = loss_fn(pred,y)#计算损失
        optimizer.zero_grad()#梯度清零
        loss.backward()#反向传播
        optimizer.step()#更新参数

        loss_value = loss.item()
        # if batch_size_num %10==0:
        print(f"loss:{loss_value:>7f} [number:{batch_size_num}]")
        batch_size_num +=1


#测试函数
def test (dataloader,model,loss_fn):
    size=len(dataloader.dataset)
    num_batchs=len(dataloader)
    model.eval()
    test_loss, correct = 0,0
    with torch.no_grad():
        for X,y in dataloader:
            X,y = X.to(device),y.to(device)
            pred = model.forward(X)
            test_loss += loss_fn(pred,y).item()  # test_loss是会自动累加每一个批次的损失值
            correct +=(pred.argmax(1)== y).type(torch.float).sum().item() # 标量

    test_loss /= num_batchs
    correct /= size
    accuracy = 100*correct
    print(f"Test result: \n Accuracy :{(accuracy)}%,Avg loss:{test_loss}")
    return accuracy

#损失函数
loss_fn=nn.CrossEntropyLoss()

#优化器
# optimizer = torch.optim.SGD(model.parameters(),lr=0.001)#尝试不同的值可以确保最后的准确率
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)

#训练循环
best_acc = 0
epochs=50
for t in range(epochs):
    print(f"Epoch{t+1}\n------")
    train(train_dataloader,model,loss_fn,optimizer)
    current_acc=test(test_dataloader, model, loss_fn)

    if current_acc > best_acc:
        best_acc = current_acc
        torch.save(model.state_dict(),'best_model.pth')
        print(f'最佳模型:准确率{best_acc:.2f}%')
print("Dnoe!")
print(f'最佳模型:准确率{best_acc:.2f}%')

结果:

此外有两个小点也会影响准确率,可以微调模型。

1)优化器中学习率值,也就是这里的lr=0.0001,可以尝试其他值。太大正确率波动大,可能错过最优点;太小训练慢,可能卡在局部最优点

2)epoch值,是训练循环次数。次数少模型可能没学够,正确率低;太多可能过拟合,训练集正确率高,测试集反而低。

四、调整学习率(调度器)

1.固定步长调度器StepLR

在优化器后边加上:

有时候这个导入可能是呈灰色的,可能是编码工具的原因,代码是能运行的。

python 复制代码
from torch.optim.lr_scheduler import StepLR
scheduler=torch.optim.lr_scheduler.StepLR(optimizer,step_size=10,gamma=0.5)

在训练循环中if current_acc > best_acc:前面加上三行,如下:

python 复制代码
best_acc = 0
epochs=50
for t in range(epochs):
    print(f"Epoch{t+1}\n------")
    train(train_dataloader,model,loss_fn,optimizer)
    current_acc=test(test_dataloader, model, loss_fn)

    scheduler.step()
    current_lr=optimizer.param_groups[0]['lr']
    print(f'当前学习率:{current_lr:.6f}')

    if current_acc > best_acc:
        best_acc = current_acc
        torch.save(model.state_dict(),'best_model.pth')
        print(f'最佳模型:准确率{best_acc:.2f}%')
print("Dnoe!")
print(f'最佳模型:准确率{best_acc:.2f}%')

这里学习率没有提升,可以修改里面的参数进行对比,但有时候可能是模型已经收敛,所以调度器对它影响不大,需要知道的是调度器也是优化模型的一种方法。

2.ReduceLROnPlateau调度器

也是在优化器后边加上:

python 复制代码
from torch.optim.lr_scheduler import ReduceLROnPlateau

# scheduler = ReduceLROnPlateau(optimizer, patience=5, factor=0.1)
#这样写我们需要修改训练函数,让他有个返回值,比较麻烦

scheduler = ReduceLROnPlateau(optimizer, mode='max', patience=5, factor=0.1, verbose=True)
#这样就不需要修改训练函数了

# mode='max' 表示监控指标越大越好(如准确率),如果监控 loss 就用 mode='min'
#这里用了max我们后边就监控准确率,用min就监控损失函数
#监控的指标不同,结果也是不同的不过都是为了增加模型准确率,既然是更高准确率,那用max就更直接

和上一个调度器不一样的是这里我们需要填一个参数,根据上面所述,我们使用了max就填入准确率

python 复制代码
scheduler.step(current_acc)

调度器的选择也需要我们自己去尝试,哪个更合适模型

相关推荐
昵称已被吞噬~‘(*@﹏@*)’~14 小时前
【RL+空战】学习记录03:基于JSBSim构造简易空空导弹模型,并结合python接口调用测试
开发语言·人工智能·python·学习·深度强化学习·jsbsim·空战
我想我不够好。14 小时前
学到的知识点 1.8
学习
旖旎夜光15 小时前
Linux(9)
linux·学习
浩瀚地学16 小时前
【Java】常用API(二)
java·开发语言·经验分享·笔记·学习
棒棒的皮皮16 小时前
【深度学习】YOLO模型速度优化Checklist
人工智能·深度学习·yolo·计算机视觉
chao_66666616 小时前
解决 PowerShell 中文乱码问题
网络·学习·powershell
喵了meme16 小时前
Linux学习日记24:Linux网络编程基础
linux·网络·学习
BullSmall16 小时前
《庄子》导读
学习
HL_风神16 小时前
设计原则之迪米特
c++·学习·设计模式