我的问题:
我正在尝试在 tf.keras 中训练语义分割模型,事实上,当我使用 channels_last (WHC) 模式时它运行良好(它达到 96%+ val acc)。我想在 channels_first (CHW) 模式下训练它,以便权重与 TensorRT 兼容。当我这样做时,前几个 epoch 中约 80% 的训练准确率下降到 0.020% 左右并永久保持在那里。
知道我的模型的基础是tf.keras.applications.MobileNet()
具有预先训练的“imagenet”权重的模型很有用。(底部的模型架构。)
改造过程:
我使用了提供的指南,在这里只更改了几件事:
- 设置
tf.keras.backend.set_image_data_format()
为“channels_first”。 - 我将输入张量中的通道顺序从:
input_tensor=Input(shape=(376, 672, 3))
更改为:input_tensor=Input(shape=(3, 376, 672))
- 在我的图像预处理(使用
tf.data.Dataset
)中,我tf.transpose(img, perm=[2, 0, 1])
在输入图像和单热编码掩码上都使用了来更改通道顺序。我用相等断言检查了这个,以确保它正确并且看起来很好。
当我更改这些时,训练开始正常,但正如我所说,训练准确度几乎下降到零。当我恢复一切时,一切都很好。
可能的线索:
我做错了什么或者这里有什么问题?我的怀疑是围绕这些问题:
- 当我设置后端时,预训练的 imageNet 权重是否也更改为“channels_first”顺序?这是我应该考虑的事情吗?
- 会不会是
tf.transpose()
函数弄乱了掩码的 one-hot 编码?(我有 3 个类别由 3 种颜色表示:车道、对面车道、背景)
也许我没有看到明显的东西。我可以根据需要提供进一步的代码和答案。
编辑:
08/17:这仍然是一个持续的问题,我尝试了几件事:
- 我用 numpy 断言转置后检查了图像和掩码是否正确,似乎正确。
- 我怀疑损失函数在错误的轴上计算,所以我为第一个轴(通道所在的位置)定制了损失函数。这里是:
def ReverseAxisLoss(y_true, y_pred):
return K.categorical_crossentropy(y_true, y_pred, from_logits=True, axis=1)
- 我的主要怀疑是“通道优先”后端设置对移动网络部分的预训练“imagenet”权重没有任何作用。TF2.x / Keras 是否有更新的方法将预训练的权重转换为 CHW 格式?
这里是我使用的架构(theskipNet()
是head network,mobilenet是base,在create_model()
function里面是connected)
def skipNet(encoder_output, feed1, feed2, classes):
# random initializer and regularizer
stddev = 0.01
init = RandomNormal(stddev=stddev)
weight_decay = 1e-3
reg = l2(weight_decay)
score_feed2 = Conv2D(kernel_size=(1, 1), filters=classes, padding="SAME",
kernel_initializer=init, kernel_regularizer=reg)(feed2)
score_feed2_bn = BatchNormalization()(score_feed2)
score_feed1 = Conv2D(kernel_size=(1, 1), filters=classes, padding="SAME",
kernel_initializer=init, kernel_regularizer=reg)(feed1)
score_feed1_bn = BatchNormalization()(score_feed1)
upscore2 = Conv2DTranspose(kernel_size=(4, 4), filters=classes, strides=(2, 2),
padding="SAME", kernel_initializer=init,
kernel_regularizer=reg)(encoder_output)
height_pad1 = ZeroPadding2D(padding=((1,0),(0,0)))(upscore2)
upscore2_bn = BatchNormalization()(height_pad1)
fuse_feed1 = add([score_feed1_bn, upscore2_bn])
upscore4 = Conv2DTranspose(kernel_size=(4, 4), filters=classes, strides=(2, 2),
padding="SAME", kernel_initializer=init,
kernel_regularizer=reg)(fuse_feed1)
height_pad2 = ZeroPadding2D(padding=((0,1),(0,0)))(upscore4)
upscore4_bn = BatchNormalization()(height_pad2)
fuse_feed2 = add([score_feed2_bn, upscore4_bn])
upscore8 = Conv2DTranspose(kernel_size=(16, 16), filters=classes, strides=(8, 8),
padding="SAME", kernel_initializer=init,
kernel_regularizer=reg, activation="softmax")(fuse_feed2)
return upscore8
def create_model(classes):
base_model = tf.keras.applications.MobileNet(input_tensor=Input(shape=IMG_SHAPE),
include_top=False,
weights='imagenet')
conv4_2_output = base_model.get_layer(index=43).output
conv3_2_output = base_model.get_layer(index=30).output
conv_score_output = base_model.output
head_model = skipNet(conv_score_output, conv4_2_output, conv3_2_output, classes)
for layer in base_model.layers:
layer.trainable = False
model = Model(inputs=base_model.input, outputs=head_model)
return model