在本文中,咱们将深入研究超参数优化。
为了不便起见本文将应用 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'])
基线模型 蕴含卷积和池化层。对于调优,咱们将测试以下内容:
- 卷积、MaxPooling 和 Dropout 层的“块”数
- 每个块中 Conv 层的过滤器大小:32、64
- 转换层上的无效或雷同填充
- 最初一个额定层的暗藏层大小:25-150,乘以 25
- 优化器:SGD(nesterov=True,动量=0.9)或 Adam
- 学习率: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 模型测试精度相比:
- 基线 CNN 模型:90.8 %
- 最佳 CNN 模型:92%
咱们看到优化模型的性能晋升!
除了准确性之外,咱们还能够看到优化的成果很好,因为:
在每种状况下都抉择了一个非零的 Dropout 值,即便咱们也提供了零 Dropout。这是意料之中的,因为 Dropout 是一种缩小过拟合的机制。乏味的是,最好的 CNN 架构是规范CNN,其中过滤器的数量在每一层中逐步减少。这是意料之中的,因为随着后续层的减少,模式变得更加简单(这也是咱们在学习各种模型和论文时被证实的后果)须要更多的过滤器能力捕捉这些模式组合。
以上例子也阐明Keras Tuner 是应用 Tensorflow 优化深度神经网络的很好用的工具。
咱们下面也说了本文抉择是贝叶斯优化器。然而还有两个其余的选项:
RandomSearch:随机抉择其中的一些来防止摸索超参数的整个搜寻空间。然而,它不能保障会找到最佳超参数
Hyperband:抉择一些超参数的随机组合,并仅应用它们来训练模型几个 epoch。而后应用这些超参数来训练模型,直到用尽所有 epoch 并从中抉择最好的。
最初数据集地址和keras_tuner的文档如下
https://avoid.overfit.cn/post/c3f904fab4f84914b8a1935f8670582f
作者:Nikos Kafritsas