手部地标坐标神经网络不收敛
Hand Landmark Coordinate Neural Network Not Converging
我目前正在尝试使用 tensorflow 训练自定义模型以检测图像中显示的 2 只手(指尖、第一指关节、下指关节、手腕和手掌)中的每只手上的 17 landmarks/keypoints,用于34 个点(因此要预测 x 和 y 的总值 68 个)。但是,我无法让模型收敛,输出是一个点数组,每个预测都几乎相同。
我从一个包含如下图像的数据集开始:
每个注释都有红点与每个关键点相关。为了扩展数据集以尝试获得更强大的模型,我拍摄了具有不同背景、角度、位置、姿势、光照条件、反射率等的手的照片,如这些进一步的图像所示:
我现在创建了大约 3000 张图像,地标存储在 csv 中:
我有一个 .67 训练 .33 测试的训练测试拆分,每个测试随机选择图像。我用所有 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 左右,无论我有多少个 epochs 运行:
此外,当我 运行 基于模型进行预测时,似乎每个图像的预测输出基本相同,每个图像之间只有微小的差异。该模型似乎预测了一组坐标,看起来有点像张开的手,在一般区域最有可能找到手。与针对每个图像的自定义解决方案相反,可最大限度地减少偏差的全能解决方案。这些图像说明了这一点,绿色是预测点,红色是左手的实际点:
所以,我想知道是什么导致了这种情况,是模型、数据还是两者兼而有之,因为我尝试修改模型或扩充数据似乎都没有任何好处。我什至尝试降低复杂性以仅预测一只手,预测每只手的边界框,并预测单个关键点,但无论我尝试什么,结果都非常不准确。
因此,对于我可以做些什么来帮助模型收敛以为它看到的每张手图像创建更准确和自定义的预测的任何建议,我们将不胜感激。
谢谢,
山姆
通常,神经网络很难预测地标的精确坐标。更好的方法可能是全卷积网络。这将按如下方式工作:
- 您在最后省略了密集层,因此最终得到 (m, n, n_filters) 的输出,其中 m 和 n 是下采样特征图的维度(因为您在在网络的某些早期阶段,它们的分辨率将低于您的输入图像)。
- 您将最后一个(输出)层的 n_filters 设置为您要检测的不同地标的数量,再加上一个表示没有地标。
- 您删除了一些最大池化,使您的最终输出具有相当高的分辨率(因此之前引用的 m 和 n 更大)。现在您的输出具有形状 mxnx(n_landmarks+1) 并且每个 nxm (n_landmark+1) 维向量表示哪个地标作为图像中的位置对应于图像中的位置墨西哥网格。因此,最后一个输出卷积层的激活需要是一个 softmax 来表示概率。
- 现在您可以训练您的网络在本地预测地标,而无需使用密集层。
这是一个非常简单的架构,为了获得最佳结果,可能需要更复杂的架构,但我认为这应该让您初步了解比使用密集层进行预测更好的方法。
以及为什么您的网络每次都预测相同值的原因:这可能是因为您的网络无法学习您希望它学习的内容,因为它不适合这样做。如果是这种情况,网络将只学习预测一个值,这对大多数图像来说都相当好(所以基本上是所有图像的每个地标的“平均”位置)。
我目前正在尝试使用 tensorflow 训练自定义模型以检测图像中显示的 2 只手(指尖、第一指关节、下指关节、手腕和手掌)中的每只手上的 17 landmarks/keypoints,用于34 个点(因此要预测 x 和 y 的总值 68 个)。但是,我无法让模型收敛,输出是一个点数组,每个预测都几乎相同。
我从一个包含如下图像的数据集开始:
每个注释都有红点与每个关键点相关。为了扩展数据集以尝试获得更强大的模型,我拍摄了具有不同背景、角度、位置、姿势、光照条件、反射率等的手的照片,如这些进一步的图像所示:
我现在创建了大约 3000 张图像,地标存储在 csv 中:
我有一个 .67 训练 .33 测试的训练测试拆分,每个测试随机选择图像。我用所有 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 左右,无论我有多少个 epochs 运行:
此外,当我 运行 基于模型进行预测时,似乎每个图像的预测输出基本相同,每个图像之间只有微小的差异。该模型似乎预测了一组坐标,看起来有点像张开的手,在一般区域最有可能找到手。与针对每个图像的自定义解决方案相反,可最大限度地减少偏差的全能解决方案。这些图像说明了这一点,绿色是预测点,红色是左手的实际点:
所以,我想知道是什么导致了这种情况,是模型、数据还是两者兼而有之,因为我尝试修改模型或扩充数据似乎都没有任何好处。我什至尝试降低复杂性以仅预测一只手,预测每只手的边界框,并预测单个关键点,但无论我尝试什么,结果都非常不准确。
因此,对于我可以做些什么来帮助模型收敛以为它看到的每张手图像创建更准确和自定义的预测的任何建议,我们将不胜感激。
谢谢,
山姆
通常,神经网络很难预测地标的精确坐标。更好的方法可能是全卷积网络。这将按如下方式工作:
- 您在最后省略了密集层,因此最终得到 (m, n, n_filters) 的输出,其中 m 和 n 是下采样特征图的维度(因为您在在网络的某些早期阶段,它们的分辨率将低于您的输入图像)。
- 您将最后一个(输出)层的 n_filters 设置为您要检测的不同地标的数量,再加上一个表示没有地标。
- 您删除了一些最大池化,使您的最终输出具有相当高的分辨率(因此之前引用的 m 和 n 更大)。现在您的输出具有形状 mxnx(n_landmarks+1) 并且每个 nxm (n_landmark+1) 维向量表示哪个地标作为图像中的位置对应于图像中的位置墨西哥网格。因此,最后一个输出卷积层的激活需要是一个 softmax 来表示概率。
- 现在您可以训练您的网络在本地预测地标,而无需使用密集层。
这是一个非常简单的架构,为了获得最佳结果,可能需要更复杂的架构,但我认为这应该让您初步了解比使用密集层进行预测更好的方法。
以及为什么您的网络每次都预测相同值的原因:这可能是因为您的网络无法学习您希望它学习的内容,因为它不适合这样做。如果是这种情况,网络将只学习预测一个值,这对大多数图像来说都相当好(所以基本上是所有图像的每个地标的“平均”位置)。