深度学习:堆叠预训练CNN模型进行分类

目录

  • 引言
  • 工作流程
  • 模型堆叠
  • 实现

虽然有多种方法可以对狗的品种进行分类,但本文将使用预训练的堆叠模型来实现这一目的。在数据集中,包含120种狗的品种,需要对它们进行分类。通过堆叠Resnet50V2和Densenet121,在瓶颈特征提取后得到了良好的结果。这些预训练模型都是在ImageNet开放数据集上训练的。

工作流程

在进一步讨论之前,先来看一下任务的工作流程。首先,将使用堆叠的预训练模型提取瓶颈特征,然后将训练一个简单的密集神经网络来对提取的特征进行分类。在测试或预测部分,将输入提取的特征,并将其作为输入传递给简单的DNN模型以进行最终预测。预测将是类别编号,然后将该类别编号转换为相应的狗品种标签。

模型堆叠的概念非常简单,只需使用concatenate层连接多个模型的输出即可。将使用堆叠模型进行瓶颈特征提取。

from keras.applications.densenet import DenseNet121, preprocess_input as preprocess_densenet from keras.applications.resnet_v2 import ResNet50V2 , preprocess_input as preprocess_resnet from keras.layers.merge import concatenate input_shape = (331,331,3) input_layer = Input(shape=input_shape) # 第一个特征提取器 preprocessor_resnet = Lambda(resnet_preprocess)(input_layer) resnet50v2 = ResNet50V2(weights = 'imagenet', include_top = False, input_shape = input_shape, pooling ='avg')(preprocessor_resnet) preprocessor_densenet = Lambda(densenet_preprocess)(input_layer) densenet = DenseNet121(weights = 'imagenet', include_top = False, input_shape = input_shape, pooling ='avg')(preprocessor_densenet) merge = concatenate([resnet50v2,densenet]) stacked_model_final = Model(inputs = input_layer, outputs = merge) stacked_model_final.summary()

已经堆叠了Densenet121和resnet50v2。通过concatenate层,连接了两个预训练模型提取的特征。

将使用Keras和其他库来实现狗品种分类器。在下一篇文章中,将讨论如何将训练好的模型部署到网站上。

1. 加载数据

将使用的数据集可以在Kaggle上找到,下载链接。建议使用Kaggle笔记本而不是将数据集下载到本地机器上。

# 数据路径 train_dir = '/kaggle/input/dog-breed-identification/train/' labels_dataframe = pd.read_csv('/kaggle/input/dog-breed-identification/labels.csv') dog_breeds = sorted(list(set(labels_dataframe['breed']))) n_classes = len(dog_breeds) class_to_num = dict(zip(dog_breeds, range(n_classes))) labels_dataframe['file_path'] = labels_dataframe['id'].apply(lambda x:train_dir+f"{x}.jpg") labels_dataframe['breed'] = labels_dataframe.breed.map(class_to_num)

2. 提取瓶颈特征

这一步非常关键且耗时,将使用堆叠模型提取所有图像的特征。为了避免内存不足的问题,将分批提取特征。提取的特征将作为新X_train,类别编号将作为y_train。

def bottleneck_feature_extractor(df): img_size = (331,331,3) data_size = len(df) batch_size = 20 X = np.zeros([data_size,3072], dtype=np.uint8) datagen = ImageDataGenerator() generator = datagen.flow_from_dataframe(df, x_col = 'file_path', class_mode = None, batch_size=20, shuffle = False,target_size = (img_size[:2]),color_mode = 'rgb') i = 0 for batch_input in tqdm(generator): batch_input = stacked_model_final.predict(batch_input) X[i * batch_size : (i + 1) * batch_size] = batch_input i += 1 if i * batch_size >= data_size: break return X X_train = bottleneck_feature_extractor(labels_dataframe)

X_train包含了所有提取的特征,这些特征将作为最终预测模型的输入。

3. 最终预测模型:

有X_train(提取的特征)和y_train(类别编号的分类值),是时候构建一个最终的预测模型,该模型将在X_train和y_train上进行训练,并返回最终预测的类别编号。

import keras final_model = keras.models.Sequential([ InputLayer(X.shape[1:]), Dropout(0.7), Dense(n_classes, activation='softmax') ]) final_model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

为了高效训练,可以利用回调函数。

from keras.callbacks import EarlyStopping,ModelCheckpoint, ReduceLROnPlateau # 回调函数 EarlyStop_callback = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) checkpoint = ModelCheckpoint('/kaggle/working/checkpoing', monitor = 'val_loss',mode = 'min',save_best_only= True) # 基于改进减少学习率 lr = ReduceLROnPlateau(factor = 0.5,patience = 3,monitor = 'val_loss',min_lr = 0.00001) my_callback=[EarlyStop_callback,checkpoint]

有一个早期停止器,如果训练没有进一步改进,学习率减少器将减少学习率。

4. 训练阶段

在X_train和y_train以及X_test和y_test上训练final_model,并借助validation_split。

# 在提取的特征上训练final_model。 history_graph = final_model.fit(X_train , y_train, batch_size=128, epochs=60, validation_split=0.1 , callbacks = my_callback)

使用validation_split = 0.1,数据集将被分成(90%)和(10%)。

5. 绘制结果

绘制训练历史可以让了解性能和训练情况。这里的history_graph是包含每个epoch所有训练图的历史对象。

import matplotlib.pyplot as plt fig, (ax_1, ax_2) = plt.subplots(2, 1, figsize=(14, 14)) ax1.plot(history_graph.history['loss'],color = 'b',label = 'loss') ax1.plot(history_graph.history['val_loss'],color = 'r',label = 'val_loss') ax_1.set_xticks(np.arange(1, 60, 1)) ax_1.set_yticks(np.arange(0, 1, 0.1)) ax_1.legend(['loss','val_loss'],shadow = True) ax_2.plot(history_graph.history['accuracy'],color = 'green',label = 'accuracy') ax_2.plot(history_graph.history['val_accuracy'],color = 'red',label = 'val_accuracy') ax_2.legend(['accuracy','val_accuracy'],shadow = True) plt.show()

哇!最后,已经训练了模型,并获得了85%的验证准确率,这是一个好的开始。虽然这不是结束,可以通过一些微调进一步提高这个准确率。

6. 模型保存

保存训练好的模型以供进一步开发。使用保存的模型,可以在网站上部署模型,可以创建一个能够分类狗品种的Android应用。

final_model.save('/kaggle/working/dogbreed.h5') stacked_model_final.save('/kaggle/working/feature_extractor.h5')

7. 测试和预测

需要遵循在训练过程中遵循的相同工作流程。首先使用stacked_model_final提取单个图像的瓶颈特征,然后将它们转换为张量并传递给最终模型作为输入。最终模型将返回狗品种的类别编号。

img = load_img(img_path, target_size=(331,331)) img = img_to_array(img) img = np.expand_dims(img,axis = 0) # 这是创建张量(4维) extracted_features = stacked_model_final.predict(img) y_pred = predictor_model.predict(extracted_features) y_pred是包含所有120个狗品种类别编号概率的预测数组。需要挑选具有最高概率的类别编号。获得类别编号后,将类别编号转换回类别标签,使用字典class_to_num。 def get_key(val): for key, value in class_to_num.items(): if val == value: return key pred_codes = np.argmax(y_pred, axis = 1) predicted_dog_breed = get_key(pred_codes)
  • 在特征提取之前应用图像增强
  • 堆叠更多密集的预训练模型(VGG19, VGG16等)
  • 增加提取特征的维度
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485