基于TensorFlow的图像分类技术

大家好,希望已经使用过TensorFlow或其他CNN预训练模型进行过图像分类,并且对如何分类图像有一定的了解。但是,当需要对细节丰富的对象(如狗的品种、猫的品种、叶子疾病)进行分类时,传统方法可能无法给出满意的结果。在这种情况下,更倾向于使用模型堆叠来捕捉更多的细节。让直接进入技术细节。

在数据集中,有120种狗的品种,将使用堆叠预训练模型TensorFlow,Densenet121)对它们进行分类,这些模型是在Imagenet上训练的。将堆叠这些模型提取的瓶颈特征,以获得更高的准确性,这将取决于堆叠在一起的模型。

堆叠预训练模型是如何工作的?当处理分类问题时,倾向于使用一个主要关注最大池化特征的分类器,这意味着它在训练时不考虑细小或小对象。这就是使用堆叠模型的原因,它们可以帮助根据突出和细节对象特征轻松分类图像。

为了创建堆叠模型,需要使用两个或更多的分类架构,如Resnet、Vgg、Densenet等。这些分类器以图像为输入,并根据它们的架构生成特征矩阵。通常,每个分类器会按照以下阶段进行,以创建一个特征向量:

1. 卷积:这是生成特征图的过程,这些特征图描绘了图像特定的特征,如边缘、锐度等。

2. 最大池化:在此过程中,从使用卷积过程生成的特征图中提取突出特征。

3. 展平:在此过程中,最终的特征图被转换为特征向量。

在从不同模型获得特征向量后,将它们堆叠在一起,以形成一个特征矩阵,然后用作最终神经网络模型的输入,其工作是将这些矩阵分类为最终的数据类别。

现在知道了堆叠模型的工作原理,让使用Vgg16和Resnet架构设计一个堆叠模型。

在继续之前,让看看如何堆叠模型以获得瓶颈特征。

from keras.applications.resnet_v2 import ResNet50V2, preprocess_input as resnet_preprocess from keras.applications.densenet import DenseNet121, preprocess_input as densenet_preprocess 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 = Model(inputs = input_layer, outputs = merge) stacked_model.summary()

在这里,堆叠了两个模型(Densenet121和resnet50V2),两者都有include_top = False,这意味着只提取瓶颈特征,然后使用concatenate层将它们合并。

现在知道如何创建堆叠模型,可以开始创建狗品种分类器。

上述图表显示了训练和推理分类器的工作流程。将在图像上训练堆叠模型,这将生成一个特征矩阵,然后用作另一个神经网络的输入特征以对狗品种进行分类。

现在得到了一个高层次的方法图。让看看训练和推理的逐步程序。

加载数据集。将使用Kaggle上的数据集,可以在这里找到(https://www.kaggle.com/c/dog-breed-identification)。将加载数据到一个名为labels_dataframe的pandas dataframe中,并将每个y标签转换为数值。

# 数据路径 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)

现在需要将breed列转换为y_train,使用to_categorical。

from keras.utils import to_categorical y_train = to_categorical(labels_dataframe.breed)

在这一步中,将使用刚刚设计的stacked_model进行瓶颈特征提取,这些提取的特征将成为X_train,用于训练。使用批次,以便不会有任何OOM(内存不足)问题。

def 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 input_batch in tqdm(generator): input_batch = stacked_model.predict(input_batch) X[i * batch_size : (i + 1) * batch_size] = input_batch i += 1 if i * batch_size >= data_size: break return X X_train = feature_extractor(labels_dataframe)

这里X_train(包括预训练模型提取的所有特征)是新X_train,将用于最终模型。

现在将创建一个简单的模型,它将接受X_train(由堆叠模型提取的特征)和y_train(分类值),这将是最终的预测模型。

import keras predictor_model = keras.models.Sequential([ InputLayer(X.shape[1:]), Dropout(0.7), Dense(n_classes, activation='softmax') ]) predictor_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(monitor = 'val_loss',factor = 0.5,patience = 3,min_lr = 0.00001) my_callback=[EarlyStop_callback,checkpoint]

这段代码将自动保存最佳时期,早期停止,如果训练中没有进一步的改进,它将降低学习率。

现在将在X_train和y_train上训练predictor_model,X_test,y_test将自动由validation_split获取。

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

使用validation_split = 0.1,将数据集分为训练(90%)和测试(10%)。

将绘制训练的历史,并计算表现。这里history_graph是将用来绘制每个时期的历史的History对象。

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

已经有效地训练了模型,并获得了大约85%的验证准确率。当然,可以进一步提高它,将在最后讨论。

最后,将保存预训练模型,以便以后用于推理。

dnn.save('/kaggle/working/dogbreed.h5') stacked_model.save('/kaggle/working/feature_extractor.h5') 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.predict(img) y_pred = predictor_model.predict(extracted_features) y_pred 是预测数组的形状(1,120)。y_pred是一个数组,具有每个类别的概率。现在需要找到具有最高概率的类别标签,然后使用已经定义的字典class_to_num将类别编号转换为类别标签。
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485