Press "Enter" to skip to content

超级加速深度学习模型的训练

使用单周期学习率实现超级收敛

Philip Swinburn在Unsplash上的照片

您是否遇到过这样的情况:在准确率达到90%之前很容易获得初始的提升,但是一旦达到90%,就需要非常努力才能获得进一步的性能提升?您的模型训练时间是否过长?

在本文中,我们将介绍一种有趣的技术来加速您的训练过程,获得额外的性能提升并加快训练速度。基本上,我们将通过使用一种名为单周期学习率的策略来动态地改变学习率。

最初由Leslie Smith在一篇论文中提到,单周期学习率调度[1][2]专注于在训练过程中动态更新学习率的独特策略。听起来有点复杂,不用担心,让我们先从典型的训练设置开始,然后逐渐了解如何使用单周期学习率来改善结果。

训练图像分类器

由于我们正努力学习一种改善模型性能的巧妙技巧(周期率),为什么不边享受经典互动游戏剪刀-石头-布呢。

Markus Spiske在Unsplash上的照片

问题陈述

剪刀-石头-布是一种经典的儿童游戏,两个玩家使用手势(剪刀、石头或布)进行对抗,目标是战胜对手。例如,石头战胜剪刀,但布战胜石头。有趣,对吧?

我们的目标是训练一个图像分类模型,能够检测这三种手势中的一种。然后,我们可以利用这样训练好的模型来开发一个完整的游戏。但本文仅限于训练分类器本身,含有可供部署的模型的完整游戏将在另一篇文章中说明。

数据集

我们很幸运地已经有一个标记好的数据集,可以高效地利用它来训练一个分类模型。这个数据集托管在TensorFlow数据集目录中,并由Laurence Moroney(CC BY 2.0)提供。它具有以下属性:

  • 数据点数量:2800
  • 类别数量:3
  • 可用的训练-测试拆分:是
  • 数据集大小:220 MiB

TensorFlow提供了一个简洁的API来访问这些数据集,以下代码段可用于下载训练和验证集:

import tensorflow_datasets as tfdsDATASET_NAME = 'rock_paper_scissors'(dataset_train_raw, dataset_test_raw), dataset_info = tfds.load(    name=DATASET_NAME,    data_dir='tmp',    with_info=True,    as_supervised=True,    split=[tfds.Split.TRAIN, tfds.Split.TEST],)# 从数据集中绘制样本fig = tfds.show_examples(dataset_train_raw, dataset_info)

以下是来自该数据集的一些样本图像:

图:剪刀石头布数据集中的样本数据点

学习率

学习率是一个关键的超参数,它可以决定一个设置的成败,但通常被忽视。之所以被忽视,是因为大多数库/包已经预设了足够好的默认值。但这些默认值只能起到一定作用。

对于我们这种特殊情况的定制使用,找到正确的学习率非常重要。确定最优值是一个棘手的权衡。学习率过慢(或过小),模型几乎不会学到任何东西。学习率过快(或过大),模型会超过神经网络所寻找的那个神秘的极小值。下图是一个更好理解的说明:

图:学习率对模型学习目标(极小值)的影响。 来源:作者

梯度下降和优化器

梯度下降是训练/优化神经网络的标准方法。它通过在梯度的相反方向上更新网络的参数来最小化目标函数。不详细介绍,它有助于沿着目标函数的斜坡下降。可参考这里详细了解梯度下降。

深度学习社区在最初使用基本梯度下降训练模型后已经取得了长足进展。多年来,许多改进方法帮助加快训练速度并避免明显的问题。简要介绍一些值得注意和最受欢迎的方法:

AdaGrad:适应性梯度算法是一种优化算法,它根据每个参数的历史梯度调整学习率,允许对不频繁出现的参数进行较大的更新,对频繁出现的参数进行较小的更新。它旨在高效地处理稀疏数据。在处理稀疏数据时非常适用。

RMSProp:均方根传播是通过使用最近梯度平方的移动平均数来调整每个参数的学习率,优化学习过程。它通过解决AdaGrad中学习率趋于减小的问题,以及根据最近梯度大小自适应地缩放学习率。

ADAM:自适应矩估计是一种优化算法,综合了RMSProp和动量方法的思想。它保持过去梯度和梯度平方的指数衰减平均值,并使用它们来自适应地更新参数。ADAM以其高效性和对深度神经网络训练的有效性而闻名。

一周学习率和超级收敛

一周学习率是一个简单的两步流程,用于在训练过程中改进学习率和动量。它的工作原理如下:

  • 步骤1:我们从一个较低的值线性逐渐增加学习率到一个较高的值
  • 步骤2:在接下来的几个epoch中,保持最高值的学习率
  • 步骤3:然后逐渐降低学习率

在这三个步骤中,动量则以完全相反的方向更新,即当学习率增加时,动量减小,反之亦然。

一周学习率的应用

首先,我们通过一个简单的实现来了解一周学习率,然后将其用于训练我们的模型。我们将从Martin Gorner在2019年的TensorFlow World演讲中借用他的一周学习率计划的现成实现,如清单2所示。

def lr_function(epoch):    # 设置学习率的起始、最小和最大值    start_lr = 1e-3; min_lr = 1e-3; max_lr = 2e-3    # 定义逐渐增加学习率的epoch数和衰减因子    rampup_epochs = 6; sustain_epochs = 0; exp_decay = .5    # 根据当前epoch更新学习率的方法    def lr(epoch, start_lr, min_lr, max_lr, rampup_epochs,           sustain_epochs, exp_decay):        if epoch < rampup_epochs:            lr = ((max_lr - start_lr) / rampup_epochs                        * epoch + start_lr)        elif epoch < rampup_epochs + sustain_epochs:            lr = max_lr        else:            lr = ((max_lr - min_lr) *                      exp_decay**(epoch - rampup_epochs -                                    sustain_epochs) + min_lr)        return lr    return lr(epoch, start_lr, min_lr, max_lr,              rampup_epochs, sustain_epochs, exp_decay)

我们执行这个函数(见代码列表2)来展示学习率如何随着前面讨论的两个步骤变化。在这里,我们首先使用初始学习率为1e-3,在前几个时期将其提升到2e-3。然后在剩余的时期逐渐降低回1e-3。下图展示了在24个时期内的动态学习率曲线。

24个时期内的单周期学习率策略。学习率线性增加,随后在剩余时期缓慢衰减。图片来源:作者

我们将通过将其应用于使用MobileNetV2模型作为特征提取器,并在当前剪刀石头布案例中对分类头进行训练来测试我们的单周期学习率调度程序。然后,我们将与简单的CNN以及使用标准Adam优化器的MobileNetV2 +分类头进行比较。完整的笔记本可在github上参考。以下代码段概述了如何使用TensorFlow回调函数来插入我们的1周期率实用程序。

# 设置图像形状 INPUT_IMG_SHAPE= (128, 128, 3)# 获取预训练的MobileNetV2base_model = tf.keras.applications.MobileNetV2(  input_shape=INPUT_IMG_SHAPE,  include_top=False,  weights='imagenet',  pooling='avg')# 添加分类头model_lr = tf.keras.models.Sequential()model_lr.add(base_model)model_lr.add(tf.keras.layers.Dropout(0.5))model_lr.add(tf.keras.layers.Dense(    units=NUM_CLASSES,    activation=tf.keras.activations.softmax,    kernel_regularizer=tf.keras.regularizers.l2(l=0.01)))# 编译模型model_lr.compile(    optimizer=tf.keras.optimizers.Adam(),    loss=tf.keras.losses.sparse_categorical_crossentropy,    metrics=['accuracy'])# 设置时期数initial_epochs = 24# 设置模型进行训练# LearningRateScheduler回调函数是我们将# 自定义的1周期率函数插入的地方training_history_lr = model_lr.fit(    x=dataset_train_augmented_shuffled.repeat(),    validation_data=dataset_test_shuffled.repeat(),    epochs=initial_epochs,    steps_per_epoch=steps_per_epoch,    validation_steps=validation_steps,    callbacks=[        tf.keras.callbacks.LearningRateScheduler(lambda epoch: \                                             lr_function(epoch),                                             verbose=True)    ],    verbose=1)

我们使用批量大小为64训练所有三个模型24个时期。以下图展示了1周期学习率的影响。与其他两个模型相比,它能够帮助我们的模型在仅使用5个时期中实现收敛。在验证数据集中,超级收敛现象也是可见的。

使用1周期学习率的MobileNetV2(mobileNetV2_lr)在仅使用5个时期内效果优于MobileNetV2和简单CNN架构

在10个时期内,我们达到了验证精度在90-92%之间的一致值,这是迄今为止在所有模型中看到的最好结果。在测试数据集上评估模型性能也展示了同样的情况,即MobileNetV2_lr轻松超过了其他两个模型。

# 简单CNN测试损失:  0.7511898279190063测试准确率:  0.7768816947937012# MobileNetV2测试损失:  0.24527719616889954测试准确率:  0.9220430254936218# MobileNetV2_LR测试损失:  0.27864792943000793测试准确率:  0.9166666865348816

结论

通过实施One-Cycle学习率技术,我们可以克服模型性能在90%准确率之后的停滞现象,并优化训练时间。这项技术是由Leslie Smith和他的团队引入的,它能在训练过程中动态调整学习率,为模型性能的提升提供了战略性方法。采用这种方法,您可以高效地处理训练设置的复杂性,释放出更快速和更有效的深度学习模型的潜力。拥抱One-Cycle学习率的力量,提升您的训练体验,取得卓越的结果!

Leave a Reply

Your email address will not be published. Required fields are marked *