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

时间:2018-10-21 18:38:33

标签: machine-learning keras deep-learning conv-neural-network activation-function

最终目标:对象中点计算。

我有一个小的数据集(约120张图像),其中有一个对象(在所有情况下都相同),标签是图像中对象中点的归一化x,y坐标(始终在0到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)

而不是线性激活(通常在尝试获得回归结果时建议这样做)

对于损失函数,我使用了带有“ rmsprop”优化器的MSE,除了准确性和MSE外,我还编写了一个自定义指标来告诉我预测点是否与标签相距超过5% >

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

在大约150个时期训练模型后,我没有得到很好的结果(我也尝试了不同的批量大小)

我应该将激活层更改为线性吗?还是可以对模型进行其他修改?还是ResNet完全不适合此任务?

2 个答案:

答案 0 :(得分:1)

您的任务与对象检测有关。区别在于,每个图像中似乎只有一个对象,而在检测中可能有多个对象或不存在任何对象。对于对象检测,有诸如YOLOv3(https://pjreddie.com/media/files/papers/YOLOv3.pdf)或Single Shot Multibox Detector-SSD(https://arxiv.org/pdf/1512.02325.pdf)之类的网络,但是ResNet也可以作为对象检测网络进行训练(如本文所述:{{ 3}})

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

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

原则上,您的设置对我来说很好。但是有很多事情可能导致性能降低,因为您没有告知数据集的范围:您使用的是预先训练的网络还是从头开始训练?它是您要学习的新类别还是网络以前见过的对象类别?等等

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

  • 将优化程序(更改为SGD或Adam)
  • 更改学习速度(小于大于零)
  • 增加数据集的大小。为了为新的对象类别重新训练网络,我的经验法则是使用大约500-1000张图像。要从头进行再培训,您还需要几个数量级。
  • 您可能要检查YOLO或SSD并根据情况修改这些网络

希望您能从中得到启发。

答案 1 :(得分:0)

除了您已完成的工作外,您还可以做很多其他事情:

  1. 使用ImageAugmentation技术生成更多数据。另外,对图像进行归一化。
  2. 制作一个具有更多卷积层的更深层次的模型。
  3. 对于卷积层,使用适当的 weight初始值设定项(也许是He-normal)。
  4. 在各层之间使用BatchNormalization,以使过滤器值的平均值 std 分别等于0和1。
  5. 尝试使用交叉熵损失,因为它有助于更​​好地计算梯度。在MSE中,梯度会随着时间的流逝而变得非常小,尽管它是回归问题的首选。您也可以尝试MeanSquaredLogarithmicError
  6. 尝试将优化程序更改为 Adam 具有Nestrov动量的随机梯度下降(在验证集上的表现优于Adam)。
  7. 以防万一,您的数据集中还有更多的类,并且存在类不平衡问题,您可以使用聚焦损失,它是交叉熵损失的一种变体,对错误分类的标签的处罚要比对正确分类的惩罚更大。分类标签。另外,减小批量大小上采样也应有所帮助。
  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