使用 Keras Tuner 进行超参数调优
2020 年 1 月 29 日
作者:Tom O'Malley

机器学习项目的成功通常极大地取决于选择合适的超参数。随着机器学习领域的不断成熟,依靠试错来寻找这些参数的最佳值(也称为“研究生下降”)已经不再可扩展。事实上,许多当今最先进的结果,例如 EfficientNet,都是通过复杂的超参数优化算法发现的。

Keras Tuner 是一款易于使用且可分布的超参数优化框架,可以解决执行超参数搜索时的痛点。Keras Tuner 使得定义搜索空间并利用包含的算法来寻找最佳超参数值变得轻而易举。Keras Tuner 内置了贝叶斯优化、Hyperband 和随机搜索算法,并且设计为易于研究人员扩展,以便尝试新的搜索算法。
Keras Tuner 的实际应用。您可以在下面找到完整的代码。
以下是一个简单的端到端示例。首先,我们定义一个模型构建函数。它接受一个 hp 参数,您可以从中采样超参数,例如 hp.Int('units', min_value=32, max_value=512, step=32)(来自特定范围的整数)。请注意,超参数可以在模型构建代码中内联定义。以下示例创建了一个简单的可调模型,我们将对其进行训练,以在 CIFAR-10 上进行训练。
import tensorflow as tf

def build_model(hp):
  inputs = tf.keras.Input(shape=(32, 32, 3))
  x = inputs
  for i in range(hp.Int('conv_blocks', 3, 5, default=3)):
    filters = hp.Int('filters_' + str(i), 32, 256, step=32)
    for _ in range(2):
      x = tf.keras.layers.Convolution2D(
        filters, kernel_size=(3, 3), padding='same')(x)
      x = tf.keras.layers.BatchNormalization()(x)
      x = tf.keras.layers.ReLU()(x)
    if hp.Choice('pooling_' + str(i), ['avg', 'max']) == 'max':
      x = tf.keras.layers.MaxPool2D()(x)
    else:
      x = tf.keras.layers.AvgPool2D()(x)
  x = tf.keras.layers.GlobalAvgPool2D()(x)
  x = tf.keras.layers.Dense(
      hp.Int('hidden_size', 30, 100, step=10, default=50),
      activation='relu')(x)
  x = tf.keras.layers.Dropout(
      hp.Float('dropout', 0, 0.5, step=0.1, default=0.5))(x)
  outputs = tf.keras.layers.Dense(10, activation='softmax')(x)

  model = tf.keras.Model(inputs, outputs)
  model.compile(
    optimizer=tf.keras.optimizers.Adam(
      hp.Float('learning_rate', 1e-4, 1e-2, sampling='log')),
    loss='sparse_categorical_crossentropy', 
    metrics=['accuracy'])
  return model
接下来,实例化一个调优器。您应该指定模型构建函数以及要优化的目标的名称(对于内置指标,是否要最小化或最大化会自动推断 - 对于自定义指标,您可以通过 kerastuner.Objective 类指定)。在本例中,Keras Tuner 将使用 Hyperband 算法进行超参数搜索。
import kerastuner as kt

tuner = kt.Hyperband(
    build_model,
    objective='val_accuracy',
    max_epochs=30,
    hyperband_iterations=2)
接下来,我们将使用 TensorFlow 数据集 下载 CIFAR-10 数据集,然后开始超参数搜索。要开始搜索,请调用 search 方法。此方法与 keras.Model.fit 的签名相同。
import tensorflow_datasets as tfds

data = tfds.load('cifar10')
train_ds, test_ds = data['train'], data['test']

def standardize_record(record):
  return tf.cast(record['image'], tf.float32) / 255., record['label']

train_ds = train_ds.map(standardize_record).cache().batch(64).shuffle(10000)
test_ds = test_ds.map(standardize_record).cache().batch(64)

tuner.search(train_ds,
             validation_data=test_ds,
             epochs=30,
             callbacks=[tf.keras.callbacks.EarlyStopping(patience=1)])
每个模型最多会训练 30 个 epoch,并且将运行 Hyperband 算法的两个迭代。之后,您可以使用 get_best_models 函数检索搜索过程中找到的最佳模型。
best_model = tuner.get_best_models(1)[0]
您还可以查看搜索找到的最佳超参数值。

best_hyperparameters = tuner.get_best_hyperparameters(1)[0]
这就是执行复杂超参数搜索所需的全部代码!

您可以在 此处 找到上述示例的完整代码。

内置可调模型

除了允许您定义自己的可调模型外,Keras Tuner 还提供两种内置可调模型:HyperResnet 和 HyperXception。这些模型分别搜索 ResNet 和 Xception 架构的各种排列。这些模型可以像这样与 Tuner 一起使用。
tuner = kt.tuners.BayesianOptimization(
  kt.applications.HyperResNet(input_shape=(256, 256, 3), classes=10),
  objective='val_accuracy',
  max_trials=50)

分布式调优

使用 Keras Tuner,您可以进行数据并行和试用并行分布。也就是说,您可以使用 tf.distribute.Strategy 在多个 GPU 上运行每个模型,也可以在不同的工作程序上并行搜索多个不同的超参数组合。

要执行试用并行搜索,无需进行任何代码更改。只需设置 KERASTUNER_TUNER_IDKERASTUNER_ORACLE_IPKERASTUNER_ORACLE_PORT 环境变量,例如,如以下 bash 脚本所示。
export KERASTUNER_TUNER_ID="chief"
export KERASTUNER_ORACLE_IP="127.0.0.1"
export KERASTUNER_ORACLE_PORT="8000"
python run_my_search.py
调优器通过一个中央 Oracle 服务协调它们的搜索,该服务告诉每个调优器接下来尝试哪些超参数值。有关更多信息,请参阅我们的 分布式调优指南

自定义训练循环

可以对 kerastuner.Tuner 类进行子类化以支持高级用例,例如
  • 自定义训练循环(GAN、强化学习等)
  • 在模型构建函数之外添加超参数(预处理、数据增强、测试时增强等)
以下是一个简单的示例。
class MyTuner(kt.Tuner):

    def run_trial(self, trial, ...):
        model = self.hypermodel.build(trial.hyperparameters)
        score = …  # Run the training loop and return the result.
        self.oracle.update_trial(trial.trial_id, {'score': score})
        self.oracle.save_model(trial.trial_id, model)
有关更多信息,请参阅我们的 调优器子类化指南

调优 Scikit-learn 模型

尽管名称如此,但 Keras Tuner 可用于调优各种机器学习模型。除了针对 Keras 模型的内置调优器外,Keras Tuner 还提供了一个针对 Scikit-learn 模型的内置调优器。以下是如何使用此调优器的简单示例。
from sklearn import ensemble
from sklearn import linear_model

def build_model(hp):
    model_type = hp.Choice('model_type', ['random_forest', 'ridge'])
    if model_type == 'random_forest':
        with hp.conditional_scope('model_type', 'random_forest'):
            model = ensemble.RandomForestClassifier(
                n_estimators=hp.Int('n_estimators', 10, 50, step=10),
                max_depth=hp.Int('max_depth', 3, 10))
    elif model_type == 'ridge':
        with hp.conditional_scope('model_type', 'ridge'):
            model = linear_model.RidgeClassifier(
                alpha=hp.Float('alpha', 1e-3, 1, sampling='log'))
    else:
        raise ValueError('Unrecognized model_type')
    return model

tuner = kt.tuners.Sklearn(
        oracle=kt.oracles.BayesianOptimization(
            objective=kt.Objective('score', 'max'),
            max_trials=10),
        hypermodel=build_model,
        directory=tmp_dir)
X, y = ...
tuner.search(X, y)
有关 Keras Tuner 的更多信息,请参阅 Keras Tuner 网站Keras Tuner GitHub。Keras Tuner 是一个完全在 GitHub 上开发的开源项目。如果您想在 Keras Tuner 中看到的功能,请在 GitHub 上打开一个带有功能请求的问题,如果您有兴趣贡献,请查看我们的 贡献指南 并向我们发送 PR!
下一篇文章
Hyperparameter tuning with Keras Tuner

作者:Tom O'Malley

机器学习项目的成功通常极大地取决于选择合适的超参数。随着机器学习领域的不断成熟,依靠试错来寻找这些参数的最佳值(也称为“研究生下降”)已经不再可扩展。事实上,许多当今最先进的结果,例如 EfficientNet,都是通过复杂的超参数优化算法发现的…