摘要
学习率的设置也是训练好模型的关键点之一,所以需要掌握常用的学习率的设置,一种是使用自带函数,另一种是自己设置每个阶段的学习率作为调整,
模型保存方式
在学习率调整的时候你是没有办法直接感觉多少epoch能训练出来,所以保存模型的时候最好是可以继续训练的形式,例如我在训练102分类的时候acc达到93的时候基本很难再增进了这时候就要在考虑一下学习率的设置,开始的时候全程0.001,所以这时候将学习率改为0.0001会有更好的效果。所以保存可以中断训练继续的保存很重要
net = resnet50.Resnet().cuda()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(),lr=0.001)
checkpoint = torch.load(PATH)
net.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
#这部分是加载,只有训练一次之后才有的模型可以这样加载,对应保存部分
torch.save({'epoch':epoch,
'model_state_dict': net.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss
},PATH)
#这部分代码写在每个训练当中进行保存,
自带函数调整
import torch
import torch.optim as optim
from torch.optim import lr_scheduler
from torchvision.models import AlexNet
import matplotlib.pyplot as plt
model = AlexNet(num_classes=2)
optimizer = optim.SGD(params=model.parameters(), lr=0.05)
# lr_scheduler.StepLR()
# Assuming optimizer uses lr = 0.05 for all groups
# lr = 0.05 if epoch < 30
# lr = 0.005 if 30 <= epoch < 60
# lr = 0.0005 if 60 <= epoch < 90
scheduler = lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
plt.figure()
x = list(range(100))
y = []
for epoch in range(100):
scheduler.step()
lr = scheduler.get_lr()
print(epoch, scheduler.get_lr()[0])
y.append(scheduler.get_lr()[0])
plt.plot(x, y)
plt.show()
函数的重点使用注意二个点就可以,step_size的设置就是每个多少个epoch变动一次,变动的大小幅度是乘gamma。在使用自带的学习率规则调整只需要在训练的批次当中加上lrScheduler.step()进行调成即可,具体如下
lrScheduler = optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.95)
for epoch in range(pre_epoch, EPOCH):
for i, data in enumerate(trainloader):
...
lrScheduler.step()
...
自定义学习率
train_cfg = dict(
warmup=5,
lr=[0.004, 0.002, 0.0004, 0.00004, 0.000004],
step_lr=dict(
COCO=[90, 110, 130, 150, 160],
),
)
def adjust_learning_rate(optimizer, epoch, train_cfg):
global lr
# 在warmup 训练期间,学习率先行增大到初始学习率lr[0] = 0.04
if epoch <= train_cfg.warmup:
lr = 0.004*epoch*2
# 在warmup 之后,学习率按照设置的lr进行衰减,也可以自行设置指数衰减的形式
else:
for i in range(len(train_cfg.step_lr.COCO)):
if train_cfg.step_lr.COCO[i] >= epoch:
lr = train_cfg.lr[i]
break
# lr = train_cfg.init_lr * (gamma ** (step_index))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
在训练模型中一个比较好的点就是学习率预热,先从小的学习率开始慢慢增大,训练数据更加平稳,具体使用如下
optimizer = optim.Adam(net.parameters(),lr=0.004)
for epoch in range(pre_epoch, EPOCH):
for i, data in enumerate(trainloader):
...
adjust_learning_rate(optimizer,epoch,train_cfg)
...
总结
学习率的变化最好是结合训练情况及时调整,整体流程就是开始学习率预热,中间调整lr最大为0.05左右,到acc提升到末尾的时候,可以将lr在设置到0.00005左右的大小。
其他重要细节点
在训练的时候有几个比较重要的点,就是训练集分为训练集和验证集的时候,最好是保持数据的类别比例分开,将训练集和验证集分为9:1的比例比较协调,在其中要保证每个类别的比例基本没有太大变化。比如猫狗比例3:7,那么验证集的提取比例也应为3:7。
预训练权重也是可以极大的加快训练速度,大部分数据集都可以使用预训练权重。使用预训练权重有个最要要的细节是transform.normalize的使用
针对预训练权重初始化训练
大部分的网络预训练都是在1000分类的图像训练,所以transforms.Normalize的设置是不同的,这种归一化可以使得数据更加稳定,训练的效果更好,使得自己的数据集均值和标准差符合imagenet的分布,是预训练权重效果完全展现,所以,只要使用了预训练权重就要设置transforms.Normalize
if 'coco' in args.dataset:
mean_vals = [0.471, 0.448, 0.408]
std_vals = [0.234, 0.239, 0.242]
elif 'imagenet' in args.dataset:
mean_vals = [0.485, 0.456, 0.406]
std_vals = [0.229, 0.224, 0.225]
对不同的预训练过的模型设置值,在没有预训练权重的时候需要自己去计算mean和std的值,代码如下
import torchvision.transforms as transforms
import torch.nn as nn
import numpy as np
import pandas as pd
import torch.optim as optim
from torch.utils.data.sampler import SubsetRandomSampler
from efficientnet_pytorch import EfficientNet
from PIL import Image
from tqdm import tqdm
import torch.nn.functional as F
import torch
import random
from torch.autograd import Variable
from torch.utils.data import Dataset, DataLoader
train_df = pd.read_csv('./54_data/train.csv')
train_df['filename'] = train_df['filename'].apply(lambda x: './54_data/train/{0}'.format(x))
class MyDataset(Dataset):
def __init__(self, df, transform):
self.df = df
self.transform = transform
def __getitem__(self, index):
img = Image.open(self.df['filename'].iloc[index]).convert('RGB')
img = self.transform(img)
return img, torch.from_numpy(np.array(self.df['label'].iloc[index]))
def __len__(self):
return len(self.df)
train_transform = transforms.Compose([
transforms.Resize([224, 224]),
transforms.ToTensor(),
])
train_data = MyDataset(train_df, train_transform)
dataloader = DataLoader(train_data, batch_size=10, shuffle=True)
mean = torch.zeros(3)
std = torch.zeros(3)
print('==> Computing mean and std..')
for inputs, targets in dataloader:
for i in range(3):
# print(inputs)
mean[i] += inputs[:, i, :, :].mean()
std[i] += inputs[:, i, :, :].std()
mean.div_(len(dataloader))
std.div_(len(dataloader))
print(mean)
print(std)
计算出来之后再去设置自己的mean和std的值,具体如下
tf = transforms.Compose([
lambda x: Image.open(x).convert('RGB'), # string path= > image data
transforms.Resize((int(self.resize * 1.25), int(self.resize * 1.25))),
transforms.RandomRotation(15),
transforms.CenterCrop(self.resize),
transforms.ToTensor(),
transforms.Normalize(mean=[0.436424, 0.433079, 0.330037],
std=[0.252266, 0.247213, 0.250690])
])
效果图
这是102类别的分类,数据集5735张用efficientnet-b0的效果,用上归一化效果和速度方面直接暴涨,测试集基本也在96-97左右,基本已经达到最大训练效果了。