即使我的 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 并根据您的情况修改这些网络
希望您能为您的解决方案找到一些灵感。
除了您所做的,您还可以做很多其他事情:
- 使用ImageAugmentation技术生成更多数据。另外,规范化图像。
- 用更多的卷积层制作更深的模型。
- 对卷积层使用适当的权重初始化器可能是 He-normal。
- 在层之间使用 BatchNormalization 使过滤器值的 mean 和 std 分别等于 0 和 1。
- 尝试使用交叉熵损失,因为它有助于更好地计算梯度。在 MSE 中,梯度随着时间的推移变得非常小,尽管它更适合回归类问题。您也可以尝试 MeanSquaredLogarithmicError.
- 尝试将优化器更改为 Adam 或 Stochastic Gradient Descent with nestrov momentum(在验证集上比 Adam 表现更好)。
- 如果你的数据集中有更多的classes,并且你有class不平衡问题,你可以使用Focal loss,交叉熵损失的一种变体,它对错误 class 化标签的惩罚比正确 class 化标签更多。此外,减少 批量大小 和 上采样 应该会有所帮助。
- 使用贝叶斯优化技术对模型进行超参数调整。
这里是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
最终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 并根据您的情况修改这些网络
希望您能为您的解决方案找到一些灵感。
除了您所做的,您还可以做很多其他事情:
- 使用ImageAugmentation技术生成更多数据。另外,规范化图像。
- 用更多的卷积层制作更深的模型。
- 对卷积层使用适当的权重初始化器可能是 He-normal。
- 在层之间使用 BatchNormalization 使过滤器值的 mean 和 std 分别等于 0 和 1。
- 尝试使用交叉熵损失,因为它有助于更好地计算梯度。在 MSE 中,梯度随着时间的推移变得非常小,尽管它更适合回归类问题。您也可以尝试 MeanSquaredLogarithmicError.
- 尝试将优化器更改为 Adam 或 Stochastic Gradient Descent with nestrov momentum(在验证集上比 Adam 表现更好)。
- 如果你的数据集中有更多的classes,并且你有class不平衡问题,你可以使用Focal loss,交叉熵损失的一种变体,它对错误 class 化标签的惩罚比正确 class 化标签更多。此外,减少 批量大小 和 上采样 应该会有所帮助。
- 使用贝叶斯优化技术对模型进行超参数调整。
这里是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