• 深度学习的学习率调节实践


    作者|Luke Newman
    编译|VK
    来源|Towards Data Science

    多层感知器

    多层感知器(MLP)是由一个输入层、一个或多个隐藏层和一个称为输出层的最终层组成的人工神经网络(ANN)。通常,靠近输入层的层称为较低层,靠近输出层的层称为外层。除输出层外的每一层都包含一个偏置神经元,并与下一层完全相连。

    当一个ANN包含一个很深的隐藏层时,它被称为深度神经网络(DNN)。

    在这项调查中,我们将在MNIST时尚数据集上训练一个深度MLP,并通过指数增长来寻找最佳学习率,绘制损失图,并找到损失增长的点,以达到85%以上的准确率。对于最佳实践,我们将实现早期停止,保存检查点,并使用TensorBoard绘制学习曲线。

    你可以在这里查看jupyter Notebook:https://github.com/lukenew2/learning_rates_and_best_practices/blob/master/optimal_learning_rates_with_keras_api.ipynb

    指数学习率

    学习率可以说是最重要的超参数。一般情况下,最佳学习速率约为最大学习速率(即训练算法偏离的学习速率)的一半。找到一个好的学习率的一个方法是训练模型进行几百次迭代,从非常低的学习率(例如,1e-5)开始,逐渐增加到非常大的值(例如,10)。

    这是通过在每次迭代时将学习速率乘以一个常数因子来实现的。如果你将损失描绘为学习率的函数,你应该首先看到它在下降。但过一段时间后,学习率会太高,因此损失会迅速回升:最佳学习率将略低于转折点。然后,你可以重新初始化你的模型,并使用此良好的学习率对其进行正常训练。

    Keras模型

    我们先导入相关库

    import os
    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    
    PROJECT_ROOT_DIR = "."
    IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "images")
    os.makedirs(IMAGES_PATH, exist_ok=True)
    
    def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=300):
        path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)
        print("Saving figure", fig_id)
        if tight_layout:
            plt.tight_layout()
        plt.savefig(path, format=fig_extension, dpi=resolution)
    
    import tensorflow as tf
    from tensorflow import keras
    

    接下来加载数据集

    (X_train, y_train), (X_test, y_test) = keras.datasets.fashion_mnist.load_data()
    
    X_train.shape
    
    X_train.dtype
    

    标准化像素

    X_valid, X_train = X_train[:5000] / 255.0, X_train[5000:] / 255.0
    y_valid, y_train = y_train[:5000], y_train[5000:] 
    X_test = X_test / 255.0
    

    让我们快速看一下数据集中的图像样本,让我们感受一下分类任务的复杂性:

    class_names = ["T-shirt/top", "Trouser", "Pullover", "Dress", "Coat",
                   "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"]
    
    n_rows = 4
    n_cols = 10
    plt.figure(figsize=(n_cols * 1.2, n_rows * 1.2))
    for row in range(n_rows):
        for col in range(n_cols):
            index = n_cols * row + col
            plt.subplot(n_rows, n_cols, index + 1)
            plt.imshow(X_train[index], cmap="binary", interpolation="nearest")
            plt.axis('off')
            plt.title(class_names[y_train[index]], fontsize=12)
    plt.subplots_adjust(wspace=0.2, hspace=0.5)
    save_fig('fashion_mnist_plot', tight_layout=False)
    plt.show()
    

    我们已经准备好用Keras来建立我们的MLP。下面是一个具有两个隐藏层的分类MLP:

    model = keras.models.Sequential([
        keras.layers.Flatten(input_shape=[28,28]),
        keras.layers.Dense(300, activation="relu"),
        keras.layers.Dense(100, activation="relu"),
        keras.layers.Dense(10, activation="softmax")
    ])
    

    让我们一行一行地看这个代码:

    • 首先,我们创建了一个Sequential模型,它是神经网络中最简单的Keras模型,它只由一堆按顺序连接的层组成。

    • 接下来,我们构建第一层并将其添加到模型中。它是一个Flatten层,其目的是将每个输入图像转换成一个1D数组:如果它接收到输入数据X,则计算X.reshape(-1,1)。因为它是模型的第一层,所以应该指定输入形状。或者,你可以添加keras.layers.InputLayer作为第一层,设置其input_shape=[28,28]

    • 下一步,我们添加一个300个神经元的隐藏层,并指定它使用ReLU激活函数。每一个全连接层管理自己的权重矩阵,包含神经元与其输入之间的所有连接权重。它还管理一个偏置向量,每个神经元一个。

    • 然后我们添加了第二个100个神经元的隐藏层,同样使用ReLU激活函数。

    • 最后,我们使用softmax激活函数添加了一个包含10个神经元的输出层(因为我们执行的分类是每个类都是互斥的)。

    使用回调

    在Keras中,fit()方法接受一个回调参数,该参数允许你指定Keras将在训练开始和结束、每个epoch的开始和结束时,甚至在处理每个batch处理之前和之后要调用对象的列表。

    为了实现指数级增长的学习率,我们需要创建自己的自定义回调。我们的回调接受一个参数,用于提高学习率的因子。为了将损失描绘成学习率的函数,我们跟踪每个batch的速率和损失。

    请注意,我们将函数定义为on_batch_end(),这取决于我们的目标。当然也可以是on_train_begin(), on_train_end(), on_batch_begin()。对于我们的用例,我们希望在每个批处理之后提高学习率并记录损失:

    K = keras.backend
    
    class ExponentialLearningRate(keras.callbacks.Callback):
        def __init__(self, factor):
            self.factor = factor
            self.rates = []
            self.losses = []
        def on_batch_end(self, batch, logs):
            self.rates.append(K.get_value(self.model.optimizer.lr))
            self.losses.append(logs["loss"])
            K.set_value(self.model.optimizer.lr, self.model.optimizer.lr * self.factor)
    

    现在我们的模型已经创建好了,我们只需调用它的compile()方法来指定要使用的loss函数和优化器。或者,你可以指定要在训练和评估期间计算的额外指标列表。

    首先,我们使用“稀疏的分类交叉熵”损失,因为我们有稀疏的标签(也就是说,对于每个实例,只有一个目标类索引,在我们的例子中,从0到9),并且这些类是互斥的)。接下来,我们指定使用随机梯度下降,并将学习速率初始化为1e-3,并在每次迭代中增加0.5%:

    model.compile(loss="sparse_categorical_crossentropy",
                  optimizer=keras.optimizers.SGD(lr=1e-3),
                  metrics=["accuracy"])
    expon_lr = ExponentialLearningRate(factor=1.005)
    

    现在让我们只在一个epoch训练模型:

    history = model.fit(X_train, y_train, epochs=1,
                        validation_data=(X_valid, y_valid),
                        callbacks=[expon_lr])
    

    我们现在可以将损失绘制为学习率的函数:

    plt.plot(expon_lr.rates, expon_lr.losses)
    plt.gca().set_xscale('log')
    plt.hlines(min(expon_lr.losses), min(expon_lr.rates), max(expon_lr.rates))
    plt.axis([min(expon_lr.rates), max(expon_lr.rates), 0, expon_lr.losses[0]])
    plt.xlabel("Learning rate")
    plt.ylabel("Loss")
    save_fig("learning_rate_vs_loss")
    

    正如我们所期望的,随着学习率的提高,最初的损失逐渐减少。但过了一段时间,学习率太大,导致损失反弹:最佳学习率将略低于损失开始攀升的点(通常比转折点低10倍左右)。我们现在可以重新初始化我们的模型,并使用良好的学习率对其进行正常训练。

    还有更多的学习率技巧,包括创建学习进度表,我希望在以后的调查中介绍,但对如何手动选择好的学习率有一个直观的理解同样重要。

    我们的损失在3e-1左右开始反弹,所以让我们尝试使用2e-1作为我们的学习率:

    keras.backend.clear_session()
    np.random.seed(42)
    tf.random.set_seed(42)
    
    model = keras.models.Sequential([
        keras.layers.Flatten(input_shape=[28, 28]),
        keras.layers.Dense(300, activation="relu"),
        keras.layers.Dense(100, activation="relu"),
        keras.layers.Dense(10, activation="softmax")
    ])
    
    model.compile(loss="sparse_categorical_crossentropy",
                  optimizer=keras.optimizers.SGD(lr=2e-1),
                  metrics=["accuracy"])
    

    使用TensorBoard进行可视化

    TensorBoard是一个很好的交互式可视化工具,你可以使用它查看训练期间的学习曲线、比较学习曲线、可视化计算图、分析训练统计数据、查看模型生成的图像,可视化复杂的多维数据投影到三维和自动聚类,等等!这个工具是在安装TensorFlow时自动安装的,所以你应该已经安装了。

    让我们首先定义将用于TensorBoard日志的根日志目录,再加上一个小函数,该函数将根据当前时间生成一个子目录路径,以便每次运行时它都是不同的。你可能需要在日志目录名称中包含额外的信息,例如正在测试的超参数值,以便更容易地了解你在TensorBoard中查看的内容:

    root_logdir = os.path.join(os.curdir, "my_logs")
    
    def get_run_logdir():
        import time
        run_id = time.strftime("run_%Y_%m_%d-%H_%M_%S")
        return os.path.join(root_logdir, run_id)
    
    run_logdir = get_run_logdir() # 例如, './my_logs/run_2020_07_31-15_15_22'
    

    Keras api提供了一个TensorBoard()回调函数。TensorBoard()回调函数负责创建日志目录,并在训练时创建事件文件和编写摘要(摘要是一种二进制数据记录用于创建可视化TensorBoard)。

    每次运行有一个目录,每个目录包含一个子目录,分别用于训练日志和验证日志。两者都包含事件文件,但训练日志也包含分析跟踪:这使TensorBoard能够准确地显示模型在模型的每个部分(跨越所有设备)上花费了多少时间,这对于查找性能瓶颈非常有用。

    early_stopping_cb = keras.callbacks.EarlyStopping(patience=20)
    checkpoint_cb = keras.callbacks.ModelCheckpoint("my_fashion_mnist_model.h5", save_best_only=True)
    tensorboard_cb = keras.callbacks.TensorBoard(run_logdir)
    
    history = model.fit(X_train, y_train, epochs=100,
                        validation_data=(X_valid, y_valid),
                        callbacks=[early_stopping_cb, checkpoint_cb, tensorboard_cb])
    

    接下来,我们需要启动TensorBoard服务器。我们可以通过运行以下命令在Jupyter中直接执行此操作。第一行加载TensorBoard扩展,第二行启动端口6004上的TensorBoard服务器,并连接到它:

    %load_ext tensorboard 
    %tensorboard — logdir=./my_logs — port=6004
    

    现在你应该可以看到TensorBoard的web界面。单击“scaler”选项卡以查看学习曲线。在左下角,选择要可视化的日志(例如,第一次运行的训练日志),然后单击epoch_loss scaler。请注意,在我们的训练过程中,训练损失下降得很顺利。

    你还可以可视化整个图形、学习的权重(投影到3D)或分析轨迹。TensorBoard()回调函数也有记录额外数据的选项,例如NLP数据集的嵌入。

    这实际上是一个非常有用的可视化工具。

    结论

    在这里我们得到了88%的准确率,这是我们可以达到的最好的深度MLP。如果我们想进一步提高性能,我们可以尝试卷积神经网络(CNN),它对图像数据非常有效。

    就我们的目的而言,这就足够了。我们学会了如何:

    • 使用Keras的Sequential API构建深度mlp。

    • 通过按指数增长学习率,绘制损失图,并找到损失重新出现的点,来找到最佳学习率。

    • 构建深度学习模型时的最佳实践,包括使用回调和使用TensorBoard可视化学习曲线。

    如果你想在这里看到演示幻灯片或jupyterNotebook中完整的代码和说明,请随时查看Github存储库:https://github.com/lukenew2/learning_rates_and_best_practices

    额外的资源

    https://www.tensorflow.org/tensorboard/get_started

    https://towardsdatascience.com/learning-rate-schedules-and-adaptive-learning-rate-methods-for-deep-learning-2c8f433990d1

    原文链接:https://towardsdatascience.com/learning-rates-and-best-practices-for-deep-learning-e77215a86178

    欢迎关注磐创AI博客站:
    http://panchuang.net/

    sklearn机器学习中文官方文档:
    http://sklearn123.com/

    欢迎关注磐创博客资源汇总站:
    http://docs.panchuang.net/

  • 相关阅读:
    JS中的原型规则与原型链
    JS中的“==”与强制类型转换
    协作开发中常用的Git命令小结
    JavaScript变量类型检测总结
    IDEA IntelliJ常用设置以及快捷键(转)
    Spring 发送 Email
    SSM框架的整合思路&功能实现
    使用Eclipse把java文件打包成jar 含有第三方jar库的jar包
    基于CDH5.x 下面使用eclipse 操作hive 。使用java通过jdbc连接HIVESERVICE 创建表
    Volley源码学习笔记
  • 原文地址:https://www.cnblogs.com/panchuangai/p/13568128.html
Copyright © 2020-2023  润新知