对于初级炼丹师,往往苦于调参。那么有没有什么高科技炼丹炉,可以让我们只要按下按钮,就可以自行调整,炼出一炉品质上佳的仙丹呢?
那肯定是有的,下面,请允许我隆重介绍KerasTuner。在下面的例子里,将使用KerasTuner进行超参数搜索,从现在开始,你炼的模型就是超模型啦~
KerasTuner并没有限制我们的操作空间,它仍允许我们进行大量自定义。当然,它也是十分便利的,我们只要简单的设定一下搜索空间,就可以去摸鱼了。不过,你的显卡就要替你受罪了。训练超模型需要的算力较大,所以,没GPU的同学不建议使用。
在我们开始之前,先给出其官方文档:
KerasTuner
这会有助于理解代码。
下面先来一个简单的超模型
这里继承父类keras_tuner.HyperModel,我们需要编写其中的build方法与fit方法。其中为了将模型定义与超参数分离开,编写了一个model方法,这样有助于提高代码的条理性与可读性。
from tensorflow import keras
import keras_tuner
class HyperModel(keras_tuner.HyperModel):
def model(self,units,num_layers,lr):
model = keras.Sequential()
model.add(keras.layers.Rescaling(scale=1 / 127.5, offset=-1))
model.add(keras.layers.Flatten())
for i in range(num_layers):
model.add(keras.layers.Dense(units, activation='relu'))
model.add(keras.layers.Dropout(rate=0.3))
model.add(keras.layers.Dense(10, activation="softmax"))
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=lr),
loss="categorical_crossentropy",
metrics=["accuracy"],
)
return model
def build(self, hp):
units = hp.Int("units", min_value=64, max_value=256, step=32)
num_layers = hp.Int("num_layers",min_value=1,max_value=3)
lr = hp.Float("lr",min_value=1e-5,max_value=1e-3,sampling="log")
return self.model(units,num_layers,lr)
def fit(self, hp, model, x_train,y_train, **kwargs):
return model.fit(
x_train,
y_train,
**kwargs
)
简单的例子用简单的数据进行测试,这里就使用经典的手写数字数据集吧。
from tensorflow import keras
import numpy as np
(x, y), (x_test, y_test) = keras.datasets.mnist.load_data()
x = np.expand_dims(x,axis=-1)
x_test = np.expand_dims(x_test,axis=-1)
num_classes =10
y = keras.utils.to_categorical(y, num_classes)
y_test = keras.utils.to_categorical(y_test,num_classes)
这里设定最多搜10次,每次迭代2个epoch。
tuner = keras_tuner.RandomSearch(
HyperModel(),
objective="val_accuracy",
max_trials=10,
overwrite=True,
directory="my_dir",
project_name="tune_hypermodel",
)
tuner.search(x,y,epochs=2,batch_size=128,validation_data=(x_test, y_test),workers =8)
我们需要使用在验证集表现最好的超参数构建模型,进行训练。
首先看看每次的结果是咋样的:
tuner.results_summary()
构建模型并训练
hypermodel = HyperModel()
best_hp = tuner.get_best_hyperparameters()[0]
model = hypermodel.build(best_hp)
hypermodel.fit(best_hp, model, x, y, epochs=20,batch_size=128,validation_data=(x_test, y_test),workers =8)
model.save('demo_hypermodel.h5')
好了,通过上面的例子,相信你已经对KerasTuner有一定的了解了。下面我们正式构建可以用于比赛的超·MobileNetV2。
下面的模型将会稍微复杂点。我们将数据增强的参数也纳入了搜索范围,而对模型的参数,我们搜索学习率和dropout比率。
事实上,搜索什么参数不一定按照这样定义,你完全可以根据自己的需求来,这里仅做一个简单的示范。
要注意的是,并不是把所有的参数都纳入搜索就是最好的,要考虑实际情况。
from tensorflow.keras.preprocessing.image import ImageDataGenerator
class HyperMobilenet_v2(keras_tuner.HyperModel):
def __init__(self,input_size,batch_size,train_root):
super(keras_tuner.HyperModel, self).__init__()
self.input_size = input_size
self.batch_size = batch_size
self.train_root = train_root
def model(self,Dropout_rate,lr):
model = Mobilenet_v2(
input_size=self.input_size,
weights='imagenet',
Dropout_rate=Dropout_rate,
Trainable=True
)
model.compile(optimizer=keras.optimizers.Adam(learning_rate=lr),
loss=keras.losses.CategoricalCrossentropy(),
metrics=["accuracy"])
return model
def build(self, hp):
lr = hp.Float("lr",min_value=1e-5,max_value=1e-3,sampling="log")
Dropout_rate = hp.Choice("Dropout_rate",values =[0.1,0.2,0.3,0.4,0.5,0.6])
return self.model(Dropout_rate,lr)
def dataset(self, hp):
train_root = self.train_root
zoom_range = hp.Float("zoom_range",min_value=0.1,max_value=0.3,sampling="linear")
channel_shift_range = hp.Int("channel_shift_range",min_value=0,max_value=30,step=10)
train_generator = ImageDataGenerator(rotation_range=360,
zoom_range =zoom_range,
horizontal_flip = True,
validation_split =0.2,
channel_shift_range =channel_shift_range
)
train_dataset = train_generator.flow_from_directory(batch_size=self.batch_size,
directory=train_root,
shuffle=True,
target_size=(self.input_size,self.input_size),
subset='training')
valid_dataset = train_generator.flow_from_directory(batch_size=self.batch_size,
directory=train_root,
shuffle=True,
target_size=(self.input_size,self.input_size),
subset='validation')
return train_dataset,valid_dataset
def fit(self, hp, model,**kwargs):
train_dataset,valid_dataset = self.dataset(hp)
return model.fit(
train_dataset,
validation_data=valid_dataset,
**kwargs
)
因为我们搜索的超参数比上面例子的更多,我们的搜索次数设定得更大些,每次的epochs也增加到10次。你也可以调得更大,但这样需要的时间会更多。
tuner = keras_tuner.BayesianOptimization(
hypermodel =HyperMobilenet_v2(input_size=128,batch_size=128,train_root='./train/'),
objective="val_accuracy",
max_trials=50,
overwrite=True,
directory="my_dir",
project_name="tune_hypermodel",
)
tuner.search(epochs=10,workers=8)
用表现最好的超参数构建模型。
hypermodel = HyperMobilenet_v2(input_size=128,batch_size=128,train_root='./train/')
best_hp = tuner.get_best_hyperparameters()[0]
model = hypermodel.build(best_hp)
加入回调进行训练。
import time
import os
save_path = './models_save/%s' % (time.strftime('%Y_%m_%d_%H_%M_%S'))
reduce_lr = keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.2,patience=10,verbose=1)
early_stop =keras.callbacks.EarlyStopping(monitor='val_accuracy', patience=15,verbose=1)
save_weights = keras.callbacks.ModelCheckpoint(save_path + "/model_{epoch:02d}_{val_accuracy:.4f}.h5",
save_best_only=True, monitor='val_accuracy')
hypermodel.fit(
best_hp,
model,
epochs=100,
workers =8,
callbacks=[save_weights,early_stop,reduce_lr]
)