即使我的 CNN 模型正在进行回归,我是否可以对我的输出层使用 Sigmoid 激活?

Can I use a Sigmoid activation for my output layer, even if my CNN model is doing a regression?

最终objective:对象中点计算。

我有一个小数据集(大约 120 张图像),其中有一个对象(在所有情况下都相同),标签是图像中对象中点的标准化 x、y 坐标(始终介于0 和 1)

例如x = image_005 ; y = (0.1, 0.15) 对于对象位于左下角附近的图像

我正在尝试使用 ResNet 架构,但针对我的图像大小进行了自定义(所有图像都是相同的)。由于输出值始终在 0 和 1 之间,对于两个坐标,我想知道是否可以在我的最后一层使用 Sigmoid 激活:

 X = Dense(2, activation='sigmoid', name='fc', kernel_initializer = glorot_uniform(seed=0))(X)

而不是线性激活(当你试图获得回归结果时经常被建议)

对于损失函数,我使用 MSE,使用 'rmsprop' 优化器,除了准确性和 MSE 之外,我还编写了一个自定义指标来告诉我预测点是否偏离标签超过5%

model.compile(optimizer='rmsprop', loss='mean_squared_error', metrics=['mse','acc',perc_midpoint_err])

在对模型进行大约 150 次训练后(我也尝试了不同的批量大小),我没有得到好的结果

我应该把激活层改成线性的吗?或者我可以对我的模型做不同的修改吗?还是 ResNet 完全不适合这个任务?

您的任务与对象检测有关。不同之处在于,您的每张图像中似乎只有一个物体,而在检测中可能有多个物体或没有物体存在。对于物体检测,有YOLOv3(https://pjreddie.com/media/files/papers/YOLOv3.pdf) or Single Shot Multibox Detector - SSD (https://arxiv.org/pdf/1512.02325.pdf) but also ResNet can be trained as an object detection network (as in this paper: https://arxiv.org/pdf/1506.01497.pdf)

等网络

我将简要描述YOLO如何解决边界框x,y坐标的回归问题:

  • YOLO 对 x,y 使用 sigmoid 激活函数
  • 它将图像划分为网格单元并预测每个网格单元中潜在对象的偏移量。如果您在多个位置有大图像或对象,这可能会有所帮助。
  • 原始论文使用 MSE 作为损失函数,但在我最喜欢的 keras-reimplementation 中,他们使用交叉熵损失和 Adam 优化器。

原则上你的设置对我来说没问题。但是有很多事情可能会导致性能不佳,因为您没有说明数据集的域:您是使用预训练网络还是从头开始训练?它是您要学习的新类别还是网络以前见过的对象类别?等等

以下是您可以尝试的一些想法:

  • 更改优化器(SGD 或 Adam)
  • 改变学习率(小比大好)
  • 增加数据集大小。为了针对新的对象类别重新训练网络,我的经验法则是使用大约 500-1000 张图像。要从头开始再训练,您需要多出几个数量级。
  • 您可能想查看 YOLO 或 SSD 并根据您的情况修改这些网络

希望您能为您的解决方案找到一些灵感。

除了您所做的,您还可以做很多其他事情:

  1. 使用ImageAugmentation技术生成更多数据。另外,规范化图像。
  2. 用更多的卷积层制作更深的模型。
  3. 对卷积层使用适当的权重初始化器可能是 He-normal。
  4. 在层之间使用 BatchNormalization 使过滤器值的 meanstd 分别等于 0 和 1。
  5. 尝试使用交叉熵损失,因为它有助于更​​好地计算梯度。在 MSE 中,梯度随着时间的推移变得非常小,尽管它更适合回归类问题。您也可以尝试 MeanSquaredLogarithmicError.
  6. 尝试将优化器更改为 AdamStochastic Gradient Descent with nestrov momentum(在验证集上比 Adam 表现更好)。
  7. 如果你的数据集中有更多的classes,并且你有class不平衡问题,你可以使用Focal loss,交叉熵损失的一种变体,它对错误 class 化标签的惩罚比正确 class 化标签更多。此外,减少 批量大小 上采样 应该会有所帮助。
  8. 使用贝叶斯优化技术对模型进行超参数调整。

这里是Resnet的简单实现:

def unit(x, filters, pool=False):
    res = x
    if pool:
        x = MaxPooling2D(pool_size=(2, 2))(x)
        res = Conv2D(filters=filters, kernel_size=(1, 1), strides=(2, 2), padding='same', kernel_initializer='he_normal')(res)
    out = BatchNormalization()(x)
    out = Activation('relu')(out)
    out = Conv2D(filters=filters, kernel_size=(3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(out)

    out = BatchNormalization()(out)
    out = Activation('relu')(out)
    out = Conv2D(filters=filters, kernel_size=(3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(out)

    x = keras.layers.add([res, out])
    return x

def model(inputs):
    inp = Input(inputs)
    x = Conv2D(32, (3, 3), padding='same', kernel_initializer='he_uniform')(inp)
    x = unit(x, 32)
    x = unit(x, 32)
    x = unit(x, 32)

    x = unit(x, 64, pool=True)
    x = unit(x, 64)
    x = unit(x, 64)

    x = unit(x, 128, pool=True)
    x = unit(x, 128)
    x = unit(x, 128)

    x = unit(x, 256, pool=True)
    x = unit(x, 256)
    x = unit(x, 256)

    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dropout(0.25)(x)

    x = AveragePooling2D((3, 3))(x)
    x = Flatten()(x)
    x = Dense(2, activation='sigmoid')(x)

    model = Model(inputs=inp, outputs=x)
    optimizer = Adam(lr=0.001)
    # model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])
    model.compile(optimizer=optimizer, loss=keras.losses.MeanSquaredLogarithmicError(), metrics=['accuracy'])
    return model