必备必考 | 调参技能之学习率衰减方案(一)—超多图直观对比

2019-08-16 17:52:00 浏览数 (1)

作者:pyimagesearch 原文链接:https://www.pyimagesearch.com/2019/07/22/keras-learning-rate-schedules-and-decay/ 编译:AI算法与图像处理

导读

作为一名合格的算法工程师,调参的技巧必不可少,接下来将有三篇关于如何调整学习率的文章分享,会设计到大量的图片,篇幅较长,希望大家能耐心读完,也不辜负我辛辛苦苦翻译过来,由于本人自身水平有限,如果有地方翻译的不够准确或不当还请原谅~

内容简介

在本文中,你将学习如何使用Keras实现学习率衰减的方案(Keras learning rate schedules and decay)。

你将学习如何使用Keras的标准学习率衰减以及阶梯型,线性的和多项式学习率衰减方案。

在训练神经网络时,学习率通常是需要你调整的最重要的超参数:

  • 学习率太小,可能导致你的神经网络根本无法学习
  • 学习率太大,你可能会overshoot低loss的区域(甚至从训练开始时就过拟合)

当谈到训练神经网络时,最大的收益(就准确性而言)将来自于选择正确的学习率和适当的学习率表。

但说起来容易做起来难。

为了帮助深度学习从业者(如自己)学习如何评估问题并选择合适的学习率,我们将开始一系列关于学习率衰减方案和使用Keras进行超参数调整的教程。

在本文的第一部分中,我们将讨论为什么学习率是训练神经网络时最重要的超参数。

然后将深入探讨为什么我们要在训练期间调整学习率。

这里我将展示如何使用keras实现和利用一些学习率表。包括:

  • 大多数keras优化器都内置了学习率衰减方案
  • 阶梯型(step-based)衰减的学习率方案
  • 线性学习率衰减方案
  • 多项式学习率方案

我们将使用这些学习率方案在CIFAR-10上进行多个实验,并评估哪一个表现的最好。

这些实验组将作为你在探索自己的深度学习项目和选择适当的学习率和学习率方案时可以使用的模板。

为什么要调整我们的学习率并使用学习率方案

要了解为什么学习率方案是一个有价值的方法,可用于提高模型的准确率并降低loss,考虑到几乎所有神经网络使用的标准权重更新公式:

回想一下,学习率 alpha,控制着我们梯度改变的步长(step)。更大的alpha值意味着更大的步长。如果alpha为0,则网络无法执行任何步骤(因为梯度乘以0为0).

你遇到的大多数初始学习率都是下面的集合中:

然后,在不改变学习率的条件下,网络训练固定数量的epoch。

这种方法在某些情况下可能效果很好,但是随着时间的推移降低学习率往往是有益的。在训练网络时,我们试图在损失曲面(loss landscape)中找到一些位置(location),网络可以获得合理的准确率。它不一定是全局最小值,甚至不是局部最小值,但在实践中,仅仅找到一个具有相当小的loss landscape区域就是“足够好”。

如果我们不断保持高学习率,我们可能会overshoot这些低loss的区域,因此我们将采取一系列步骤进入低loss的区域。

相反,我们可以做的是降低我们的学习率,从而允许我们的网络采取更小的步长——这种降低的学习率使我们的网络能够下降到“更优化”的loss landscape区域,否则用我们的学习率将完全错过。

因此,我们可以将学习率调整的过程视为:

  1. 在训练过程的早期以较高的学习率找到一组合理的“好的”权重。
  2. 在后续过程中调整这些权重,以使用较小的学习率找到更优的权重。

我们将在本文中介绍一些最受欢迎的学习率方案。

项目文件结构

公众号后台回复“学习率1”,获取本文所需的所有程序和文件

下载附件并使用tree命令查看项目结构:

代码语言:javascript复制
$
 tree
.
├── output
│   ├── lr_linear_schedule.png
│   ├── lr_poly_schedule.png
│   ├── lr_step_schedule.png
│   ├── train_linear_schedule.png
│   ├── train_no_schedule.png
│   ├── train_poly_schedule.png
│   ├── train_standard_schedule.png
│   └── train_step_schedule.png
├── pyimagesearch
│   ├── __init__.py
│   ├── learning_rate_schedulers.py
│   └── resnet.py
└── train.py
 
2 directories, 12 files

output文件夹下包含学习率和训练历史图。结果中包含的五个实验分别对应于具有tran_*.png文件名的五个图。

pyimagesearch 模块包含了我们的ResNet CNN和learning_rate_schedulers.py。LearningRateDecay父类只包含一个叫plot的方法,用于绘制每种类型的学习率衰减图。还包含子类,StepDecay 和 PolynomialDecay,它们为计算每个周期(epoch)完成时的学习率。这两个类都包含通过继承的绘图方法(面向对象的概念)。

我们的训练代码train.py将在CIFAR-10数据集上训练ResNet。我们将在没有学习速率衰减以及含标准,线性,阶梯型(step-based)和多项式学习率衰减的情况下分别运行代码。

keras中标准的衰减方案(The standard “decay” schedule in Keras)

keras库附带一个基于时间的学习率方案——它通过优化器类的decay参数进行控制(例如SGD,Adam等等)。

为了了解我们如何利用这种类型的学习率衰减,让我们看看如何初始化ResNet架构和SGD优化器的示例:

代码语言:javascript复制
# 初始化优化器和模型,并编译
opt = SGD(lr=1e-2, momentum=0.9, decay=1e-2/epochs)
model = ResNet.build(32, 32, 3, 10, (9, 9, 9),
  (64, 64, 128, 256), reg=0.0005)
model.compile(loss="categorical_crossentropy", optimizer=opt,
  metrics=["accuracy"])

这里,初始化我们的SGD优化器,初始学习率1e-2。

然后,我们将衰减设置为学习率除以正在训练网络的周期数(一个常见的经验法则)。

在内部,Keras应用以下学习率方案来调整每个batch更新后的学习率 ——这是一个误解,认为Keras在每个epoch之后更新标准衰减。 使用Keras提供的默认学习率调度程序时请记住这一点。

更新的公式如下:

以CIFAR-10 数据集为例,我们共有50000个训练图片。

如果我们使用的batch大小是64,这意味着每个epoch共有 50000 / 64 = 782步。因此,在一个epoch完成之前,总共需要应用782次权重更新。

为了查看学习率方案计算的示例,我们假设初始学习率为α = 0.01且我们的 decay= 0.01/40(假设我们正在训练40个epoch)。

在应用任何学习率方案之前,步骤 0 的学习率为:

在一个epoch之后,我们可以看到以下的学习率:

下图1继续计算keras的标准学习率衰减 α = 0.01和 0.01/40 的decay:

图1 keras标准学习率衰减方案

你将在本文的“运行训练代码”和“keras学习率方案结果”两部分中学习利用这种类型的学习率衰减。

LearningRateDecay类(The standard “decay” schedule in Keras)

在本文的其余部分中,我们将实现自己的自定义学习率方案,然后训练我们的神经网络时将它们与Keras结合使用来。

为了保持我们的代码整洁,更不用说,遵循面向对象的编程最佳实践,让我们首先定义一个基础LearningRateDecay类,我们将为每个相应的学习率方案定义子类。

打开目录结构中的learning_rate_schedulers.py并插入以下代码:

代码语言:javascript复制
# 导入必要的包
import matplotlib.pyplot as plt
import numpy as np
 
class LearningRateDecay:
  def plot(self, epochs, title="Learning Rate Schedule"):
    # 为每个相应的周期计算一系列的学习率
    lrs = [self(i) for i in epochs]
 
    # 学习率方案
    plt.style.use("ggplot")
    plt.figure()
    plt.plot(epochs, lrs)
    plt.title(title)
    plt.xlabel("Epoch #")
    plt.ylabel("Learning Rate")

我们应用的每个学习率方案都将具有绘图功能,使我们能够随时查看我们的学习率。

通过我们的基础LearningRateSchedule类的应用,让我们继续创建阶梯型(或者叫基于步长)的学习率方案。

keras中阶梯型的学习率方案(Step-based learning rate schedules with Keras)

图2 Keras学习率基于步骤的衰减。 红色的时间方案是0.5的衰减因子,蓝色是0.25的衰减因子。

一种普遍的学习率方案是阶梯型(step-based)的衰减,其中我们在训练期间的特定epoch之后系统地降低学习率。

阶梯型(step-based)衰减学习率方案可以看作是分段函数,如图2所示 —— 这里学习率对于多个epoch是恒定的,然后是下降,再次是恒定的,然后再次下降,等等。

将阶梯型(step-based)衰减应用于我们的学习率时,我们有两种选择:

  • 定义一个方程,用来模拟我们希望实现的分段下降学习率。
  • 使用ctrl c方法来训练深度神经网络。在这里,我们以给定一个学习率训练一些epoch,并最终注意到验证性能停滞,然后ctrl c停止程序,调整我们的学习率,并继续训练。

我们将主要关注本文中基于方程式的分段下降学习率设置。

ctrl c方法稍微高级一些,通常使用更深的神经网络应用于更大的数据集,其中获得合理模型所需的确切epoch数是未知的。

当应用阶梯型衰减时,我们经常在每个固定数量的epoch之后将学习率降低(1)一半或(2)一个数量级。例如,假设我们的初始学习率是 α = 0.01。

在10个epoch之后,我们将学习率降低到 α = 0.005。

在另外10个epoch(即,第20个总epoch)之后,α 再次下降0.5倍,使得 α = 0.0025等。

实际上,这是完全相同的学习率方案,如图2所示(红线)。

蓝线显示更强势的跌落系数0.25。以数学方式建模,我们可以将基于步骤的衰减方程定义为:

这里的αi是初始学习率,F值是控制学习率下降速率的因子,D是每个epoch下降的值,E是当前epoch。

因子F越大,学习率就会越慢衰减。

反过来,因子F越小,学习率就会越快衰减。

综上,继续应用我们的StepDecay类

返回到 learning_rate_schedulers.py 文件并插入下面的代码:

代码语言:javascript复制
class StepDecay(LearningRateDecay):
  def __init__(self, initAlpha=0.01, factor=0.25, dropEvery=10):
    # 存储基本的初始学习率,下降因子和每个epoch下降量    
    self.initAlpha = initAlpha
    self.factor = factor
    self.dropEvery = dropEvery
 
  def __call__(self, epoch):
    # 为当前epoch计算学习率
    exp = np.floor((1   epoch) / self.dropEvery)
    alpha = self.initAlpha * (self.factor ** exp)
 
    # 返回学习率
    return float(alpha)

第2行定义StepDecay类的构造函数。

然后,我们存储初始学习率(initAlpha),下降因子和epoch值dropEvery。

call函数:

  • 接受当前的epoch数。
  • 基于上面详述的阶梯型的衰减公式计算学习率(第10和11行)。
  • 返回为当前epoch的计算得到的学习率(第14行)。

您将在本文后面看到如何使用此学习率方案。

keras中的线性和多项式学习率方案

我最喜欢的两个学习率方案是线性学习率衰减和多项式学习率衰减

使用这些方法,我们的学习率在固定数量的时期内衰减为零

学习率衰减的速率基于多项式函数的参数。多项式的较小指数/幂将导致学习速率“更慢”地衰减,而较大的指数会“更快地”衰减学习速率。

方便的是,这两种方法都可以在一个类中实现:

代码语言:javascript复制
class PolynomialDecay(LearningRateDecay):
  def __init__(self, maxEpochs=100, initAlpha=0.01, power=1.0):
    # 存储最大的epoch数,基本学习率和多项式的幂
    self.maxEpochs = maxEpochs
    self.initAlpha = initAlpha
    self.power = power
 
  def __call__(self, epoch):
    # 基于多项式衰减计算得到新的学习率
    decay = (1 - (epoch / float(self.maxEpochs))) ** self.power
    alpha = self.initAlpha * decay
 
    # 返回新的学习率
    return float(alpha)

第2行定义了PolynomialDecay 类的构造函数,它需要三个值:

  • maxEpochs :我们将要训练的epoch总数
  • initAlpha:初始化学习率
  • power:多项式的幂/指数

注意,如果设置 power=1.0,那么你将具有线性学习率衰减。

第10-11行为当前epoch计算调整后的学习率,而第14行返回新的学习率。

运行训练程序

现在我们已经实现了一些不同的keras学习率方案,让我们看看如何在实际的训练程序中应用它们。

新建一个文件,并命名为train.py,并插入下面的代码:

代码语言:javascript复制
# 设置matplotlib后端,以便可以在后台保存数字
import matplotlib
matplotlib.use("Agg")
 
# 导入必要的包
from pyimagesearch.learning_rate_schedulers import StepDecay
from pyimagesearch.learning_rate_schedulers import PolynomialDecay
from pyimagesearch.resnet import ResNet
from sklearn.preprocessing import LabelBinarizer
from sklearn.metrics import classification_report
from keras.callbacks import LearningRateScheduler
from keras.optimizers import SGD
from keras.datasets import cifar10
import matplotlib.pyplot as plt
import numpy as np
import argparse

第2-16行导入所需的包。

第3行设置matplotlib后端,以便我们可以将图形创建为图像文件。我们最值得注意的进口包括:

  • StepDecay:用于计算和绘制基于步骤的学习率衰减的类。
  • PolynomialDecay:编写的用于计算基于多项式的学习率衰减的类。
  • ResNet:在Keras应用的卷积神经网络。
  • LearningRateScheduler:一个Keras回调函数。我们将把我们的学习率方案schedule传递给这个类,这个类将在每个epoch完成时被称为回调函数,以计算我们的学习率。

让我们继续并解析我们的命令行参数:

代码语言:javascript复制
# 构造解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-s", "--schedule", type=str, default="",
  help="learning rate schedule method")
ap.add_argument("-e", "--epochs", type=int, default=100,
  help="# of epochs to train for")
ap.add_argument("-l", "--lr-plot", type=str, default="lr.png",
  help="path to output learning rate plot")
ap.add_argument("-t", "--train-plot", type=str, default="training.png",
  help="path to output training plot")
args = vars(ap.parse_args())

我们的程序接受四个命令行参数:

  • --schedule:学习率方案方法。有效选项是“standard”,“step”,“linear”,“poly”。默认情况下,不会使用学习率方案。
  • --epochs:要训练的epoch数(默认值= 100)。
  • --lr-plot:输出图的路径。我建议使用更具描述性的路径 文件名覆盖lr.png的默认值。
  • --train-plot:输出准确率/损失训练历史图的路径。同样,我建议使用描述性路径 文件名,否则将默认设置training.png。

有了我们的导入和命令行参数,现在是时候初始化我们的学习率方案了:

代码语言:javascript复制
# 在变量中存储要训练的epoch数,然后初始化要使用的回调列方案和学习率回调函数
epochs = args["epochs"]
callbacks = []
schedule = None
 
# 检查是否应该使用阶梯型(step-based)的学习率衰减
if args["schedule"] == "step":
  print("[INFO] using 'step-based' learning rate decay...")
  schedule = StepDecay(initAlpha=1e-1, factor=0.25, dropEvery=15)
 
# 检查是否应该使用线性学习率衰减
elif args["schedule"] == "linear":
  print("[INFO] using 'linear' learning rate decay...")
  schedule = PolynomialDecay(maxEpochs=epochs, initAlpha=1e-1, power=1)
 
# 检查是否应该使用多项式学习率衰减
elif args["schedule"] == "poly":
  print("[INFO] using 'polynomial' learning rate decay...")
  schedule = PolynomialDecay(maxEpochs=epochs, initAlpha=1e-1, power=5)
 
# 如果学习率计划不为空,请将其添加到回调列方案中
if schedule is not None:
  callbacks = [LearningRateScheduler(schedule)]

第2行设置我们将直接从命令行args变量训练的epoch数。

从那里我们将初始化我们的回调列方案和学习率方案(第3和4行)。

如果args [“schedule”]包含有效值,则第7-19行然后选择学习率方案:

  • “step”:初始化StepDecay
  • “linear”:使用power = 1初始化PolynomialDecay,方案示将使用线性学习率衰减
  • “poly”:使用power=5的多项式衰减

在你复制本教程中的实验结果后,请务必重新访问第7-19行并插入你自己的其他elif语句,以便你可以运行自己的一些实验!

第22和23行初始化LearningRateScheduler,并将调度作为回调列方案的单个回调部分。存在不使用学习速率衰减的情况(即,如果在执行程序时不覆盖--schedule命令行参数)。

让我们继续加载我们的数据:

代码语言:javascript复制
# 加载训练和测试数据,然后将其缩放到范围[0,1]
print("[INFO] loading CIFAR-10 data...")
((trainX, trainY), (testX, testY)) = cifar10.load_data()
trainX = trainX.astype("float") / 255.0
testX = testX.astype("float") / 255.0
 
# 将标签从整数转换为向量
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)
 
# 初始化CIFAR-10数据集的标签名称
labelNames = ["airplane", "automobile", "bird", "cat", "deer",
  "dog", "frog", "horse", "ship", "truck"]

第3行加载我们的CIFAR-10数据。数据集很方便地已经分成训练和测试集。

我们必须执行的唯一预处理是将数据缩放到范围[0, 1] (第4和5行)。

第8-10行对标签进行二值化,然后第13和14行初始化我们的labelNames(即类)。不要添加或更改labelNames列方案作为列方案的顺序和长度。

让我们初始化decay参数:

代码语言:javascript复制
# 初始化优化器的衰减
decay = 0.0
 
# 如果我们使用Keras的“标准”衰减,那么我们需要设置衰减参数
if args["schedule"] == "standard":
  print("[INFO] using 'keras standard' learning rate decay...")
  decay = 1e-1 / epochs
 
# 否则,没有使用学习率方案
elif schedule is None:
  print("[INFO] no learning rate schedule being used")

第2行初始化我们的学习率衰减。

如果我们使用“标准”学习速率衰减计划,则将衰减初始化为1e-1 / epochs(第5-7行)。

在完成所有初始化后,让我们继续编译 训练我们的ResNet模型:

代码语言:javascript复制
# 初始化我们的优化器和模型,然后编译它
opt = SGD(lr=1e-1, momentum=0.9, decay=decay)
model = ResNet.build(32, 32, 3, 10, (9, 9, 9),
  (64, 64, 128, 256), reg=0.0005)
model.compile(loss="categorical_crossentropy", optimizer=opt,
  metrics=["accuracy"])
 
# 训练网络
H = model.fit(trainX, trainY, validation_data=(testX, testY),
  batch_size=128, epochs=epochs, callbacks=callbacks, verbose=1)

我们的随机梯度下降(SGD)优化器使用的衰减在第2行初始化。

从那里开始,3和4行构建我们的ResNet CNN,输入形状为32x32x3和10个类。

我们的模型(model)使用“categorical_crossentropy”的损失函数进行编译,因为我们的数据集有> 2个类。如果你使用仅包含2个类的其他数据集,请务必使用loss =“binary_crossentropy”。

第9和10行是我们训练过程的一部分。请注意,我们已将回调作为参数提供。每个epoch完成后将调用回调。其中包含的LearningRateScheduler将处理我们的学习率衰减(只要回调不是空列方案)。

最后,让我们评估一下我们的网络并生成图方案:

代码语言:javascript复制
# 评估网络
print("[INFO] evaluating network...")
predictions = model.predict(testX, batch_size=128)
print(classification_report(testY.argmax(axis=1),
  predictions.argmax(axis=1), target_names=labelNames))
 
# 绘制训练loss和准确率
N = np.arange(0, args["epochs"])
plt.style.use("ggplot")
plt.figure()
plt.plot(N, H.history["loss"], label="train_loss")
plt.plot(N, H.history["val_loss"], label="val_loss")
plt.plot(N, H.history["acc"], label="train_acc")
plt.plot(N, H.history["val_acc"], label="val_acc")
plt.title("Training Loss and Accuracy on CIFAR-10")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend()
plt.savefig(args["train_plot"])
 
# 如果学习率方案不为空,则保存学习率图
if schedule is not None:
  schedule.plot(N)
  plt.savefig(args["lr_plot"])

第3-5行评估我们的网络并向我们的终端打印分类结果。

第8-19行生成并保存我们的训练历史图(准确率/loss曲线)。如果适用,第22-24行生成学习率方案图。我们将在下一节中检查这些绘图可视化。

keras学习率方案结果

通过我们的(1)学习率方案和(2)应用的训练程序,让我们进行一些实验,看看哪个学习率方案现最佳:

  1. 初始学习率为1e-1
  2. 共训练了100个epoch

实验1:没有学习率衰减/时间方案作为基准,我们首先在CIFAR-10上训练我们的ResNet模型,没有学习率衰减或衰减方案:

代码语言:javascript复制
$ python train.py --train-plot output/train_no_schedule.png
[INFO] loading CIFAR-10 data...
[INFO] no learning rate being used
Train on 50000 samples, validate on 10000 samples
Epoch 1/100
50000/50000 [==============================] - 186s 4ms/step - loss: 2.1204 - acc: 0.4372 - val_loss: 1.9361 - val_acc: 0.5118
Epoch 2/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.5150 - acc: 0.6440 - val_loss: 1.5013 - val_acc: 0.6413
Epoch 3/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.2186 - acc: 0.7369 - val_loss: 1.2288 - val_acc: 0.7315
...
Epoch 98/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.5220 - acc: 0.9568 - val_loss: 1.0223 - val_acc: 0.8372
Epoch 99/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.5349 - acc: 0.9532 - val_loss: 1.0423 - val_acc: 0.8230
Epoch 100/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.5209 - acc: 0.9579 - val_loss: 0.9883 - val_acc: 0.8421
[INFO] evaluating network...
              precision    recall  f1-score   support
 
    airplane       0.84      0.86      0.85      1000
  automobile       0.90      0.93      0.92      1000
        bird       0.83      0.74      0.78      1000
         cat       0.67      0.79      0.73      1000
        deer       0.78      0.88      0.83      1000
         dog       0.85      0.69      0.76      1000
        frog       0.85      0.89      0.87      1000
       horse       0.94      0.82      0.88      1000
        ship       0.91      0.90      0.90      1000
       truck       0.90      0.90      0.90      1000
 
   micro avg       0.84      0.84      0.84     10000
   macro avg       0.85      0.84      0.84     10000
weighted avg       0.85      0.84      0.84     10000

图3 在CIFAR-10上训练ResNet的第一个实验没有学习率衰减。

在这里,我们获得了大约85%的准确度,但正如我们所看到的,验证loss和准确率停滞在epoch〜15之后并且在100个epoch的剩余周期内没有改善。

我们现在的目标是利用学习率方案来超过我们85%的准确度(没有过度拟合)。

实验:2:Keras标准优化器学习率衰减在我们的第二个实验中,我们将使用Keras标准衰减的学习率方案:

代码语言:javascript复制
$ python train.py --schedule standard --train-plot output/train_standard_schedule.png
[INFO] loading CIFAR-10 data...
[INFO] using 'keras standard' learning rate decay...
Train on 50000 samples, validate on 10000 samples
Epoch 1/100
50000/50000 [==============================] - 184s 4ms/step - loss: 2.1074 - acc: 0.4460 - val_loss: 1.8397 - val_acc: 0.5334
Epoch 2/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.5068 - acc: 0.6516 - val_loss: 1.5099 - val_acc: 0.6663
Epoch 3/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.2097 - acc: 0.7512 - val_loss: 1.2928 - val_acc: 0.7176
...
Epoch 98/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1752 - acc: 1.0000 - val_loss: 0.8892 - val_acc: 0.8209
Epoch 99/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1746 - acc: 1.0000 - val_loss: 0.8923 - val_acc: 0.8204
Epoch 100/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1740 - acc: 1.0000 - val_loss: 0.8924 - val_acc: 0.8208
[INFO] evaluating network...
              precision    recall  f1-score   support
 
    airplane       0.81      0.86      0.84      1000
  automobile       0.91      0.91      0.91      1000
        bird       0.75      0.71      0.73      1000
         cat       0.68      0.65      0.66      1000
        deer       0.78      0.81      0.79      1000
         dog       0.77      0.74      0.75      1000
        frog       0.83      0.88      0.85      1000
       horse       0.86      0.87      0.86      1000
        ship       0.90      0.90      0.90      1000
       truck       0.90      0.88      0.89      1000
 
   micro avg       0.82      0.82      0.82     10000
   macro avg       0.82      0.82      0.82     10000
weighted avg       0.82      0.82      0.82     10000

图4 第二个学习率衰减方案实验使用Keras的标准学习率衰减方案

这次我们只获得82%的准确率,这方案明,学习率衰减/方案(调整方案)并不总能改善你的结果!你需要小心使用哪种学习率计划。

实验3:阶梯型学习率方案结果让我们继续并执行阶梯型学习率调整方案,这将使我们的学习率每15个时期降低0.25倍:

代码语言:javascript复制
$ python train.py --schedule step --lr-plot output/lr_step_schedule.png --train-plot output/train_step_schedule.png
[INFO] using 'step-based' learning rate decay...
[INFO] loading CIFAR-10 data...
Train on 50000 samples, validate on 10000 samples
Epoch 1/100
50000/50000 [==============================] - 186s 4ms/step - loss: 2.2839 - acc: 0.4328 - val_loss: 1.8936 - val_acc: 0.5530
Epoch 2/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.6425 - acc: 0.6213 - val_loss: 1.4599 - val_acc: 0.6749
Epoch 3/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.2971 - acc: 0.7177 - val_loss: 1.3298 - val_acc: 0.6953
...
Epoch 98/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1817 - acc: 1.0000 - val_loss: 0.7221 - val_acc: 0.8653
Epoch 99/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1817 - acc: 1.0000 - val_loss: 0.7228 - val_acc: 0.8661
Epoch 100/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1817 - acc: 1.0000 - val_loss: 0.7267 - val_acc: 0.8652
[INFO] evaluating network...
              precision    recall  f1-score   support
 
    airplane       0.86      0.89      0.87      1000
  automobile       0.94      0.93      0.94      1000
        bird       0.83      0.80      0.81      1000
         cat       0.75      0.73      0.74      1000
        deer       0.82      0.87      0.84      1000
         dog       0.82      0.77      0.79      1000
        frog       0.89      0.90      0.90      1000
       horse       0.91      0.90      0.90      1000
        ship       0.93      0.93      0.93      1000
       truck       0.90      0.93      0.92      1000
 
   micro avg       0.87      0.87      0.87     10000
   macro avg       0.86      0.87      0.86     10000
weighted avg       0.86      0.87      0.86     10000

图5 实验#3演示了阶梯型的学习率方案(左)。训练历史准确率/loss曲线显示在右侧。

图5(左)显示了我们的学习率方案。请注意,在每15个时期之后,我们的学习率会下降,从而产生“阶梯式”效果。

图5(右)演示了基于步骤的学习率调度的经典标志 - 您可以清楚地看到我们:

  • 训练/验证loss减少
  • 训练/验证准确率提高

......当我们的学习率下降时。

这在前两次下降(epoch15和30)中尤其明显,之后学习率下降变得不那么显著。

这种类型的陡降(steep drop)是使用阶梯型学习率方案的典型标志 -——如果你在论文,出版物或其他教程中看到这种类型的训练行为,几乎可以肯定他们使用阶梯型衰减!

回到我们的准确率,我们现在的准确率为86-87%,这是对我们第一次实验的改进。

实验4:线性学习率方案结果通过设置power = 1.0来尝试使用Keras的线性学习率方案:

代码语言:javascript复制
$ python train.py --schedule linear --lr-plot output/lr_linear_schedule.png --train-plot output/train_linear_schedule.png
[INFO] using 'linear' learning rate decay...
[INFO] loading CIFAR-10 data...
Epoch 1/100
50000/50000 [==============================] - 187s 4ms/step - loss: 2.0399 - acc: 0.4541 - val_loss: 1.6900 - val_acc: 0.5789
Epoch 2/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.4623 - acc: 0.6588 - val_loss: 1.4535 - val_acc: 0.6557
Epoch 3/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.1790 - acc: 0.7480 - val_loss: 1.2633 - val_acc: 0.7230
...
Epoch 98/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1025 - acc: 1.0000 - val_loss: 0.5623 - val_acc: 0.8804
Epoch 99/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1021 - acc: 1.0000 - val_loss: 0.5636 - val_acc: 0.8800
Epoch 100/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1019 - acc: 1.0000 - val_loss: 0.5622 - val_acc: 0.8808
[INFO] evaluating network...
              precision    recall  f1-score   support
 
    airplane       0.88      0.91      0.89      1000
  automobile       0.94      0.94      0.94      1000
        bird       0.84      0.81      0.82      1000
         cat       0.78      0.76      0.77      1000
        deer       0.86      0.90      0.88      1000
         dog       0.84      0.80      0.82      1000
        frog       0.90      0.92      0.91      1000
       horse       0.91      0.91      0.91      1000
        ship       0.93      0.94      0.93      1000
       truck       0.93      0.93      0.93      1000
 
   micro avg       0.88      0.88      0.88     10000
   macro avg       0.88      0.88      0.88     10000
weighted avg       0.88      0.88      0.88     10000

图6 线性学习速率衰减(左)应用于CIFAR-10上的ResNet超过100个时期与Keras。

训练准确率/loss曲线显示在右侧。

图6(左)显示我们的学习率随时间呈线性下降,而图6(右)显示我们的训练历史。

我们现在看到训练和验证loss的急剧下降,特别是在大约75个epoch左右; 但请注意,我们的训练loss明显快于我们的验证loss - 我们可能面临过度拟合的风险。

无论如何,我们现在可以获得88%的数据准确率,这是迄今为止我们的最佳结果。

实验5:多项式学习率计划结果作为最后的实验,让我们通过设置power = 5将多项式学习率调度应用于Keras:

代码语言:javascript复制
$ python train.py --schedule poly --lr-plot output/lr_poly_schedule.png --train-plot output/train_poly_schedule.png
[INFO] using 'polynomial' learning rate decay...
[INFO] loading CIFAR-10 data...
Epoch 1/100
50000/50000 [==============================] - 186s 4ms/step - loss: 2.0470 - acc: 0.4445 - val_loss: 1.7379 - val_acc: 0.5576
Epoch 2/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.4793 - acc: 0.6448 - val_loss: 1.4536 - val_acc: 0.6513
Epoch 3/100
50000/50000 [==============================] - 171s 3ms/step - loss: 1.2080 - acc: 0.7332 - val_loss: 1.2363 - val_acc: 0.7183
...
Epoch 98/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1547 - acc: 1.0000 - val_loss: 0.6960 - val_acc: 0.8581
Epoch 99/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1547 - acc: 1.0000 - val_loss: 0.6883 - val_acc: 0.8596
Epoch 100/100
50000/50000 [==============================] - 171s 3ms/step - loss: 0.1548 - acc: 1.0000 - val_loss: 0.6942 - val_acc: 0.8601
[INFO] evaluating network...
              precision    recall  f1-score   support
 
    airplane       0.86      0.89      0.87      1000
  automobile       0.94      0.94      0.94      1000
        bird       0.78      0.80      0.79      1000
         cat       0.75      0.70      0.73      1000
        deer       0.83      0.86      0.84      1000
         dog       0.81      0.78      0.79      1000
        frog       0.86      0.91      0.89      1000
       horse       0.92      0.88      0.90      1000
        ship       0.94      0.92      0.93      1000
       truck       0.91      0.92      0.91      1000
 
   micro avg       0.86      0.86      0.86     10000
   macro avg       0.86      0.86      0.86     10000
weighted avg       0.86      0.86      0.86     10000

图7 使用Keras进行基于多项式的学习率衰减结果

图7(左)显示了我们的学习率现在根据我们的多项式函数衰减的事实,而图7(右)绘制了我们的训练历史。

这次我们获得约~86%的准确率。

关于学习率方案实验的评论果

我们最好的实验来自我们的第四个实验,我们使用线性学习率计划。

但这是否意味着我们应该始终使用线性学习率方案?

不,实际上远非如此。

这里的关键点是:

  • 特定数据集(CIFAR-10)
  • 特殊的神经网络架构(ResNet)
  • 初始学习率为1e-2
  • 训练时期数(100)

......线性学习率方案是最好的。

没有两个深度学习项目是相似的,因此需要运行自己的一组实验,包括改变初始学习率和时期总数,以确定适当的学习率计划。

是否存在其他学习率方案?存在其他学习速率时间方案,并且实际上,可以接受epoch或batch数作为输入并且返回学习率的任何数学函数可以被认为是“学习率方案”。你可能遇到的另外两个学习率方案包括(1)指数学习率衰减,以及(2)周期性学习率。

我并不经常使用指数衰减,因为我发现线性和多项式衰减绰绰有余,但如果你愿意,我们非常欢迎你继承LearningRateDecay类并实现指数衰减。

另一方面,周期性学习率非常强大—— 我们将在本系列后面的教程中介绍周期性学习率。

如何选择初始学习率?你会注意到在本文中我们没有改变我们的学习率,我们将它保持在1e-2。

在执行你自己的实验时,需要组合:

  • 学习率方案...
  • ...具有不同的学习率

不要害怕混搭!

你想要探索的四个最重要的超参数包括:

  • 初始学习率
  • 训练的epoch数量
  • 学习率方案
  • 正则化强度/数量(L2,dropout等)

找到适当的平衡可能具有挑战性,但通过许多实验,你将能够找到一种能够产生高度精确的神经网络的方案。

总结

在本文中,你学习了如何利用Keras学习速率衰减和学习速率调度。

具体来说,你发现了如何使用Keras实现和利用多种学习率方案,包括:

  • 大多数Keras优化器内置的衰减方案
  • 阶梯型的学习率方案
  • 线性学习率衰减
  • 多项式学习率衰减

在实施我们的学习率方案后,我们对CIFAR-10数据集上的一组实验进行了评估。

我们的结果方案明,对于1e-2的初始学习率,线性学习速率方案(衰减超过100个时期)方案现最佳。

但是,这并不意味着线性学习速率计划总是优于其他类型的计划。相反,所有这些意味着为此:

  • 特定数据集(CIFAR-10)
  • 特殊的神经网络架构(ResNet)
  • 初始学习率为1e-2
  • 训练epoch数(100)

...线性学习率方案工作得最好。

没有两个深度学习项目是相似的,因此你需要运行自己的一组实验,包括改变初始学习率,以确定适当的学习率计划。

我建议你保留一个实验日志,详细说明任何超参数选择和相关结果,这样你就可以参考它,并对看起来很有希望的实验进行双重调整。

不要指望你能够训练神经网络并且“一次就完成” —— 很少,如果有的话,也会发生。相反,与自己一起设定期望,即你将继续运行许多实验和调整超参数。机器学习,深度学习和人工智能作为一个整体是迭代的—— 你建立在你以前的结果。

✎作者 | 神秘的铁头娃

欢迎分享文章到朋友圈,代码后台回复“学习率1”即可获取

原文链接:https://www.pyimagesearch.com/2019/07/22/keras-learning-rate-schedules-and-decay/

0 人点赞