在本文中,咱们将深入研究超参数优化。

为了不便起见本文将应用 Tensorflow 中蕴含的 Fashion MNIST[1] 数据集。该数据集在训练集中蕴含 60,000 张灰度图像,在测试集中蕴含 10,000 张图像。每张图片代表属于 10 个类别之一的单品(“T 恤/上衣”、“裤子”、“套头衫”等)。因而这是一个多类分类问题。

这里简略介绍筹备数据集的步骤,因为本文的次要内容是超参数的优化,所以这部分只是简略介绍流程,个别状况下,流程如下:

  • 加载数据。
  • 分为训练集、验证集和测试集。
  • 将像素值从 0–255 标准化到 0–1 范畴。
  • One-hot 编码指标变量。
#load data(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()# split into train, validation and test setstrain_x, val_x, train_y, val_y = train_test_split(train_images, train_labels, stratify=train_labels, random_state=48, test_size=0.05)(test_x, test_y)=(test_images, test_labels)# normalize pixels to range 0-1train_x = train_x / 255.0val_x = val_x / 255.0test_x = test_x / 255.0#one-hot encode target variabletrain_y = to_categorical(train_y)val_y = to_categorical(val_y)test_y = to_categorical(test_y)

咱们所有训练、验证和测试集的形态是:

print(train_x.shape)  #(57000, 28, 28)print(train_y.shape)  #(57000, 10)print(val_x.shape)    #(3000, 28, 28)print(val_y.shape)    #(3000, 10)print(test_x.shape)   #(10000, 28, 28)print(test_y.shape)   #(10000, 10)

当初,咱们将应用 Keras Tuner 库 [2]:它将帮忙咱们轻松调整神经网络的超参数:

pip install keras-tuner

Keras Tuner 须要 Python 3.6+ 和 TensorFlow 2.0+

超参数调整是机器学习我的项目的根底局部。有两种类型的超参数:

  • 构造超参数:定义模型的整体架构(例如暗藏单元的数量、层数)
  • 优化器超参数:影响训练速度和品质的参数(例如学习率和优化器类型、批量大小、轮次数等)

为什么须要超参数调优库?咱们不能尝试所有可能的组合,看看验证集上什么是最好的吗?

这必定是不行的因为深度神经网络须要大量工夫来训练,甚至几天。如果在云服务器上训练大型模型,那么每个试验试验都须要花很多的钱。

因而,须要一种限度超参数搜寻空间的剪枝策略。

keras-tuner提供了贝叶斯优化器。它搜寻每个可能的组合,而是随机抉择前几个。而后依据这些超参数的性能,抉择下一个可能的最佳值。因而每个超参数的抉择都取决于之前的尝试。依据历史记录抉择下一组超参数并评估性能,直到找到最佳组合或达到最大试验次数。咱们能够应用参数“max_trials”来配置它。

除了贝叶斯优化器之外,keras-tuner还提供了另外两个常见的办法:RandomSearch 和 Hyperband。咱们将在本文开端探讨它们。

接下来就是对咱们的网络应用超参数调整。咱们尝试两种网络架构,规范多层感知器(MLP)和卷积神经网络(CNN)。

首先让咱们看看基线 MLP 模型是什么:

model_mlp = Sequential()model_mlp.add(Flatten(input_shape=(28, 28)))model_mlp.add(Dense(350, activation='relu'))model_mlp.add(Dense(10, activation='softmax'))print(model_mlp.summary())model_mlp.compile(optimizer="adam",loss='categorical_crossentropy')

调优过程须要两种次要办法:

hp.Int():设置超参数的范畴,其值为整数 - 例如,密集层中暗藏单元的数量:

model.add(Dense(units = hp.Int('dense-bot', min_value=50, max_value=350, step=50))

hp.Choice():为超参数提供一组值——例如,Adam 或 SGD 作为最佳优化器?

hp_optimizer=hp.Choice('Optimizer', values=['Adam', 'SGD'])

在咱们的 MLP 示例中,咱们测试了以下超参数:

  • 暗藏层数:1-3
  • 第一密集层大小:50–350
  • 第二和第三密集层大小:50–350
  • Dropout:0、0.1、0.2
  • 优化器:SGD(nesterov=True,momentum=0.9) 或 Adam
  • 学习率:0.1、0.01、0.001

代码如下:

model = Sequential()model.add(Dense(units = hp.Int('dense-bot', min_value=50, max_value=350, step=50), input_shape=(784,), activation='relu'))for i in range(hp.Int('num_dense_layers', 1, 2)):  model.add(Dense(units=hp.Int('dense_' + str(i), min_value=50, max_value=100, step=25), activation='relu'))  model.add(Dropout(hp.Choice('dropout_'+ str(i), values=[0.0, 0.1, 0.2])))model.add(Dense(10,activation="softmax"))hp_optimizer=hp.Choice('Optimizer', values=['Adam', 'SGD'])if hp_optimizer == 'Adam':    hp_learning_rate = hp.Choice('learning_rate', values=[1e-1, 1e-2, 1e-3])elif hp_optimizer == 'SGD':    hp_learning_rate = hp.Choice('learning_rate', values=[1e-1, 1e-2, 1e-3])    nesterov=True    momentum=0.9

这里须要留神第 5 行的 for 循环:让模型决定网络的深度!

最初,就是运行了。请留神咱们之前提到的 max_trials 参数。

model.compile(optimizer = hp_optimizer, loss='categorical_crossentropy', metrics=['accuracy'])tuner_mlp = kt.tuners.BayesianOptimization(    model,    seed=random_seed,    objective='val_loss',    max_trials=30,    directory='.',    project_name='tuning-mlp')tuner_mlp.search(train_x, train_y, epochs=50, batch_size=32, validation_data=(dev_x, dev_y), callbacks=callback)

咱们失去后果

这个过程用尽了迭代次数,大概须要 1 小时能力实现。咱们还能够应用以下命令打印模型的最佳超参数:

best_mlp_hyperparameters = tuner_mlp.get_best_hyperparameters(1)[0]print("Best Hyper-parameters")best_mlp_hyperparameters.values

当初咱们能够应用最优超参数从新训练咱们的模型:

model_mlp = Sequential()model_mlp.add(Dense(best_mlp_hyperparameters['dense-bot'], input_shape=(784,), activation='relu'))for i in range(best_mlp_hyperparameters['num_dense_layers']):  model_mlp.add(Dense(units=best_mlp_hyperparameters['dense_' +str(i)], activation='relu'))  model_mlp.add(Dropout(rate=best_mlp_hyperparameters['dropout_' +str(i)]))  model_mlp.add(Dense(10,activation="softmax"))model_mlp.compile(optimizer=best_mlp_hyperparameters['Optimizer'], loss='categorical_crossentropy',metrics=['accuracy'])history_mlp= model_mlp.fit(train_x, train_y, epochs=100, batch_size=32, validation_data=(dev_x, dev_y), callbacks=callback)

或者,咱们能够用这些参数从新训练咱们的模型:

model_mlp=tuner_mlp.hypermodel.build(best_mlp_hyperparameters)history_mlp=model_mlp.fit(train_x, train_y, epochs=100, batch_size=32,                            validation_data=(dev_x, dev_y), callbacks=callback)

而后测试准确率

mlp_test_loss, mlp_test_acc = model_mlp.evaluate(test_x,  test_y, verbose=2)print('\nTest accuracy:', mlp_test_acc)# Test accuracy: 0.8823

与基线的模型测试精度相比:

基线 MLP 模型:86.6 %最佳 MLP 模型:88.2 %。测试准确度的差别约为 3%!

上面咱们应用雷同的流程,将MLP改为CNN,这样能够测试更多参数。

首先,这是咱们的基线模型:

model_cnn = Sequential()model_cnn.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))model_cnn.add(MaxPooling2D((2, 2)))model_cnn.add(Flatten())model_cnn.add(Dense(100, activation='relu'))model_cnn.add(Dense(10, activation='softmax'))model_cnn.compile(optimizer="adam", loss='categorical_crossentropy', metrics=['accuracy'])

基线模型 蕴含卷积和池化层。对于调优,咱们将测试以下内容:

  1. 卷积、MaxPooling 和 Dropout 层的“块”数
  2. 每个块中 Conv 层的过滤器大小:32、64
  3. 转换层上的无效或雷同填充
  4. 最初一个额定层的暗藏层大小:25-150,乘以 25
  5. 优化器:SGD(nesterov=True,动量=0.9)或 Adam
  6. 学习率:0.01、0.001
model = Sequential()model = Sequential()model.add(Input(shape=(28, 28, 1)))for i in range(hp.Int('num_blocks', 1, 2)):    hp_padding=hp.Choice('padding_'+ str(i), values=['valid', 'same'])    hp_filters=hp.Choice('filters_'+ str(i), values=[32, 64])    model.add(Conv2D(hp_filters, (3, 3), padding=hp_padding, activation='relu', kernel_initializer='he_uniform', input_shape=(28, 28, 1)))    model.add(MaxPooling2D((2, 2)))    model.add(Dropout(hp.Choice('dropout_'+ str(i), values=[0.0, 0.1, 0.2])))model.add(Flatten())hp_units = hp.Int('units', min_value=25, max_value=150, step=25)model.add(Dense(hp_units, activation='relu', kernel_initializer='he_uniform'))model.add(Dense(10,activation="softmax"))hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3])hp_optimizer=hp.Choice('Optimizer', values=['Adam', 'SGD'])if hp_optimizer == 'Adam':    hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3])elif hp_optimizer == 'SGD':    hp_learning_rate = hp.Choice('learning_rate', values=[1e-2, 1e-3])    nesterov=True    momentum=0.9

像以前一样,咱们让网络决定它的深度。最大迭代次数设置为 100:

model.compile( optimizer=hp_optimizer,loss='categorical_crossentropy', metrics=['accuracy'])tuner_cnn = kt.tuners.BayesianOptimization(    model,    objective='val_loss',    max_trials=100,    directory='.',    project_name='tuning-cnn')

后果如下:

失去的超参数

最初应用最佳超参数训练咱们的 CNN 模型:

model_cnn = Sequential()model_cnn.add(Input(shape=(28, 28, 1)))for i in range(best_cnn_hyperparameters['num_blocks']):  hp_padding=best_cnn_hyperparameters['padding_'+ str(i)]  hp_filters=best_cnn_hyperparameters['filters_'+ str(i)]  model_cnn.add(Conv2D(hp_filters, (3, 3), padding=hp_padding, activation='relu', kernel_initializer='he_uniform', input_shape=(28, 28, 1)))  model_cnn.add(MaxPooling2D((2, 2)))  model_cnn.add(Dropout(best_cnn_hyperparameters['dropout_'+ str(i)]))model_cnn.add(Flatten())model_cnn.add(Dense(best_cnn_hyperparameters['units'], activation='relu', kernel_initializer='he_uniform'))model_cnn.add(Dense(10,activation="softmax"))model_cnn.compile(optimizer=best_cnn_hyperparameters['Optimizer'],                                                  loss='categorical_crossentropy',                                                  metrics=['accuracy'])print(model_cnn.summary())history_cnn= model_cnn.fit(train_x, train_y, epochs=50, batch_size=32, validation_data=(dev_x, dev_y), callbacks=callback)

查看测试集的准确率:

cnn_test_loss, cnn_test_acc = model_cnn.evaluate(test_x,  test_y, verbose=2)print('\nTest accuracy:', cnn_test_acc)# Test accuracy: 0.92

与基线的 CNN 模型测试精度相比:

  1. 基线 CNN 模型:90.8 %
  2. 最佳 CNN 模型:92%

咱们看到优化模型的性能晋升!

除了准确性之外,咱们还能够看到优化的成果很好,因为:

在每种状况下都抉择了一个非零的 Dropout 值,即便咱们也提供了零 Dropout。这是意料之中的,因为 Dropout 是一种缩小过拟合的机制。乏味的是,最好的 CNN 架构是规范CNN,其中过滤器的数量在每一层中逐步减少。这是意料之中的,因为随着后续层的减少,模式变得更加简单(这也是咱们在学习各种模型和论文时被证实的后果)须要更多的过滤器能力捕捉这些模式组合。

以上例子也阐明Keras Tuner 是应用 Tensorflow 优化深度神经网络的很好用的工具。

咱们下面也说了本文抉择是贝叶斯优化器。然而还有两个其余的选项:

RandomSearch:随机抉择其中的一些来防止摸索超参数的整个搜寻空间。然而,它不能保障会找到最佳超参数

Hyperband:抉择一些超参数的随机组合,并仅应用它们来训练模型几个 epoch。而后应用这些超参数来训练模型,直到用尽所有 epoch 并从中抉择最好的。

最初数据集地址和keras_tuner的文档如下

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

作者:Nikos Kafritsas