小能豆

Keras:MobileNet 用于定位图像特征

py

我有一个自定义图像集,我试图在其中定位该图像中的 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))

阅读 16

收藏
2024-12-30

共1个答案

小能豆

您实施的 MobileNet 关键点检测方法很有前景,但有几个方面需要调整,才能提高架构与特定任务的兼容性。以下是潜在问题和修复的细分:

主要观察与调整:

  1. 输出和激活不匹配:

  2. 您的最后一层使用softmax激活,这通常用于分类任务。但是,对于预测关键点(8 x, y坐标)等回归任务,您应该使用线性激活。

  3. 代替:

    model.add(Activation('softmax'))

    和:

    model.add(Activation('linear'))

  4. 回归任务的GlobalAveragePooling:

  5. GlobalAveragePooling2D

    通常用于分类任务以减少空间维度。但是,对于回归来说,精确的空间信息至关重要,这种池化可能会损害性能。将其替换为

    Flatten()

    以保留更多空间细节:

    model.add(Flatten())

  6. 最终输出层:

  7. 最后一层应该直接输出 8 个值(x,y 坐标)作为密集层:

    model.add(Dense(8, activation='linear'))

  8. 辍学安置:

  9. 虽然 dropout 对正则化有益,但将其放在每个密集层之后可能会过度惩罚网络。请谨慎使用它,并确保它不会在训练期间阻止梯度。例如:

    model.add(Dense(2000, activation='relu')) model.add(Dropout(0.5))

  10. 输入数据的规范化:

  11. 确保输入图像已标准化,以匹配 MobileNet 预期的范围。使用:

    from tensorflow.keras.applications.mobilenet import preprocess_input X_train = preprocess_input(X_train)

  12. 学习率和优化器:

  13. 使用较小的学习率来微调预训练网络。首先:

    from tensorflow.keras.optimizers import Adam model.compile(optimizer=Adam(learning_rate=1e-4), loss='mean_squared_error', metrics=['mae'])

  14. 预训练权重和迁移学习:

  15. 利用预先训练的 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)
    ```

  16. 评估指标:

  17. 对于回归任务,使用均方误差(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'])

训练提示:

  • 批次大小:使用较小的批次大小(例如 16)以避免过度拟合。
  • 数据增强:应用旋转、缩放和平移等转换来提高泛化能力。
  • 热身训练:最初只训练密集层,然后解冻基础模型进行微调。

这些调整应该可以使网络更有效地进行关键点检测,同时利用 MobileNet 的高效结构。

2024-12-30