手地标坐标神经网络不收敛

Posted

技术标签:

【中文标题】手地标坐标神经网络不收敛【英文标题】:Hand Landmark Coordinate Neural Network Not Converging 【发布时间】:2021-12-05 12:41:08 【问题描述】:

我目前正在尝试使用 tensorflow 训练一个自定义模型,以检测图像中显示的 2 只手(指尖、第一指关节、下指关节、手腕和手掌)中每只手的 17 个地标/关键点,检测 34 个点(以及因此要预测 x 和 y 的 68 个总值)。但是,我无法让模型收敛,而是输出一个点数组,每个预测都几乎相同。

我从一个包含如下图像的数据集开始:

每个都被注释为使红点与每个关键点相关。为了扩展数据集以尝试获得更稳健的模型,我拍摄了具有各种背景、角度、位置、姿势、照明条件、反射率等的手的照片,如下图所示:

我现在创建了大约 3000 张图像,地标存储在 csv 中:

我有一个 .67 train .33 test 的训练测试拆分,每个图像都是随机选择的。我使用所有 3 个颜色通道加载图像,并在 0 和 1 之间缩放颜色值和关键点坐标。

我尝试了几种不同的方法,每种方法都涉及 CNN。第一个保持图像原样,并使用这样构建的神经网络模型:

model = Sequential()

model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'same', activation = 'relu', input_shape = (225,400,3)))
model.add(Conv2D(filters = 64, kernel_size = (3,3), padding = 'same', activation = 'relu'))
model.add(MaxPooling2D(pool_size = (2,2), strides = 2))

filters_convs = [(128, 2), (256, 3), (512, 3), (512,3)]
  
for n_filters, n_convs in filters_convs:
  for _ in np.arange(n_convs):
    model.add(Conv2D(filters = n_filters, kernel_size = (3,3), padding = 'same', activation = 'relu'))
  model.add(MaxPooling2D(pool_size = (2,2), strides = 2))

model.add(Flatten())
model.add(Dense(128, activation="relu"))
model.add(Dense(96, activation="relu"))
model.add(Dense(72, activation="relu"))
model.add(Dense(68, activation="sigmoid"))

opt = Adam(learning_rate=.0001)
model.compile(loss="mse", optimizer=opt, metrics=['mae'])
print(model.summary())

我已经修改了各种超参数,但似乎没有任何明显的区别。

我尝试过的另一件事是调整图像大小以适应 224x224x3 数组,以便与 VGG-16 网络一起使用,例如:

vgg = VGG16(weights="imagenet", include_top=False,
    input_tensor=Input(shape=(224, 224, 3)))
vgg.trainable = False

flatten = vgg.output
flatten = Flatten()(flatten)

points = Dense(256, activation="relu")(flatten)
points = Dense(128, activation="relu")(points)
points = Dense(96, activation="relu")(points)
points = Dense(68, activation="sigmoid")(points)

model = Model(inputs=vgg.input, outputs=points)

opt = Adam(learning_rate=.0001)
model.compile(loss="mse", optimizer=opt, metrics=['mae'])
print(model.summary())

此模型与第一个模型的结果相似。无论我做什么,我似乎都得到了相同的结果,因为我的 mse 损失最小化大约 0.009,mae 大约 0.07,无论我运行多少个 epoch:

此外,当我根据模型运行预测时,似乎每个图像的预测输出基本相同,每个图像之间只有细微的差异。似乎该模型预测的坐标数组看起来有点像张开的手,在一般区域中最有可能找到手。与针对每个图像的自定义解决方案相比,可以最大限度地减少偏差的包罗万象的解决方案。这些图像说明了这一点,绿色是预测点,红色是左手的实际点:

所以,我想知道是什么导致了这种情况,无论是模型、数据还是两者兼而有之,因为我尝试修改模型或扩充数据似乎都没有任何好处。我什至尝试降低仅预测一只手的复杂度,预测每只手的边界框,并预测单个关键点,但无论我尝试什么,结果都非常不准确。

因此,对于我可以做些什么来帮助模型收敛以便为它看到的每张手的图像创建更准确和自定义的预测的任何建议都将非常感激。

谢谢,

山姆

【问题讨论】:

这是一个异常复杂的问题。考虑简化它。 【参考方案1】:

通常,神经网络很难预测地标的准确坐标。更好的方法可能是全卷积网络。这将按如下方式工作:

    您在最后省略了密集层,因此最终得到 (m, n, n_filters) 的输出,其中 m 和 n 是您的下采样特征图的维度(因为您在网络的某个早期阶段使用了 maxpooling它们的分辨率将低于您的输入图像)。 您将最后(输出)层的 n_filters 设置为您要检测的不同地标的数量,再加上一个表示没有地标。 您删除了一些最大池,以便最终输出具有相当高的分辨率(因此之前引用的 m 和 n 更大)。现在您的输出具有 mxnx(n_landmarks+1) 形状,并且每个 nxm (n_landmark+1) 维向量指示哪个地标作为图像中与 mxn 网格中的位置相对应的位置存在。因此,最后一个输出卷积层的激活需要是一个 softmax 来表示概率。 现在您可以训练您的网络在本地预测地标,而无需使用密集层。

这是一个非常简单的架构,为了获得最佳结果,可能需要更复杂的架构,但我认为这应该让您初步了解比使用密集层进行预测更好的方法。

关于为什么您的网络每次都预测相同的值的原因:这可能是因为您的网络无法学习您希望它学习的内容,因为它不适合这样做。如果是这种情况,网络只会学习预测一个值,这对于大多数图像来说是相当好的(所以基本上是所有图像的每个地标的“平均”位置)。

【讨论】:

我明白了。感谢@Marc Felix 的输入,这很有趣。我最初尝试过与您建议的类似的东西,但那是早期的,而且数据少得多。因此结果似乎不是那么好,所以我转向密集的方法。既然我有更多数据并报告结果,我会试试你的建议。 感谢@Marc Felix 的回答!使用完全卷积神经网络,其输出为 (1, 1, 1, 68) 张量,通过使用池化和卷积层将其变为该形状而不是密集层,确实创建了一个模型,其输出针对每个图片。结果并不完美,但我相信我可以通过更多数据来改进它。否则,模型将按预期工作。感谢您对问题的解释,再次感谢您的解决方案!

以上是关于手地标坐标神经网络不收敛的主要内容,如果未能解决你的问题,请参考以下文章

当神经网络不收敛时要尝试的事情

模型不收敛的原因

神经网络不收敛的 11 个原因及其解决办法

Pytorch 深度卷积网络在 CIFAR10 上不收敛

神经网络 - 输出收敛到 0,python

GPS坐标转高德地标(火星坐标/国测坐标)