深度学习之优化模型(数据预处理,数据增强,调整学习率)
一、模型的准备这次我们使用的数据集是一共有20种的食物图片其中各种食物文件夹中食物图片现在我们对这个文件生成对应的train.txt和test.txt功能创建训练集/测试集的标签文件 参数 root数据集根目录 dir子目录名 import os#导入操作系统模块用于处理文件和路径 def train_test_file(root,dir): file_txt open(dir.txt,w)#创建txt文件w表示写入会覆盖原有内容 pathos.path.join(root,dir)#拼接完整路径 for roots,directories,files in os.walk(path):#遍历目录树os.walk返回三个值当前目录路径子目录列表文件列表 if len(directories) !0:#如果第一层有子目录记录所有类别名 dirsdirectories else: now_dirroots.split(\\)#到达图片文件所在层最底层分割路径获取当前类别文件夹名用反斜杠分割路径 for file in files:#拼接图片完整路径 path_1os.path.join(roots,file) print(path_1)#打印路径调试能用到 file_txt.write(path_1 str(dirs.index(now_dir[-1]))\n)#写入txt文件 #dirs.indexnow_dir[-1]:获取当前类别的数字标签也就是类别 file_txt.close() rootrD:\filedata\food_dataset#数据集根目录 train_dirtrain#训练集文件夹名 test_dirtest#测试集文件夹名 train_test_file(root,train_dir)#生成训练集标签文件train.txt train_test_file(root,test_dir)#生成测试集文件训练集文件二、数据预处理和数据增强数据预处理就是对所有数据进行数据增强标准化归一化去噪等操作而其中的数据增强指增加数据多样性是对数据裁剪大小变换旋转亮度调整等操作。数据增强是数据预处理的一部分数据增强专门用来产生一些新的数据。这些操作能让机器学会一张图片的几种呈现状态而不是只认识原图这样模型在现实中遇到各种情况的图片时都能正确识别。数据预处理不宜少也不宜多不做数据预处理模型就会比较死板也就是泛化能力差。过度的数据增强会让模型过拟合所以需要自己调整添加适量的预处理。预处理特别是Resize和ToTensor能省很多麻烦不然图片大小不一、格式不对模型根本没法训练。class USE_getitem(): def __init__(self,text): self.texttext def __getitem__(self, index): resultself.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(p0.5),#50%概率水平翻转 transforms.ColorJitter(brightness0.1,contrast0.1),#调整亮度#,saturation0.1,hue0.1 transforms.ToTensor(),#转成张量 ]), valid: transforms.Compose([ transforms.Resize([256,256]),#测试时就直接缩放到256x256 transforms.ToTensor(),#转成张量 ]), } #数据集读取食物图片 class food_dataset(Dataset): def __init__(self,file_path,transformNone):#从train.txt或test.txt读取图片路径和标签 self.file_pathfile_path self.imgs[] self.labels[] self.transformtransform 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就处理返回图片和标签 imageImage.open(self.imgs[idx]) if self.transform: imageself.transform(image) labelself.labels[idx] labelint(label) labeltorch.from_numpy(np.array(label,dtypenp.int64)) return image,label #数据加载 training_datafood_dataset(file_pathr.\train.txt,transformdata_transforms[train]) test_datafood_dataset(file_pathr.\test.txt,transformdata_transforms[valid]) train_dataloaderDataLoader(training_data, batch_size64,shuffleTrue) test_dataloaderDataLoader(test_data, batch_size64,shuffleTrue) #自定义cnn模型 class CNN(nn.Module): def __init__(self): super(CNN,self).__init__() self.conv1nn.Sequential( nn.Conv2d( in_channels3, out_channels16, kernel_size5, stride1, padding2, ), nn.ReLU(), nn.MaxPool2d(kernel_size2), ) self.conv2nn.Sequential( nn.Conv2d(16,32,5,1,2), nn.ReLU(), nn.Conv2d(32,32,5,1,2), nn.ReLU(), nn.MaxPool2d(2), ) self.conv3nn.Sequential( nn.Conv2d(32,128,5,1,2), nn.ReLU(), ) self.dropoutnn.Dropout(0.3) self.outnn.Linear(128*64*64,20)#三层卷积层最后输出20个类别 def forward(self,x):#前向传播卷积-展平-全连接 xself.conv1(x) xself.conv2(x) xself.conv3(x) xx.view(x.size(0),-1) outputself.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_num1#统计训练的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 %100: print(floss:{loss_value:7f} [number:{batch_size_num}]) batch_size_num 1 #测试函数 def test (dataloader,model,loss_fn): sizelen(dataloader.dataset) num_batchslen(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(fTest result: \n Accuracy :{(accuracy)}%,Avg loss:{test_loss}) return accuracy #损失函数 loss_fnnn.CrossEntropyLoss() #优化器 # optimizer torch.optim.SGD(model.parameters(),lr0.001)#尝试不同的值可以确保最后的准确率 optimizer torch.optim.Adam(model.parameters(), lr0.0001) #训练循环 best_acc 0 epochs50 for t in range(epochs): print(fEpoch{t1}\n------) train(train_dataloader,model,loss_fn,optimizer) current_acctest(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优化器中学习率值也就是这里的lr0.0001可以尝试其他值。太大正确率波动大可能错过最优点太小训练慢可能卡在局部最优点2epoch值是训练循环次数。次数少模型可能没学够正确率低太多可能过拟合训练集正确率高测试集反而低。四、调整学习率调度器1.固定步长调度器StepLR在优化器后边加上有时候这个导入可能是呈灰色的可能是编码工具的原因代码是能运行的。from torch.optim.lr_scheduler import StepLR schedulertorch.optim.lr_scheduler.StepLR(optimizer,step_size10,gamma0.5)在训练循环中if current_acc best_acc:前面加上三行如下best_acc 0 epochs50 for t in range(epochs): print(fEpoch{t1}\n------) train(train_dataloader,model,loss_fn,optimizer) current_acctest(test_dataloader, model, loss_fn) scheduler.step() current_lroptimizer.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调度器也是在优化器后边加上from torch.optim.lr_scheduler import ReduceLROnPlateau # scheduler ReduceLROnPlateau(optimizer, patience5, factor0.1) #这样写我们需要修改训练函数让他有个返回值比较麻烦 scheduler ReduceLROnPlateau(optimizer, modemax, patience5, factor0.1, verboseTrue) #这样就不需要修改训练函数了 # modemax 表示监控指标越大越好如准确率如果监控 loss 就用 modemin #这里用了max我们后边就监控准确率用min就监控损失函数 #监控的指标不同结果也是不同的不过都是为了增加模型准确率既然是更高准确率那用max就更直接和上一个调度器不一样的是这里我们需要填一个参数根据上面所述我们使用了max就填入准确率scheduler.step(current_acc)调度器的选择也需要我们自己去尝试哪个更合适模型
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2418470.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!