Keras深度学习框架第二十九讲:在自定义训练循环中应用KerasTuner超参数优化

1、简介

在KerasTuner中,HyperModel类提供了一种方便的方式来在可重用对象中定义搜索空间。你可以通过重写HyperModel.build()方法来定义和进行模型的超参数调优。为了对训练过程进行超参数调优(例如,通过选择适当的批处理大小、训练轮数或数据增强设置),程序员可以重写HyperModel.fit()方法,在该方法中你可以访问:

  • hp对象,它是keras_tuner.HyperParameters的一个实例
  • 由HyperModel.build()构建的模型

在"开始使用KerasTuner"一文的"调整模型训练"部分中给出了一个基本示例。

2、自定义训练循环的超参数调优

本文将通过重写HyperModel.fit()方法来子类化HyperModel类,并编写一个自定义训练循环。如果你想了解如何使用Keras编写一个自定义训练循环,可以参考指南《从零开始编写训练循环》。

首先,我们导入所需的库,并为训练和验证创建数据集。在这里,我们仅使用随机数据作为演示目的。

python 复制代码
import keras_tuner
import tensorflow as tf
import keras
import numpy as np


x_train = np.random.rand(1000, 28, 28, 1)
y_train = np.random.randint(0, 10, (1000, 1))
x_val = np.random.rand(1000, 28, 28, 1)
y_val = np.random.randint(0, 10, (1000, 1))

接着,我们将HyperModel类子类化为MyHyperModel。在MyHyperModel.build()中,我们构建一个简单的Keras模型来进行10个不同类别的图像分类。MyHyperModel.fit()接受几个参数,其签名如下所示:

python 复制代码
def fit(self, hp, model, x, y, validation_data, callbacks=None, **kwargs):

hp 参数用于定义超参数。
model 参数是由 MyHyperModel.build() 返回的模型。
x, y, 和 validation_data 都是自定义参数。稍后我们将通过调用 tuner.search(x=x, y=y, validation_data=(x_val, y_val)) 来传递我们的数据给它们。你可以定义任意数量的这些参数并给它们自定义的名称。
callbacks 参数原本是为了与 model.fit() 一起使用的。KerasTuner 在其中放置了一些有用的 Keras 回调,例如,在模型最佳轮次时保存模型的回调。

在自定义训练循环中,我们将手动调用这些回调。但在调用它们之前,我们需要使用以下代码将我们的模型分配给它们,以便它们可以访问模型以进行保存。

python 复制代码
for callback in callbacks:
    callback.model = model

在这个例子中,我们只调用了回调的 on_epoch_end() 方法来帮助我们保存模型的最佳状态。如果需要,你也可以调用其他回调方法。如果你不需要保存模型,那么你就不需要使用回调。

在自定义训练循环中,我们将通过将NumPy数据包装成tf.data.Dataset来调优数据集的批处理大小。请注意,你也可以在这里调优任何预处理步骤。此外,我们还调优了优化器的学习率。

我们将使用验证损失作为模型的评估指标。为了计算平均验证损失,我们将使用keras.metrics.Mean(),它在批次之间平均验证损失。我们需要返回验证损失,以便Tuner可以记录它。

python 复制代码
class MyHyperModel(keras_tuner.HyperModel):
    def build(self, hp):
        """Builds a convolutional model."""
        inputs = keras.Input(shape=(28, 28, 1))
        x = keras.layers.Flatten()(inputs)
        x = keras.layers.Dense(
            units=hp.Choice("units", [32, 64, 128]), activation="relu"
        )(x)
        outputs = keras.layers.Dense(10)(x)
        return keras.Model(inputs=inputs, outputs=outputs)

    def fit(self, hp, model, x, y, validation_data, callbacks=None, **kwargs):
        # Convert the datasets to tf.data.Dataset.
        batch_size = hp.Int("batch_size", 32, 128, step=32, default=64)
        train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(
            batch_size
        )
        validation_data = tf.data.Dataset.from_tensor_slices(validation_data).batch(
            batch_size
        )

        # Define the optimizer.
        optimizer = keras.optimizers.Adam(
            hp.Float("learning_rate", 1e-4, 1e-2, sampling="log", default=1e-3)
        )
        loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)

        # The metric to track validation loss.
        epoch_loss_metric = keras.metrics.Mean()

        # Function to run the train step.
        @tf.function
        def run_train_step(images, labels):
            with tf.GradientTape() as tape:
                logits = model(images)
                loss = loss_fn(labels, logits)
                # Add any regularization losses.
                if model.losses:
                    loss += tf.math.add_n(model.losses)
            gradients = tape.gradient(loss, model.trainable_variables)
            optimizer.apply_gradients(zip(gradients, model.trainable_variables))

        # Function to run the validation step.
        @tf.function
        def run_val_step(images, labels):
            logits = model(images)
            loss = loss_fn(labels, logits)
            # Update the metric.
            epoch_loss_metric.update_state(loss)

        # Assign the model to the callbacks.
        for callback in callbacks:
            callback.set_model(model)

        # Record the best validation loss value
        best_epoch_loss = float("inf")

        # The custom training loop.
        for epoch in range(2):
            print(f"Epoch: {epoch}")

            # Iterate the training data to run the training step.
            for images, labels in train_ds:
                run_train_step(images, labels)

            # Iterate the validation data to run the validation step.
            for images, labels in validation_data:
                run_val_step(images, labels)

            # Calling the callbacks after epoch.
            epoch_loss = float(epoch_loss_metric.result().numpy())
            for callback in callbacks:
                # The "my_metric" is the objective passed to the tuner.
                callback.on_epoch_end(epoch, logs={"my_metric": epoch_loss})
            epoch_loss_metric.reset_state()

            print(f"Epoch loss: {epoch_loss}")
            best_epoch_loss = min(best_epoch_loss, epoch_loss)

        # Return the evaluation metric value.
        return best_epoch_loss

现在,我们可以初始化Tuner了。在这里,我们使用Objective("my_metric", "min")作为需要最小化的指标。目标名称应该与你在传递给回调的on_epoch_end()方法的日志中使用的键一致。回调需要使用日志中的这个值来找到最佳的epoch以保存模型的检查点。

换句话说,当你自定义训练循环并决定在每个epoch结束时记录一些指标时,你需要确保你传递给on_epoch_end()方法的日志中包含一个键(例如"my_metric"),该键与你在Tuner中定义的Objective的名称相匹配。这样,Tuner就可以使用这个指标来跟踪模型性能的变化,并决定何时保存最佳的模型检查点。

在上面的例子中,如果我们在每个epoch结束时计算了验证损失,并将其作为"val_loss"键传递给on_epoch_end()方法,那么我们需要在初始化Tuner时使用Objective("val_loss", "min"),因为我们的目标是找到具有最小验证损失的epoch。

python 复制代码
tuner = keras_tuner.RandomSearch(
    objective=keras_tuner.Objective("my_metric", "min"),
    max_trials=2,
    hypermodel=MyHyperModel(),
    directory="results",
    project_name="custom_training",
    overwrite=True,
)

我们通过将我们在MyHyperModel.fit()方法的签名中定义的参数传递给tuner.search()来开始搜索。

python 复制代码
tuner.search(x=x_train, y=y_train, validation_data=(x_val, y_val))

最后,我们可以检索结果。

在Keras Tuner中,一旦tuner.search()方法执行完毕,你就可以从Tuner对象中检索最佳模型、最佳超参数配置以及搜索结果的历史记录。这些结果可以帮助你理解模型性能如何随着超参数的变化而变化,并为你提供最佳的模型配置以进行进一步的应用或部署。

通常,你可以使用tuner.get_best_models()来获取一个或多个最佳模型,使用tuner.get_best_hyperparameters()来获取最佳超参数配置,以及使用tuner.results_summary()来查看搜索结果的摘要。

python 复制代码
best_hps = tuner.get_best_hyperparameters()[0]
print(best_hps.values)

best_model = tuner.get_best_models()[0]
best_model.summary()

3、总结

使用Keras Tuner进行自定义训练循环超参数调优的过程可以大致分为以下几个步骤:

3.1. 安装Keras Tuner

首先,确保你已经安装了Keras Tuner库。可以使用pip进行安装:

bash 复制代码
pip install keras-tuner

3.2. 定义继承自keras_tuner.HyperModel的类

你需要定义一个继承自keras_tuner.HyperModel的类,并在其中定义buildfit方法。

  • build方法:用于定义模型的架构,并使用hp参数设置超参数的搜索空间。
  • fit方法:用于模型的训练过程,它接受hp参数以及训练数据和其他必要的参数。
python 复制代码
import tensorflow as tf
from tensorflow.keras import layers
from keras_tuner import HyperModel

class MyHyperModel(HyperModel):
    def build(self, hp):
        model = tf.keras.Sequential()
        # 示例:定义含可调参数的全连接层
        hp_units = hp.Int('units', min_value=32, max_value=512, step=32)
        model.add(layers.Dense(units=hp_units, activation='relu'))
        # ... 其他层 ...
        model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
        return model

    def fit(self, hp, x_train, y_train, **kwargs):
        model = self.build(hp)
        model.fit(x_train, y_train, epochs=10, **kwargs)
        # 假设这里只进行一轮训练作为示例,实际中可能需要多轮
        return {'loss': model.evaluate(x_train, y_train)[0], 'accuracy': model.evaluate(x_train, y_train)[1]}

3.3. 准备数据和回调

准备好你的训练数据和验证数据,以及可能需要的回调函数(如模型保存、早停等)。

3.4. 使用Tuner进行搜索

实例化你的Tuner类(如RandomSearchHyperband等),并传入你的HyperModel、数据以及搜索的目标(如最小化验证损失)。

python 复制代码
from keras_tuner import RandomSearch

tuner = RandomSearch(
    MyHyperModel(),
    objective='val_loss',
    max_trials=10,  # 搜索的最大试验次数
    executions_per_trial=3,  # 每个试验的重复次数
    directory='my_dir',  # 结果保存目录
    project_name='my_project'
)

tuner.search(x_train, y_train,
             validation_data=(x_val, y_val),
             epochs=10,  # 注意这里的epochs仅用于fit方法中的一轮训练
             callbacks=[...])  # 可能的回调,如ModelCheckpoint

3.5. 检索结果

搜索完成后,你可以从Tuner对象中检索最佳模型、最佳超参数配置以及搜索结果的历史记录。

python 复制代码
best_hps = tuner.get_best_hyperparameters(num_trials=1)[0]
print(f'Best hyperparameters: {best_hps.values}')

best_model = tuner.get_best_models(num_models=1)[0]
# 使用best_model进行预测或进一步评估

3.6. 可视化结果

Keras Tuner提供了丰富的可视化支持,你可以使用TensorBoard等工具来查看搜索过程的详细结果。

使用Keras Tuner进行自定义训练循环的超参数调优涉及安装Keras Tuner库,定义继承自HyperModel的类并实现其build和fit方法,准备训练数据和验证数据以及可能的回调。随后,实例化Tuner类并传入定义的HyperModel和数据,开始搜索最佳超参数组合。搜索完成后,可以通过Tuner的接口检索到最佳的超参数和模型。整个调优过程中需要注意设置合理的搜索空间、试验次数,并使用独立的验证集来评估模型性能,最后可以利用可视化工具查看调优结果。

相关推荐
川石课堂软件测试7 分钟前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
985小水博一枚呀15 分钟前
【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法
人工智能·深度学习·神经网络·cnn·transformer
AltmanChan16 分钟前
大语言模型安全威胁
人工智能·安全·语言模型
985小水博一枚呀20 分钟前
【深度学习滑坡制图|论文解读2】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法
人工智能·深度学习·神经网络·cnn·transformer·迁移学习
数据与后端架构提升之路29 分钟前
从神经元到神经网络:深度学习的进化之旅
人工智能·神经网络·学习
爱技术的小伙子35 分钟前
【ChatGPT】如何通过逐步提示提高ChatGPT的细节描写
人工智能·chatgpt
深度学习实战训练营2 小时前
基于CNN-RNN的影像报告生成
人工智能·深度学习
昨日之日20064 小时前
Moonshine - 新型开源ASR(语音识别)模型,体积小,速度快,比OpenAI Whisper快五倍 本地一键整合包下载
人工智能·whisper·语音识别
浮生如梦_4 小时前
Halcon基于laws纹理特征的SVM分类
图像处理·人工智能·算法·支持向量机·计算机视觉·分类·视觉检测
深度学习lover4 小时前
<项目代码>YOLOv8 苹果腐烂识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·苹果腐烂识别