我有一个自定义图像集,我试图在其中定位该图像中的 4 个特征。这些值是 x、y 坐标。我运行了一些基本的 CNN,它们运行良好。我现在的目标是转换为 MobileNet。
我在使用 Keras 的内置MobileNet和代码时遇到了麻烦……所以我用适当的层模仿了结构。基础模型似乎适合分类,而我的模型实际上只是试图找到 8 个 x,y 坐标。我已尽最大努力适应不同的输出层,但到目前为止,我的损失相当严重,学习率很高。
我哪里做错了?我感觉好像我因为无法使用内部 MN 模型而错过了一些东西,因为 MobileNet 被宣传为可以适应对象检测和分类。
我的实现(棘手的部分似乎已经结束了):
model = Sequential() model.add(Conv2D(32, (3, 3), strides=(2, 2), padding='same', use_bias=False, input_shape=(224, 224, 3))) model.add(BatchNormalization()) model.add(Activation('relu')) # block 1 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(64, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 2 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(2, 2), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(128, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 3 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(128, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 4 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(2, 2), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(256, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 5 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(256, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 6 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(2, 2), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(512, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 7 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(512, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 8 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(512, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 9 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(512, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 10 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(512, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 11 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(512, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 12 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(2, 2), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(1024, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # block 13 ########### model.add(ZeroPadding2D(padding=(1, 1))) model.add(DepthwiseConv2D((3, 3), padding='valid', depth_multiplier=1, strides=(1, 1), use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Conv2D(1024, (1, 1), strides=(1, 1), padding='same', use_bias=False)) model.add(BatchNormalization()) model.add(Activation('relu')) ########### # final -- tried shaping for my output model.add(GlobalAveragePooling2D()) model.add(Reshape((1, 1, 1024))) model.add(Dropout(1e-3)) model.add(Conv2D(8, (1, 1), padding='same')) model.add(Activation('softmax')) model.add(Reshape((8,))) # output layers that give me a better result for 8 keypoints # model.add(Flatten()) # model.add(Dense(2000)) # model.add(Activation('relu')) # model.add(Dropout(0.5)) # ! # model.add(Dense(2000)) # model.add(Activation('relu')) # model.add(Dense(8))
您实施的 MobileNet 关键点检测方法很有前景,但有几个方面需要调整,才能提高架构与特定任务的兼容性。以下是潜在问题和修复的细分:
输出和激活不匹配:
您的最后一层使用softmax激活,这通常用于分类任务。但是,对于预测关键点(8 x, y坐标)等回归任务,您应该使用线性激活。
softmax
8 x, y
代替:
model.add(Activation('softmax'))
和:
model.add(Activation('linear'))
回归任务的GlobalAveragePooling:
GlobalAveragePooling2D
通常用于分类任务以减少空间维度。但是,对于回归来说,精确的空间信息至关重要,这种池化可能会损害性能。将其替换为
Flatten()
以保留更多空间细节:
model.add(Flatten())
最终输出层:
最后一层应该直接输出 8 个值(x,y 坐标)作为密集层:
model.add(Dense(8, activation='linear'))
辍学安置:
虽然 dropout 对正则化有益,但将其放在每个密集层之后可能会过度惩罚网络。请谨慎使用它,并确保它不会在训练期间阻止梯度。例如:
model.add(Dense(2000, activation='relu')) model.add(Dropout(0.5))
输入数据的规范化:
确保输入图像已标准化,以匹配 MobileNet 预期的范围。使用:
from tensorflow.keras.applications.mobilenet import preprocess_input X_train = preprocess_input(X_train)
学习率和优化器:
使用较小的学习率来微调预训练网络。首先:
from tensorflow.keras.optimizers import Adam model.compile(optimizer=Adam(learning_rate=1e-4), loss='mean_squared_error', metrics=['mae'])
预训练权重和迁移学习:
利用预先训练的 MobileNet 权重进行特征提取,然后添加自定义回归层:
``` from tensorflow.keras.applications import MobileNet base_model = MobileNet(input_shape=(224, 224, 3), include_top=False, weights=’imagenet’)
# Freeze base model layers for layer in base_model.layers: layer.trainable = False
# Add regression layers x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(1024, activation=’relu’)(x) x = Dropout(0.5)(x) outputs = Dense(8, activation=’linear’)(x)
model = Model(inputs=base_model.input, outputs=outputs) ```
评估指标:
对于回归任务,使用均方误差(MSE)或平均绝对误差(MAE)等指标,而不是准确度。
以下是针对您的问题而定制的简明架构:
from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense, Dropout, GlobalAveragePooling2D, Flatten from tensorflow.keras.applications import MobileNet # Base model base_model = MobileNet(input_shape=(224, 224, 3), include_top=False, weights='imagenet') for layer in base_model.layers: layer.trainable = False # Freeze base model # Custom regression head x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(1024, activation='relu')(x) x = Dropout(0.5)(x) outputs = Dense(8, activation='linear')(x) model = Model(inputs=base_model.input, outputs=outputs) # Compile model.compile(optimizer=Adam(learning_rate=1e-4), loss='mean_squared_error', metrics=['mae'])
这些调整应该可以使网络更有效地进行关键点检测,同时利用 MobileNet 的高效结构。