手部关键点坐标神经网络不收敛。

3
我目前正在尝试使用tensorflow训练自定义模型,在图像上检测2只手中的17个地标/关键点(指尖,第一关节,底部关节,手腕和手掌),共计34个点(因此需要预测68个值的x和y)。然而,我无法使模型收敛,输出结果是一个几乎对于每个预测都相同的点数组。
我开始使用的数据集包含这样的图像: enter image description here 每个关键点都有对应的红点进行注释。为了扩大数据集以尝试获得更强大的模型,我拍摄了各种背景、角度、位置、姿势、光照条件、反射率等手部照片,如下图所示:enter image description hereenter image description here enter image description here enter image description here enter image description here enter image description here

我现在已经创建了大约3000张图片,其中地标信息存储在csv文件中,格式如下:

enter image description here

我有一个0.67的训练集和0.33的测试集,图片是随机选择的。我加载了所有三个颜色通道的图像,并将颜色值和关键点坐标缩放到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左右,无论我运行多少个时期: 在此输入图像描述 此外,当我基于该模型运行预测时,似乎每个图像的预测输出基本相同,仅在每个图像之间略有变化。看起来,该模型预测了一个坐标数组,看起来有点像展开的手,位于最有可能找到手的一般区域。这是一种适用于所有图像的综合解决方案,而不是针对每个图像的定制解决方案。这些图像说明了这一点,绿色表示预测点,红色表示左手的实际点: enter image description here enter image description here enter image description here enter image description here 所以,我想知道是什么导致了这个问题,是模型、数据还是两者都有,因为我尝试修改模型或增强数据都没有效果。我甚至尝试将复杂度降低到仅预测一个手的情况,预测每只手的边界框,或预测单个关键点,但无论我尝试什么,结果都相当不准确。
因此,如果您有任何建议可以帮助模型收敛并创建更准确和定制的预测,以便对其看到的每个手图像进行预测,将非常感激。
谢谢,
山姆

这是一个异常复杂的问题。考虑简化它。 - Christoph Rackwitz
1个回答

1
通常,神经网络很难准确预测地标的精确坐标。更好的方法可能是完全卷积网络。具体做法如下:
  1. 您省略了末尾的密集层,因此最终输出为(m,n,n_filters),其中m和n是您下采样特征映射的维度(由于在网络的某个早期阶段使用了maxpooling,它们的分辨率比输入图像低)。
  2. 您将最后一层(输出层)的n_filters设置为要检测的不同地标数量再加一表示没有地标。
  3. 您删除了一些最大池化,以使您的最终输出具有相当高的分辨率(因此早期提到的m和n更大)。现在,您的输出形状为mxnx(n_landmarks+1),每个nxm(n_landmark+1)维向量指示哪个地标存在于与mxn网格中的位置相对应的图像位置。因此,您最后一个输出卷积层的激活需要是softmax来表示概率。
  4. 现在,您可以训练您的网络在不使用密集层的情况下本地预测地标。
这是一种非常简单的架构,为了获得最佳结果,可能需要更复杂的架构,但我认为这应该给您提供一个比使用密集层进行预测更好的方法的第一个想法。
至于为什么您的网络每次都会预测相同的值的解释:这可能是因为您的网络无法学习您想要它学习的内容,因为它不适合这样做。如果是这种情况,网络将只学习预测一个值,该值对大多数图像都相当不错(因此基本上是所有图像中每个地标的“平均”位置)。

1
我明白了。感谢@Marc Felix的建议。这很有趣。我最初尝试了与您建议类似的方法,但那时候数据量较少,结果似乎并不好,所以我转而采用了密集的方法。现在我有了更多的数据,我会尝试您的建议,并报告结果。 - Sam Skinner
1
感谢@Marc Felix的回答!使用完全卷积神经网络,其输出是一个(1,1,1,68)张量,通过使用池化和卷积层将其转换为该形状,而不是密集层,创建了一个输出针对每个图像的模型。结果并不完美,但我相信我可以通过更多的数据来改进它。否则,该模型按预期工作。感谢您解释问题,并再次感谢您提供的解决方案! - Sam Skinner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接