优秀的编程知识分享平台

网站首页 > 技术文章 正文

在Fashion-MNIST上使用Hyperas进行密集网络的超参数选择和调整

nanyue 2024-10-10 07:29:30 技术文章 11 ℃

这里显示的所有结果都是对Fashion-MNIST数据进行的各种实验的结果。更多的数据可以从https://www.kaggle.com/zalando-research/fashionmnist, https://github.com/zalandoresearch/fashionmnist了解。数据的重要细节如下:-。

  • -包含28x28件衣服图片。

  • -在训练集中包含6万张图像,在测试集中包含10000张图片。

  • -我们的任务是把图片分成10个不同的类,比如t恤/上衣,裤子,套头衫,衣服等等。

必要Libraires有:

  • - TensorFlow

  • - Keras

  • - Numpy

  • - Hyperas, Hyperopt

  • - Matplotlib

本文中的程序将从一个基本的基线模型开始,找出我们可以调整的超参数以及它们将使我们的体系结构更有效的效果,以及最后如何调整超参数,以便获得最佳体系结构,以便在体系结构中实现最佳结果。

超参数是在学习过程开始之前设置的可配置值。这些超参数值决定了训练算法的行为以及它如何从数据中学习参数。

选择正确的度量

在我们开始之前,我们需要清楚地了解我们的问题,然后选择一个指标来衡量我们的模型的性能以及我们打算进行优化的损失函数。我们的问题是一个普通的图像分类任务,我们选择精度作为度量和分类交叉熵作为我们的损失函数

数据点在所有类中的分布

从图中可以推断,数据集是平衡的,因为我们在每个类中都有相同数量的数据点。此选项对于选择精度作为度量标准是必需的。其他选择可能是F1分数。

记录丢失被选择为最小化它不仅有助于最小化不正确的分类,而且还确保我们以高概率预测正确的类别。

预处理数据

我们从keras.datasets加载数据

from keras.datasets import fashion_mnist

(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()

我们只提供一个训练和测试集,因为我们还需要一个验证集,我们使用scikit-learn的训练测试拆分将训练数据拆分为80%训练和20%验证

from sklearn.model_selection import train_test_split

X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=12345)

因为我们首先要用密集的神经网络来工作,所以在我们将它们输入到网络之前,我们需要对数据进行一些预处理。这包括: -

  • - 改变训练的shape ,验证和测试数据从28x28格式到784值列表

  • - 规范我们的输入,使输入值范围从0到1而不是0到255,也可以完成标准化

X_train = X_train.reshape(48000, 784)

X_val = X_val.reshape(12000, 784)

X_test = X_test.reshape(10000, 784)

X_train = X_train.astype(‘float32’)

X_val = X_val.astype(‘float32’)

X_test = X_test.astype(‘float32’)

X_train /= 255

X_val /= 255

X_test/= 255

此外,因为我们已经使用了分类交叉熵,我们需要将我们的输出转换为One-hot编码形式,这可以很容易地完成,如下所示

# np is numpy

nb_classes = 10

Y_train = np_utils.to_categorical(y_train,nb_classes)

Y_val = np_utils.to_categorical(y_val,nb_classes)

Y_test = np_utils.to_categorical(y_test,nb_classes)

基本模型

首先从一个基本的模型开始,然后不断尝试改进它,这是一个很好的实践。我们的基线模型是一个softmax-classifier,它将作为输入,列出像素值,并输出class-label。

model = Sequential([

Dense(10,input_shape=(784,),activation=’softmax’)

])

model.compile(optimizer=keras.optimizers.SGD(lr=0.1),

loss=’categorical_crossentropy’,

metrics=[‘accuracy’])

对于上述模型,损失和精度值如下所示。

Train loss: 0.4080537739445766

Train accuracy: 0.8587291666666667

-------------------------------

Validation loss: 0.4190353289047877

Validation accuracy: 0.8551666666666666

-------------------------------

Test loss: 0.4566255069732666

Test accuracy: 0.8375

为了进一步改进模型,我们需要知道哪些是我们可以在密集网络中调节的超参数,接下来的部分将给您一个关于超参数的简要概述,以及它们如何影响学习,但在此之前让我们理解为什么我们需要超参数调整。

需要进行超参数调整

  • 1、为了找到偏差和方差之间的正确平衡: - 使用密集神经网络训练我们的数据时,获得非常高的准确度非常容易,但是如果我们尝试和禁止我们使用密集神经网络,它们可能不会很好地推广到我们的验证和测试集深层/复杂的体系结构总是有可能导致我们的数据集的准确性较低,因此我们需要找到一个通用性好,准确度高的甜点。每个超参数都会影响偏差 - 方差。

  • 2、为了防止自己陷入消失/爆炸梯度问题: - 后向传播步骤可能涉及多个梯度值的乘法,如果梯度值较小(<1),我们可能遭受消失梯度问题,如果它们是大(> 1)我们可能会遭受爆炸式梯度问题的困扰。我们可以通过在学习速率,激活函数和层数的超参数调整时进行必要的调整来避免这种情况

  • 3、遇到鞍点和局部最优值: - 有些模型可能会陷入鞍点和局部最小值,其中梯度几乎为零,因此我们需要调整超参数(如学习速率),并将优化器更改为Adam或RMSProp以不卡住并停止进一步学习。

优化器遇到鞍点

  • 4、没有收敛: - 众所周知,较大的学习率可以通过在最小值附近徘徊来防止收敛到最小值,但同时如果学习速率太慢,我们可能无法在最小值处收敛为我们采取了非常小的步骤。为了克服这个问题,我们可以选择像Adam这样的自适应学习速率优化器,或者在优化器中使用衰减。

学习率的影响

  • 5、Sigmoid和tanh激活函数问题: - sigmoid和tanh函数在它们的末端弯曲,导致这些点的梯度非常低,因为即使在应用sigmoid激活之前的值显着增加,应用后的值也不会因此即使是渐变也不会增加太多的影响学习。但Sigmoid和tanh可以作为浅层网络(最多2层)的不错选择。

  • 6、加速学习: - 通过获得正确的超参数集,与更复杂并花费更多时间训练的模型相比,我们可以获得更好的结果。

  • 7、所有超参数的超组合: - 超参数和它们可以采用的值的数量在深度学习中相当高,并且当它们共同计算可能的组合时,我们可以轻松地竞赛到一个非常大的和令人畏惧的数字。因此,本文将试着给你提供一些技巧和诀窍,利用我在训练深度神经架构方面的经验来处理它。

  • 8、从更小的角度实现更多: - 更多的组件和复杂的体系结构可能并不总是获得更低的损失和更高的精度的正确途径,而是确保您充分利用当前的体系结构,并在需要时缓慢增加复杂性和组件。

超参数:密集网络

  1. 层数: - 必须明智选择,因为数量可能会引入过度拟合,消失和爆炸梯度问题等问题,较低的数字可能会导致模型具有较高的偏差和较低的潜在模型。取决于用于训练的数据大小

  2. 每层隐藏单元的数量: - 这些也必须合理选择以找到高偏差和差异之间的最佳点。再次取决于用于训练的数据大小

  3. 激活函数: - 这里的流行选择是ReLU,Sigmoid&Tanh(仅适用于浅层网络)和LeakyReLU。通常选择ReLU / LeakyReLU同样适用。Sigmoid / Tanh对于浅层网络可能表现不错。

  4. 优化器: - 这是模型在每次迭代后更新每个图层权重的算法。受欢迎的选择是SGD,RMSProp和Adam。SGD适用于浅层网络,但在这种情况下无法避开鞍点和局部最小值,RMSProp可能是更好的选择,AdaDelta / AdaGrad用于稀疏数据,而Adam则是最受欢迎的,可用于实现更快的收敛。

  5. 学习率: - 它负责核心学习的特点,必须选择的方式不会太高,因为我们无法收敛到最低限度,也不能太低,以至于我们无法加快学习进程。建议尝试10的幂,特别是0.001,0.01,0.1,1。学习速率的价值取决于所使用的优化器。对于SGD - 0.1通常效果很好,而对于Adam - 0.001 / 0.01,但建议始终尝试上述范围内的所有值。您也可以使用衰减参数,通过迭代次数来减少您的学习,以实现收敛。一般而言,使用像Adam这样的自适应学习速率算法比使用衰减学习速率更好。

  6. 初始化: - 由于默认值工作得并不好,但仍然优先使用He-normal / uniform初始化,同时对Sigmoid使用ReLUs和Glorot-normal / uniform(默认为Glorot-uniform)以获得更好的结果。必须避免使用零或任何恒定值(所有单位相同)权重初始化

  7. 批量大小: - 它表示在更新批量大小之前向网络显示的模式数量。如果批量大小较小,模式的重复性就会降低,因此权重会遍布整个地方,并且收敛会变得困难。如果批量大,学习速度会变慢,因为只有经过多次迭代后批量才会变化。建议根据数据大小尝试批量大小为2的幂(以便更好地优化内存)。

  8. Epochs的数量:- Epochs的数量是整个训练数据显示给模型的次数。它在模型拟合训练数据的程度上起着重要的作用。大量的epochs可能过于适合数据,在测试和验证集上可能存在泛化问题,也可能导致消失和爆炸梯度问题。较低的epochs可能限制模型的潜力。根据您所拥有的时间和计算资源,尝试不同的值。

  9. Dropout: - 它可以被认为是一种调节者,帮助我们找到最佳的偏差变异点。它通过在每次迭代中删除某些连接来实现,因此隐藏单元不能依赖任何特定的功能。它可以采取的值可以是0-1之间的任何值,并且仅基于模型过度拟合的数量。

  10. L1 / L2正则化: - 作为另一个正则化器,其中非常高的权重值被抑制,从而模型不依赖于单个特征。这通常会降低差异,同时增加偏差,即降低准确度。即使在显着增加Dropout值之后,模型仍然过度贴合时应该使用

  11. 批量标准化: - 通常在深度神经网络体系结构中,经过中间层的各种调整之后,标准化输入变得太大或太小,而它到达远处的层,这引起内部协变量转变的问题,这会影响学习来解决这个问题。添加一个批处理规范化层以标准化(平均居中和方差缩放)输入。这个图层必须在通过包含激活函数的图层并在Dropout图层(如果有)之前放置在架构中。

简介Hyperas

既然我们已经理解了超参数调整的需要,并且在调整我们密集的网络时已经看到了超参数的考虑,现在我们继续讨论如何做。

我们有两个选择,一个是scikit-learn GridSearchCV方式,可以通过在Keras中使用scikit-learn API包装器来使用,这种方法不使用GPU加速,因此即使在并行化之后也不能实现很高的速度,另一个我们将在这里讨论的方法是Hyperas,它使我们能够利用GPU加速功能,使您能够以至少10倍的速度训练模型,并且也是一种轻松处理超参数调整的方法。如果你没有GPU,选择任何选项都会给你几乎相同的速度。

要使用Hyperas,您首先需要使用pip安装软件包

pip install hyperas

然后在您的项目中,您将需要添加以下导入语句

from hyperopt import Trials, STATUS_OK, tpe

from hyperas import optim

from hyperas.distributions import choice, uniform

使用Hyperas:密集网络

一旦完成上述过程以执行超参数优化,您将需要3个代码片段

1、数据功能

您需要创建一个函数,直接从源文件中加载训练和验证数据,或者如果您进行了任何预处理,则建议在预处理后将数据存储在pickle / numpy / hdf5 / csv文件中,然后将代码写入数据功能来访问该文件中的数据。

有必要以这种方式加载数据,因为Hyperas将数据加载函数缓存到与您的项目位于同一文件夹中的pycache,并且它没有任何权限访问您在程序中使用的全局变量,因此上述解决方法。

在我的情况下,我从源直接加载数据,并重复所有预处理步骤,如下所示。我也可以在预处理成hdf5文件后将训练数据和验证数据存储起来,并在数据函数中从那里检索数据,反正它会起作用,除非我不希望在我的程序中访问全局变量的任何值。

该函数返回X_train,Y_train,X_val,Y_val,它们是保存训练数据的变量及其相应的类标签和验证数据以及相应的类标签

def data():

(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()

X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=12345)

X_train = X_train.reshape(48000, 784)

X_val = X_val.reshape(12000, 784)

X_train = X_train.astype('float32')

X_val = X_val.astype('float32')

X_train /= 255

X_val /= 255

nb_classes = 10

Y_train = np_utils.to_categorical(y_train, nb_classes)

Y_val = np_utils.to_categorical(y_val, nb_classes)

return X_train, Y_train, X_val, Y_val

2.模型功能

这是我们定义我们想要调整的架构价值和超参数的一般框架的地方。模型函数包含我们模型的Keras体系结构,其中{{choice([])}}或{{uniform(,)}}代替我们希望调整的超参数值。下面显示了用于优化Fashion-MNIST体系结构的模型函数示例

def model(X_train, Y_train, X_val, Y_val):

model = Sequential()

model.add(Dense({{choice([128, 256, 512, 1024])}}, input_shape=(784,)))

model.add(Activation({{choice(['relu', 'sigmoid'])}}))

model.add(Dropout({{uniform(0, 1)}}))

model.add(Dense({{choice([128, 256, 512, 1024])}}))

model.add(Activation({{choice(['relu', 'sigmoid'])}}))

model.add(Dropout({{uniform(0, 1)}}))

if conditional({{choice(['two', 'three'])}}) == 'three':

model.add(Dense({{choice([128, 256, 512, 1024])}}))

model.add(Activation({{choice(['relu', 'sigmoid'])}}))

model.add(Dropout({{uniform(0, 1)}}))

model.add(Dense(10))

model.add(Activation('softmax'))

adam = keras.optimizers.Adam(lr={{choice([10**-3, 10**-2, 10**-1])}})

rmsprop = keras.optimizers.RMSprop(lr={{choice([10**-3, 10**-2, 10**-1])}})

sgd = keras.optimizers.SGD(lr={{choice([10**-3, 10**-2, 10**-1])}})

choiceval = {{choice(['adam', 'sgd', 'rmsprop'])}}

if choiceval == 'adam':

optim = adam

elif choiceval == 'rmsprop':

optim = rmsprop

else:

optim = sgd

model.compile(loss='categorical_crossentropy', metrics=['accuracy'],optimizer=optim)

model.fit(X_train, Y_train,

batch_size={{choice([128,256,512])}},

nb_epoch=20,

verbose=2,

validation_data=(X_val, Y_val))

score, acc = model.evaluate(X_val, Y_val, verbose=0)

print('Test accuracy:', acc)

return {'loss': -acc, 'status': STATUS_OK, 'model': model}

上面说明了我们如何构建我们希望我们的模型是什么样子的一般框架,以及我们希望调整的超参数是什么。

model.add(Dense({{choice([128,256,512,1024])}})) - 用于表示我们希望调整图层中隐藏单元的数量并找到最佳适合128,256,512和1024之间。

model.add(Activation({{choice(['relu','sigmoid'])}})) - 用于表达我们希望调整激活函数参数并找到ReLU和Sigmoid

model.add(Dropout({{uniform(0,1)}})) - 用于表达我们希望调整Dropout保持概率的值并找到真实范围内的最佳拟合的事实数字在0和1之间。

包含在选择中的值是我们希望调整我们的超参数的值,并且包含在均匀内的范围是实数的范围,在该范围内我们期望我们的超参数的最佳值是

Hyperas模块运行许多不同的模型,每次从每个值中获取单个值,通过所有我们希望调整的超参数值的“选择”和“统一”给出。它最终为我们提供了在验证集上运行时获得最低损失值的值的组合。它可以被认为是做类似于sklearn中的RandomSearchCV的东西。

if conditional({{choice(['two', 'three'])}}) == 'three':

model.add(Dense({{choice([128, 256, 512, 1024])}}))

model.add(Activation({{choice(['relu', 'sigmoid'])}}))

model.add(Dropout({{uniform(0, 1)}}))

上面的代码片段用于调整层数,以了解两三级密集网络体系结构是否是一个不错的选择。这里在迭代中选择{{choice(['two','three'])}}的值。

if conditional({{choice(['two', 'three'])}}) == 'three':

如果该值为'two',则它不满足if条件中的条件,因此if块中的代码不会执行,只留下两层结构,或者如果选择的值为'three',则条件满足并执行if块,给我们一个三层架构来测试当前的迭代。

adam = keras.optimizers.Adam(lr={{choice([10**-3, 10**-2, 10**-1])}})

rmsprop = keras.optimizers.RMSprop(lr={{choice([10**-3, 10**-2, 10**-1])}})

sgd = keras.optimizers.SGD(lr={{choice([10**-3, 10**-2, 10**-1])}})

choiceval = {{choice(['adam', 'sgd', 'rmsprop'])}}

if choiceval == 'adam':

optim = adam

elif choiceval == 'rmsprop':

optim = rmsprop

else:

optim = sgd

上面的代码片段用于调整优化器和学习速率。在遍历上面的部分时,选择每个优化器中学习率值中的一个值,然后从{{choice(['adam','sgd','rmsprop'])}}被选中。如果选择的值是'adam',那么对于当前迭代,Adam优化器以及来自adam = keras.optimizers.Adam中值的池中的所选学习速率(lr = {{choice([10 ** - 3,10 ** - 2,10 ** - 1])}})被用作模型的优化器,这是在下面的if块中传递的

if choiceval == 'adam':

optim = adam

elif choiceval == 'rmsprop':

optim = rmsprop

else:

optim = sgd

3.执行

X_train,Y_train,X_val,Y_val = data()

best_run,best_model = optim.minimize(model = model,

data = data,

algo = tpe.suggest,

max_evals = 30,

trials = Trials(),

notebook_name ='Fashion_MNIST')

这段代码将使用数据函数中给出的训练和验证数据开始模型函数中给定框架的超参数优化。

optim.minimize函数中的所有值必须保持相同,除了notebook_name参数(输入IPython Notebook的名称)和max_evals参数(您希望在选择之前输入想要训练的最大模型数量)适合您的架构的超参数集。价值取决于你的资源和时间,一般来说30-50的数字应该足够好。

上面的代码片段必须写在数据和模型函数之后,并开始执行Hyperas模块,该模块将返回best_model变量中的最佳模型配置和best_run中的最佳超参数集合。

您可以使用打印(best_model.evaluate(X_test,Y_test))用最好的模型预测测试集或打印(best_run)知道什么是执行中的最佳model.On做出的参数选项打印(best_run)你将得到如下所示的输出

{'Activation':0,'Activation_1':0,'Activation_2':0,'Dense':3,'Dense_1':3,'Dense_2':1,'Dropout':0.6346993188487943,'Dropout_1':0.3471522714859784' Dropout_2':0.42208889978571484,'batch_size':1,'choiceval':0,'conditional':1,'lr':0,'lr_1':1,'lr_2':1}

为了解释上面的内容,当你运行代码的执行部分时,你需要看看包含输出的部分,如下所示

Output of the execution section and print(best ru

如图所示,输出包含模型框架,该模型框架以每个space/pool的值作为模型函数中的输入。要知道选择的确切值,我们可以参考我们在任何空间变量位置的模型函数中提供的space/pool值。

'Dense':3表示对于第一层中隐藏单元的数量,最佳模型使用列表中索引3处的值为1024.请注意: - 使用的索引值从0开始。

'Activation_1':0表示对于第二层,最佳模型具有ReLU,因为它是激活功能。

'有条件的':1表示最好的模型由3层组成。

'choiceval':0表示最佳模型使用Adam作为优化器,学习率为0.001(由lr:0给出)。

'Dropout_2':0.42 ...指示隐藏单元的第三层使用0.42的保持概率之后的Dropout层。

解码所有参数值后的最佳模型的最终模型配置如上所示

model = Sequential(

Dense(1024,input_shape =(784,),activation ='relu'),

Dropout(0.63),

Dense(1024,activation ='relu'),

Dropout(0.35),

Dense(256,activation = 'relu'),

Dropout(0.42),

Dense(10,activation ='softmax')

])

这个运行40个时期的模型给出了以下结果

Train loss: 0.23121074857314428

Train accuracy: 0.9159583333333333

-------------------------------

Validation loss: 0.28158413629730544

Validation accuracy: 0.9024166666666666

-------------------------------

Test loss: 0.3213586136817932

Test accuracy: 0.8871

我们已经在基线模型中获得了从之前的83%的测试准确度88%。

技巧和窍门

1.不要使用你的测试数据代替你的验证集。总是将数据集分成3组训练,验证和测试。使用train来学习数据中的各种模式,验证为超参数学习值,并测试模型是否能很好地推广。

2.确保您只在最优化时训练最重要的超参数,即总是尝试减少超参数的数量及其使用文章中所述的经验法则调整的值。这是因为越多的超参数及其值越多,需要训练的模型数量才能在优化时做正义。

例如。在上述情况下,激活函数中的值我们没有使用过LeakyReLU,这是因为LeakyReLU总是执行等于或稍好于ReLU。同样,我还没有优化初始化超参数,因为我们知道如果它是ReLU激活,我们需要使用He-normal和glorot-normal,如果Sigmoid作为经验法则。由于我们不希望我们的模型花费很长时间来寻找超参数,因为我们需要一个快速学习的模型,因此我们需要一个能够快速学习的模型,因此设定了时期的数量到20.我们可以在模型选择后运行更多数量的时期模型。

正如你可能已经观察到的,我们只对那些对学习有很大影响的组合进行优化,如层数,隐藏单位,学习率等。

3.调试提示: - 建议在上一次执行后必须更改模型函数时始终删除pycache文件夹,因为即使更改模型函数,模块可能会使用pycache中的旧版本进行优化。如果你仍然面临更新模型函数的问题,那么在删除pycache之后也要重新启动你的内核。

最近发表
标签列表