使用Keras Tuner进行自动超参数调优的实用教程

article/2025/9/26 16:31:31

在本文中将介绍如何使用 KerasTuner,并且还会介绍其他教程中没有的一些技巧,例如单独调整每一层中的参数或与优化器一起调整学习率等。Keras-Tuner 是一个可帮助您优化神经网络并找到接近最优的超参数集的工具,它利用了高级搜索和优化方法,例如 HyperBand 搜索和贝叶斯优化。所以只需要定义搜索空间,Keras-Tuner 将负责繁琐的调优过程,这要比手动的Grid Search强的多!

加载数据

我们这里使用手语数据集,假设想在图像分类数据集上训练 CNN,我们将使用 KerasTuner 优化神经网络。

首先,使用 pip 安装 Keras-Tuner 库并导入必要的库。

 !pip install keras-tuner

然后导入需要的包:

 import keras_tunerfrom tensorflow import kerasfrom keras import backend as Kfrom tensorflow.keras import layers, lossesimport numpy as npimport matplotlib.pyplot as pltimport os

下面我们就需要加载数据, 我们选择使用美国手语 (ASL) 数据集,该数据集可在 Kaggle 上下载。它包含代表手语的 400x400 RGB 手势图像。它共有 37 个类,每个类有 70 张图像。我们将训练一个 CNN 模型来对这些手势进行分类。

由于数据集已经基于类在文件夹目录进行了分类,加载数据集的最简单方法是使用 keras.utils.image_dataset_from_directory。使用 directory 参数指定父目录路径,并使用 labels=‘inferred’ 自动加载基于文件夹名称的标签。使用 label_mode=‘categorical’ 可以将标签作为 one-hot 向量加载,这样我们加载数据就别的非常简单了。

 BATCH_SIZE = 64train_data = keras.utils.image_dataset_from_directory(directory="../input/asl-dataset/asl_dataset",labels= 'inferred',label_mode='categorical',color_mode='rgb',batch_size=BATCH_SIZE,seed=777,shuffle=True,image_size=(400, 400) )

现在就可以使用下面的函数将 tf.data.dataset 项拆分为 train-val-test 集。我们数使用 0.7–0.15–0.15 拆分规则。

 def split_tf_dataset(ds,  train_split=0.8, val_split=0.1, test_split=0.1, shuffle=True, seed=None, shuffle_size=10000):assert (train_split + test_split + val_split) == 1# get the dataset size (in batches)ds_size = len(ds)if shuffle:# Specify seed to always have the same split distribution between runsds = ds.shuffle(shuffle_size, seed=seed)train_size = int(train_split * ds_size)val_size = int(val_split * ds_size)test_size = int(test_split * ds_size)train_ds = ds.take(train_size)                 # Take train_size number of batchesval_ds = ds.skip(train_size).take(val_size)    # Ignore the first train_size batches and take the rest val_size batchestest_ds = ds.skip(train_size).skip(val_size).take(test_size)return train_ds, val_ds, test_dstrain_data, val_data, test_data = split_tf_dataset(train_data, 0.7, 0.15, 0.15, True, 777)print(f"Train dataset has {sum(1 for _ in train_data.unbatch())} elements")print(f"Val dataset has {sum(1 for _ in val_data.unbatch())} elements")print(f"Test dataset has {sum(1 for _ in test_data.unbatch())} elements")

数据集加载已完成。让我们进入下一部分

Keras Tuner 基础知识

在使用之前,先简单介绍一下 Keras-Tuner 的工作流程。

build()函数接收keras_tuner的Hyperparameter的对象,这个对象定义了模型体系结构和超参数搜索空间。

为了定义搜索空间,hp对象提供了4个方法。hp.Choice(), hp.Int(), hp.Float()和hp.Boolean()。hp.Choice()方法是最通用的,它接受一个由str、int、float或boolean值组成的列表,但所有值的类型必须相同。

 units = hp.Choice(name="neurons", values=[150, 200])units = hp.Int(name="neurons", min=100, max=200, step=10)dropout = hp.Int(name="dropout", min=0.0, max=0.3, step=0.05)shuffle = hp.Boolean("shuffle", default=False)

Keras-Tuner 提供 3 种不同的搜索策略,RandomSearch、贝叶斯优化和 HyperBand。对于所有Tuner都需要指定一个 HyperModel、一个要优化的指标和一个计算预期时间(轮次),以及一个可选的用于保存结果的目录。例如下面的示例

 tuner = keras_tuner.Hyperband(hypermodel=MyHyperModel(),objective = "val_accuracy", #准确率max_epochs=50, #每个模型训练50轮overwrite=True,  directory='hyperband_search_dir', #保存目录project_name='sign_language_cnn')

然后就可以使用命令启动超参数的搜索了。

 tuner.search(x=train_data, max_trials=50,  validation_data=val_data,batch_size=BATCH_SIZE)

以上是Keras Tuner的基本工作流程,现在我们把这个流程应用到我们这个示例中

代码实现

首先,我们定义一个继承自 keras_tuner.HyperModel 的 HyperModel 类,并定义 build 和 fit 方法。

通过 build 方法,定义模型的架构并使用 hp 参数来设置超参数搜索空间。

fit 方法接受 hp 参数、将训练数据 x 传递给 keras model.fit() 方法的 *args 和 **kwargs。**kwargs 需要传递给 model.fit() 因为它包含模型保存的回调和可选的 tensorboard 等回调。

在 HyperModel 类中定义 fit() 方法是因为需要在训练过程中灵活地搜索参数,而不仅仅是在构建过程中。

 class MyHyperModel(keras_tuner.HyperModel) :def build(self, hp, classes=37) : model = keras.Sequential()model.add(layers.Input( (400,400,3)))model.add(layers.Resizing(128, 128, interpolation='bilinear'))# Whether to include normalization layerif hp.Boolean("normalize"):model.add(layers.Normalization())drop_rate = hp.Float("drop_rate", min_value=0.05, max_value=0.25, step=0.10)# Number of Conv Layers is up to tuningfor i in range( hp.Int("num_conv", min_value=7, max_value=8, step=1)) :# Tune hyperparams of each conv layer separately by using f"...{i}"model.add(layers.Conv2D(filters=hp.Int(name=f"filters_{i}", min_value=20, max_value=50, step=15),kernel_size= hp.Int(name=f"kernel_{i}", min_value=5, max_value=7, step=2),strides=1, padding='valid',activation=hp.Choice(name=f"conv_act_{i}", ["relu","leaky_relu", "sigmoid"] )))# Batch Norm and Dropout layers as hyperparameters to be searchedif hp.Boolean("batch_norm"):model.add(layers.BatchNormalization())if hp.Boolean("dropout"):model.add(layers.Dropout(drop_rate))model.add(layers.Flatten())for i in range(hp.Int("num_dense", min_value=1, max_value=2, step=1)) :model.add(layers.Dense(units=hp.Choice("neurons", [150, 200]),activation=hp.Choice("mlp_activ", ['sigmoid', 'relu'])))if hp.Boolean("batch_norm"):model.add(layers.BatchNormalization())if hp.Boolean("dropout"):model.add(layers.Dropout(drop_rate))# Last layermodel.add(layers.Dense(classes, activation='softmax'))# Picking an opimizer and a loss functionmodel.compile(optimizer=hp.Choice('optim',['adam','adamax']),loss=hp.Choice("loss",["categorical_crossentropy","kl_divergence"]),metrics = ['accuracy'])# A way to optimize the learning rate while also trying different optimizerslearning_rate = hp.Choice('lr', [ 0.03, 0.01, 0.003])K.set_value(model.optimizer.learning_rate, learning_rate)return modeldef fit(self, hp, model,x, *args, **kwargs) :return model.fit( x, *args,shuffle=hp.Boolean("shuffle"),**kwargs)

以上网络及参数仅作为示例。可以自定义网络和搜索空间,使其更适合你的应用。让我们详细解释以下代码:

在第 3-5 行中,构建 Keras 模型并添加一个调整大小的层。在第 7-8 行中,使用 hp.Boolean 来评估是否需要添加归一化层,在第 10 行中,为 dropout 定义了不同值。

第 12-17 动态地指定模型应该有多少卷积层,同时为每一层定义不同的超参数空间。将卷积层的数量设置为 7-8,并且在每一层中独立搜索最佳的核数量、内核大小和激活函数。这里是通过使用字符串 name=f”kernel_{i}” 中的索引 i 为循环中的每次迭代使用不同的 name 参数来做到的。这样就有了很大的灵活性,可以极大地扩展搜索空间,但是因为可能的组合可能会变得非常大,需要大量的计算能力。

在循环内使用 name=f”kernel_{i}” 可以为每一层上的每个参数定义不同的搜索空间。

在第 18-22 行中,搜索 conv 块内添加(或不添加)dropout 和批量归一化层。在 28-31 行也做了同样的事情。

在第 24-27 行中,我们添加了一个展平层,然后是可搜索数量的具有不同参数的全连接层,以在每个层中进行优化,类似于第 12-17 行。

在第 36-39 行,对模型进行了编译了,这里优化器也变为了一个可搜索的超参数。因为参数的类型限制所以不能直接传递 keras.optimizer 对象。所以这里将超参数搜索限制为 Keras 字符串别名,例如 keras.optimizers.Adam() -> ‘adam’ 。

如何调整学习率也并不简单。在第 41-43 行,我们以一种“hacky”的方式来做这件事。下面的代码可以更改优化器的超参数,例如学习率这是 keras Tuner 目前做不到的,我们只能手动完成

  lr = hp.Choice('lr', [0.03, 0.01, 0.003])K.set_value(model.optimizer.learning_rate, learning_rate)

在第 48-53 行,定义了模型类的 fit(self, hp, model,x, *args, **kwargs) 方法。将 hp 定义为参数这样可以在训练过程中调整超参数值。例如,我在每个 epoch 之前对使用了训练数据进行重新打乱,等等

在完成上述代码后,可以通过运行以下代码进行测试

 classes = 37hp = keras_tuner.HyperParameters()hypermodel = MyHyperModel()model = hypermodel.build(hp, classes)hypermodel.fit(hp, model, np.random.rand(BATCH_SIZE, 400, 400,3), np.random.rand(BATCH_SIZE, classes))

进行超参数搜索

这里我们使用了贝叶斯优化策略。它是 AutoML 中使用的最佳搜索方法之一。

传递一个模型对象,将目标设置为希望优化的指标(例如“val_accuracy”、“train_loss”),并使用 max_trials 参数和保存模型的路径定义计算预期轮次。

 tuner = keras_tuner.BayesianOptimization(hypermodel=MyHyperModel(),objective = "val_accuracy",max_trials =10, #max candidates to testoverwrite=True,directory='BO_search_dir',project_name='sign_language_cnn')

使用下面的命令Keras-Tuner 就会开始工作了。

 tuner.search(x=train_data, epochs=10,validation_data=val_data)

搜索完成后,可以使用 tuner.results_summary(1) 访问结果。可以看到为每个超参数选择了哪个值,以及在训练期间获得的最佳模型的验证分数。

如果要自动提取和构建最佳的模型,请运行以下代码。

 best_hps = tuner.get_best_hyperparameters(1)h_model = MyHyperModel()model = h_model.build(best_hps[0])

如果您想提取多个模型可以更改 tuner.get_best_hyperparameters(1) 中的数字。

有了模型,我们可以在完整数据集和使用更多 epoch 上训练这个模型。还可以传递回调函数,例如早停、保存最佳模型和学习率调度等等。

 from tensorflow.keras.callbacks import EarlyStopping,ReduceLROnPlateau,ModelCheckpointdef get_callbacks(weights_file, patience, lr_factor):''' Callbacks used for saving the best weights, early stopping and learning rate scheduling.'''return [# Only save the weights that correspond to the maximum validation accuracy.ModelCheckpoint(filepath= weights_file,monitor="val_accuracy",mode="max",save_best_only=True, save_weights_only=True),# If val_loss doesn't improve for a number of epochs set with 'patience' var # training will stop to avoid overfitting.    EarlyStopping(monitor="val_loss",mode="min",patience = patience,verbose=1),# Learning rate is reduced by 'lr_factor' if val_loss stagnates# for a number of epochs set with 'patience/2' var.     ReduceLROnPlateau(monitor="val_loss", mode="min",factor=lr_factor, min_lr=1e-6, patience=patience//2, verbose=1)]history = model.fit(x=train_data, validation_data=val_data, epochs=100,callbacks=get_callbacks('Net_weights.h5',patience=10,lr_factor=0.3))

这样就可以训练出完整的模型,训练完成后还可以绘制图表以进行检查并评估测试数据集,还有就是保存模型。

 model.load_weights(‘Net_weights.h5’)model.evaluate(test_data)model.save(‘Best_model’)

一些小技巧

  • 如果数据集非常大并且搜索时间过长,可以在搜索期间仅使用一小部分进行训练,例如 30%。这通常会在很短的时间内提供类似的结果。然后你再在整个集合上重新训练最好的模型。
  • 为了加快搜索过程的速度,可以减少训练周期数。虽然这样这可能会降低搜索优化的精度,因为这样倾向于早期表现更好的超参数会进一步进步,但是这样做是可以找到时间和结果精度之间的最佳平衡点。
  • 搜索过程中可能出现的一个问题是磁盘空间不足。因为tuner 会自动将所有模型保存在工程目录下,但表现不好的模型不会被动态删除,这将快速占用磁盘空间,尤其是在 Kaggle 或 Google Colab 上运行代码时。开发人员已将其标记为 Keras Tuners 中的增强功能,但还未解决,所以如果磁盘空间不足了,需要考虑限制搜索空间或将搜索拆分为多个较小的搜索。

总结

在本文中我们介绍了 Keras Tuner的使用。并且通过一个完整的项目实现了通过Keras Tuner自动搜索超参数的流程。与手动或网格搜索方法相比,KerasTuner 中实现的搜索策略允许更快、更轻松地进行微调。利用贝叶斯优化或 HyperBand 搜索等搜索方法不仅可以节省时间,还会会得到一个更好的模型。

https://avoid.overfit.cn/post/bbeec4bc93a64a928faabac7f238d7fa

作者:Poulinakis Kon


http://chatgpt.dhexx.cn/article/E5MNpNgN.shtml

相关文章

AI4DB:openGauss人工智能参数调优之X-Tuner

X-Tuner:参数调优与诊断 一、概述 二、使用准备 三、使用示例 四、获取帮助 五、命令参考 六、常见问题处理 一、概述 增量物化视图可以对物化视图增量刷新,需要用户手动执行语句完成对物化视图在一段时间内的增量数据刷新。与全量创建物化视图的不…

5G时代芯片之王——射频芯片

目录 什么是射频芯片? 射频芯片市场 竞争格局 什么是射频芯片? 射频( RF , Radio Frequency) , 表示可以辐射到空间的电磁频率, 频率范围从300kHz~300GHz之间。射频是一种高频交…

Tuner及工作原理介绍

Tuner的介绍 Tuner是什么? 为了提高电视信号的传输效率,减少于扰,电视信号通常都采用射频(RF)信号传输方式,即把要传输的视频或音频信号调制(作幅度调制AM或频率调制FM)到频率较高的射频载波上,从发信端发送出去&…

ADG架构搭建3 -- 一主一备ADG主备切换

本文接该系列文章的上一篇:ADG架构搭建2 – 搭建一主一备ADG架构 前言 Oracle Dataguard的角色转换包含两类:Switchover和Failover。Switchover指主备之间角色转换,主库降为备库,备库升级为主库。而failover则是指主库出现问题时…

RAC+ADG(单节点ADG)

RACADG有两种存储数据文件的形式,一个是将DG的数据文件放在ASM上,另一个是将数据文件放在本地磁盘上 本实验做的是数据放在本地磁盘上。 在搭建RACDG之前,请确保RAC是没有问题的 1.检查集群状态 grid用户下执行: crsctl status r…

ADG架构搭建1--Oracle安装

本系列文章将会带领大家从零完成一个Oracle ADG架构的搭建。 本片文章为系列文章的第一篇。 前言 本文将带领大家完成两台服务的Oracle安装。 正文 版本说明: Oracle版本 Oracle Database 12c Release 1 (12.1.0.2.0) - Enterprise Edition 操作系统版本 Red Hat …

Oracle ADG 与 DG 的区别

首先,DG(Data Guard,数据卫士)不是一个备份恢复的工具,然而,DG却拥有备份的功能,在物理DG下它可以和主库一模一样,但是它存在的目的并不仅仅是为了备份恢复数据,应该说它…

ADG架构搭建5 -- 一主两备ADG主备切换

本文接该系列文章的上一篇:ADG架构搭建4 – 搭建一主两备ADG架构 前言 在《ADG架构搭建3 – 一主一备ADG主备切换》一文中,已经带领大家完成了一主一备ADG的主备切换,相信大家对该部分内容已有所了解。本文将对《ADG架构搭建4 – 搭建一主两…

第一章 ADG基本知识

Oracle Data Guard 配置可以包含一个主数据库和多达 30 个目标。 备用数据库是主数据库的事务一致副本。 当主数据库是 CDB 时,代理配置中的所有备用数据库也必须是 CDB。 备用数据库的类型如下: 物理备用数据库 提供物理上相同的主数据库副本&#xf…

完成端口使用总结

前言 本文不是全面介绍完成端口的,只是简单介绍了一下完成端口和几个常用概念。本文主要关注完成端口关闭时资源释放问题。 基础介绍 完成端口——可能是Win32下最复杂的一种I/O模型,Win32下最复杂的内核对象。它通过指定数量的线程对重叠I/O请求进行管理,以便为已经完成的…

WinSock完成端口I/O模型

关于重叠I/O,参考《WinSock重叠I/O模型》;关于完成端口的概念及内部机制,参考译文《深度探索I/O完成端口》。 完成端口对象取代了 WSAAsyncSelect 中的消息驱动和 WSAEventSelect 中的事件对象,当然完成端口模型的内部机制要比 WS…

Windows 完成端口编程

Windows 完成端口编程 本文为转载, 原文地址: http://xingzhesun.blogbus.com/logs/3925649.html Table of Contents 1 基本概念 2 OVERLAPPED数据结构 3 完成端口的内部机制 3.1 创建完成端口 3.2 完成端口线程的工作原理 3.3 线程间数据传递 3.4 线程的安全退出 1 基本…

完成端口IOCP详解

本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何提笔,所以这篇文档总是在酝酿之中……酝酿了两年之后,终于决定开始动笔了,但愿还不算晚….. 这篇文档我非常详细并且图文并茂的介绍了…

Win socket编程--IOCP完成端口模型

引言 要想编写一个高性能的服务器应用程序,必须实现一个高效的线程模型。让太少或者太多的服务器线程来处理客户的请求,都可能导致性能问题。例如,如果一个服务器创建单个线程来处理所有的请求,那么客户端可能长期等待而得不到响…

深度探索I/O完成端口

引言 要想编写一个高性能的服务器应用程序,必须实现一个高效的线程模型。让太少或者太多的服务器线程来处理客户的请求,都可能导致性能问题。例如,如果一个服务器创建单个线程来处理所有的请求,那么客户端可能长期等待而得不到响…

Windows中I/O完成端口机制详解

Windows中I/O完成端口机制详解 引言 要想编写一个高性能的服务器应用程序,必须实现一个高效的线程模型。让太少或者太多的服务器线程来处理客户的请求,都可能导致性能问题。例如,如果一个服务器创建单个线程来处理所有的请求,那么…

c++使用完成端口实现服务器的高性能并发

如何使用c,借助完成端口完成大并发服务器的搭建,是今天要讨论的问题,套路如下: 套路总结一下: 创建完成端口 依据CPU核数创建一定数量的线程 线程中不断调用GetQueuedCompletionStatus检查完成端口状态,分别给予处理 创建一个…

C#高性能大容量SOCKET并发完成端口例子(有C#客户端)完整实例源码

遥望星空 好好干,有前途! 博客园首页新随笔联系管理订阅 随笔- 1082 文章- 0 评论- 151 C#高性能大容量SOCKET并发(转) C#高性能大容量SOCKET并发(零):代码结构说明 C#高性能大容量SOCKET并发(一…

完成端口学习笔记(一):完成端口+控制台 实现文件拷贝

最近在整理手里一个项目的后台服务端归档程序,重新梳理了一下有关“完成端口”的知识,发现还是有很多模棱两可的地方,下面记录一下再次学习的点滴,该篇博文还会有后续的补充章节,不知道什么时间会再补充^_^。 IO概念 还…

Socket编程模型之完成端口模型

转载请注明来源:http://blog.csdn.net/caoshiying?viewmodecontents 一、回顾重叠IO模型 用完成例程来实现重叠I/O比用事件通知简单得多。在这个模型中,主线程只用不停的接受连接即可;辅助线程判断有没有新的客户端连接被建立,如…