网站开发主页,上海网站制作建设多少钱,做体育最好的网站,环保网站建设说明书学习率调整得当将有助于算法快速收敛和获取全局最优#xff0c;以获得更好的性能。本文对学习率调度器进行示例介绍。  学习率调整的意义基础示例无学习率调整方法学习率调整方法一多因子调度器余弦调度器 结论 学习率调整的意义 
首先#xff0c;学习率的大小很重要。如果它…学习率调整得当将有助于算法快速收敛和获取全局最优以获得更好的性能。本文对学习率调度器进行示例介绍。  学习率调整的意义基础示例无学习率调整方法学习率调整方法一多因子调度器余弦调度器 结论 学习率调整的意义 
首先学习率的大小很重要。如果它太大优化就会发散如果它太小训练就会需要过长时间或者我们最终只能得到次优的结果陷入局部最优。我们之前看到问题的条件数很重要。直观地说这是最不敏感与最敏感方向的变化量的比率。 
其次衰减速率同样很重要。如果学习率持续过高我们可能最终会在最小值附近弹跳从而无法达到最优解。 简而言之我们希望速率衰减但要比慢这样能成为解决凸问题的不错选择。 
另一个同样重要的方面是初始化。这既涉及参数最初的设置方式又关系到它们最初的演变方式。这被戏称为预热warmup即我们最初开始向着解决方案迈进的速度有多快。一开始的大步可能没有好处特别是因为最初的参数集是随机的。最初的更新方向可能也是毫无意义的。 
鉴于管理学习率需要很多细节因此大多数深度学习框架都有自动应对这个问题的工具。本文将梳理不同的调度策略对准确性的影响并展示如何通过学习率调度器learning rate scheduler来有效管理。 
基础示例 
我们从一个简单的问题开始这个问题可以轻松计算但足以说明要义。 为此我们选择了一个稍微现代化的LeNet版本激活函数使用relu而不是sigmoid汇聚层使用最大汇聚层而不是平均汇聚层并应用于Fashion-MNIST数据集。 此外我们混合网络以提高性能。 
无学习率调整方法 
import math
import torch
from torch import nn
from torch.optim import lr_scheduler, SGD
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torchvision.utils import make_grid
import matplotlib.pyplot as pltdef load_data_fashion_mnist(batch_size):# 定义数据预处理transform  transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))])# 加载训练集和测试集train_dataset  datasets.FashionMNIST(root./data, trainTrue, downloadTrue, transformtransform)test_dataset  datasets.FashionMNIST(root./data, trainFalse, downloadTrue, transformtransform)# 创建数据加载器train_loader  DataLoader(train_dataset, batch_sizebatch_size, shuffleTrue)test_loader  DataLoader(test_dataset, batch_sizebatch_size, shuffleFalse)return train_loader, test_loader
def net_fn():model  nn.Sequential(nn.Conv2d(1, 6, kernel_size5, padding2), nn.ReLU(),nn.MaxPool2d(kernel_size2, stride2),nn.Conv2d(6, 16, kernel_size5), nn.ReLU(),nn.MaxPool2d(kernel_size2, stride2),nn.Flatten(),nn.Linear(16 * 5 * 5, 120), nn.ReLU(),nn.Linear(120, 84), nn.ReLU(),nn.Linear(84, 10))return modeldef train(net, train_loader, test_loader, num_epochs, loss, optimizer, device, schedulerNone):net.to(device)running_loss  0.0train_losses  []test_losses  []test_accuracies  []for epoch in range(num_epochs):for i, (inputs, labels) in enumerate(train_loader):inputs, labels  inputs.to(device), labels.to(device)# Zero the parameter gradientsoptimizer.zero_grad()# Forward passoutputs  net(inputs)loss_value  loss(outputs, labels)# Backward and optimizeloss_value.backward()optimizer.step()# Print statisticsrunning_loss  loss_value.item()# if i % 200  199:  # print every 200 mini-batches#     print(f[{epoch  1}, {i  1}] loss: {running_loss / 200})#     running_loss  0.0train_losses.append(running_loss / len(train_loader))# Evaluate the model on the test datasettest_loss, test_acc  evaluate(net, test_loader, device)test_losses.append(test_loss)test_accuracies.append(test_acc)print(fEpoch {epoch1}, Train Loss: {train_losses[-1]:.4f}, Test Loss: {test_losses[-1]:.4f}, Test Acc: {test_accuracies[-1]:.2f})if scheduler:if scheduler.__module__  lr_scheduler.__name__:scheduler.step()else:for param_group in  optimizer.param_groups:param_group[lr]  scheduler(epoch)plt.figure(figsize(10, 6))plt.plot(range(1, num_epochs  1), train_losses, labelTraining Loss)plt.plot(range(1, num_epochs  1), test_losses, labelTest Loss)plt.plot(range(1, num_epochs  1), test_accuracies, labelTest Accuracy)plt.title(Training, Test Losses and Test Accuracy)plt.xlabel(Epoch)plt.ylabel(Loss / Accuracy)plt.legend()plt.grid(True)plt.savefig(1.jpg)plt.show()def evaluate(model, data_loader, device):model.eval()test_loss  0correct  0with torch.no_grad():for inputs, labels in data_loader:inputs, labels  inputs.to(device), labels.to(device)outputs  model(inputs)test_loss  nn.CrossEntropyLoss(reductionsum)(outputs, labels).item()_, predicted  torch.max(outputs.data, 1)correct  (predicted  labels).sum().item()test_loss / len(data_loader.dataset)accuracy  correct / len(data_loader.dataset)#accuracy  100. * correct / len(data_loader.dataset)return test_loss, accuracy# Device configuration
device  torch.device(cuda if torch.cuda.is_available() else cpu)# Define the model
model  net_fn()# Define the loss function
loss  nn.CrossEntropyLoss()# Define the optimizer
lr0.3
optimizer  SGD(model.parameters(), lrlr)# Load the dataset
batch_size128
train_loader, test_loaderload_data_fashion_mnist(batch_size)
num_epochs30
train(model, train_loader, test_loader, num_epochs, loss, optimizer, device)这里没有使用学习率调整策略。训练过程和结果如下图所示 
.
.
.
.
Epoch 23, Train Loss: 0.1247, Test Loss: 0.3939, Test Acc: 0.90
Epoch 24, Train Loss: 0.1236, Test Loss: 0.4370, Test Acc: 0.89
Epoch 25, Train Loss: 0.1167, Test Loss: 0.4117, Test Acc: 0.89
Epoch 26, Train Loss: 0.1169, Test Loss: 0.4440, Test Acc: 0.89
Epoch 27, Train Loss: 0.1163, Test Loss: 0.4336, Test Acc: 0.89
Epoch 28, Train Loss: 0.1055, Test Loss: 0.4312, Test Acc: 0.90
Epoch 29, Train Loss: 0.1065, Test Loss: 0.4942, Test Acc: 0.89
Epoch 30, Train Loss: 0.1051, Test Loss: 0.4763, Test Acc: 0.89学习率调整方法一 
设置在每个迭代轮数甚至在每个小批量之后向下调整学习率。 例如以动态的方式来响应优化的进展情况。 
在代码最后添加SquareRootScheduler类并更新train函数参数其它内容不变。 
class SquareRootScheduler:def __init__(self, lr0.1):self.lr  lrdef __call__(self, num_update):return self.lr * pow(num_update  1.0, -0.5)scheduler  SquareRootScheduler(lr0.1)
train(model, train_loader, test_loader, num_epochs, loss, optimizer, device,scheduler)运行代码可得相应参数值和变化过程如下所示。 
Epoch 23, Train Loss: 0.1823, Test Loss: 0.2811, Test Acc: 0.90
Epoch 24, Train Loss: 0.1801, Test Loss: 0.2800, Test Acc: 0.90
Epoch 25, Train Loss: 0.1767, Test Loss: 0.2819, Test Acc: 0.90
Epoch 26, Train Loss: 0.1747, Test Loss: 0.2800, Test Acc: 0.91
Epoch 27, Train Loss: 0.1720, Test Loss: 0.2818, Test Acc: 0.90
Epoch 28, Train Loss: 0.1689, Test Loss: 0.2856, Test Acc: 0.90
Epoch 29, Train Loss: 0.1669, Test Loss: 0.2907, Test Acc: 0.90
Epoch 30, Train Loss: 0.1641, Test Loss: 0.2813, Test Acc: 0.90 我们可以看出曲线比没有策略时平滑了很多效果有所提升。 
多因子调度器 
多因子调度器。   代码部分修改 
scheduler lr_scheduler.MultiStepLR(optimizer, milestones[15, 30], gamma0.5)运行结果为  可见效果不理想出现过拟合现象。 
余弦调度器 
余弦调度器是 (Loshchilov and Hutter, 2016)提出的一种启发式算法。 它所依据的观点是我们可能不想在一开始就太大地降低学习率而且可能希望最终能用非常小的学习率来“改进”解决方案。 这产生了一个类似于余弦的调度函数形式如下所示学习率的值在 之间。  代码中添加CosineScheduler类和修改scheduler。 
class CosineScheduler:def __init__(self, max_update, base_lr0.01, final_lr0,warmup_steps0, warmup_begin_lr0):self.base_lr_orig  base_lrself.max_update  max_updateself.final_lr  final_lrself.warmup_steps  warmup_stepsself.warmup_begin_lr  warmup_begin_lrself.max_steps  self.max_update - self.warmup_stepsdef get_warmup_lr(self, epoch):increase  (self.base_lr_orig - self.warmup_begin_lr) \* float(epoch) / float(self.warmup_steps)return self.warmup_begin_lr  increasedef __call__(self, epoch):if epoch  self.warmup_steps:return self.get_warmup_lr(epoch)if epoch  self.max_update:self.base_lr  self.final_lr  (self.base_lr_orig - self.final_lr) * (1  math.cos(math.pi * (epoch - self.warmup_steps) / self.max_steps)) / 2return self.base_lr#scheduler  SquareRootScheduler(lr0.1)
#scheduler lr_scheduler.MultiStepLR(optimizer, milestones[15, 30], gamma0.5)
scheduler  CosineScheduler(max_update20, base_lr0.3, final_lr0.01)
train(model, train_loader, test_loader, num_epochs, loss, optimizer, device,scheduler)运行结果如下 过拟合现象消失效果提升。 
结论 
在开发时应根据自己需要选择合适的学习率调整策略。优化在深度学习中有多种用途。对于同样的训练误差而言选择不同的优化算法和学习率调度除了最大限度地减少训练时间可以导致测试集上不同的泛化和过拟合量。 
注部分内容摘选子书籍《动手学深度学习》