Keras 一维分割模型总是对偶数个项目进行分类
Keras 1D segmentation model always classifies even number of items
我正在尝试训练一维 CNN 来识别文本字符串的特定部分。
输入是 (128,1)
形状的数组,包含 128 个字符,目的是让网络 class 将每个字符化为特定的 class。为了便于说明,输入数组可能如下所示:
array(['3', '!', 'd', 'o', 'g', '.', '?', '8', '7', 'a', 'p', 'p', 'l',
'e', 'f', 'd', '$', '5'], dtype='<U1')
相应的标签如下所示:
array([0, 0, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0])
想法是网络将 class 将字符 "d", "o", "g"
化为 class 1
(比如,动物),将 "a", "p", "p", "l", "e"
化为 class2
(水果)剩下的变成class0
.
计划构建一个架构类似于 U-Net 的网络,但现在我正在试验一个非常简单的 downsample/upsample 网络,如下所示:
def get_model(seq_size,n_classes):
inputs = tf.keras.Input(shape=seq_size)
# Downsample phase
x = tf.keras.layers.Conv1D(32,11,padding="same")(inputs)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.MaxPooling1D(2,padding="same")(x)
x = tf.keras.layers.Conv1D(64,5,padding="same")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.MaxPooling1D(2,padding="same")(x)
# Upsample phase
x = tf.keras.layers.Conv1DTranspose(128,5,padding="same")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.UpSampling1D(2)(x)
x = tf.keras.layers.Conv1DTranspose(256,7,padding="same")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.UpSampling1D(2)(x)
outputs = tf.keras.layers.Conv1D(n_classes,1,activation="softmax",padding="same")(x)
model = tf.keras.Model(inputs,outputs)
return model
输入形状为 (128,1)
和 n_classes = 5
。
该模型对于基线工作得很好,但它有一个有趣的怪癖,我正在努力解决这个问题:当它对字符进行预测时,它总是 class确定偶数个字符(或“像素”,如果将其视为类似于图像分割任务)。所以在上面的例子中,它会识别 !dog
或 dog.
属于 class 1,而 7apple
或 applef
属于 class 2 .
只有当单词包含奇数个字符时才会出现问题,这让我认为这可能与最大池化和上采样操作有关。我试图通过了解这些操作在 Keras 中的工作方式来找到答案,但这并没有取得成果。因此,如果有人能阐明为什么预测总是偶数个字符,以及我该如何纠正,我将不胜感激!
编辑 来自评论中的建议:
澄清一下,数组仅使用 ord
函数进行编码,然后 min/max 归一化到范围 0:1.
我使用稀疏分类交叉熵作为损失函数,训练设置如下:
loss = tf.keras.losses.SparseCategoricalCrossentropy()
opt = tf.keras.optimizers.Adam()
model.compile(optimizer=opt,loss=loss,metrics=["accuracy"])
callbacks = [tf.keras.callbacks.ModelCheckpoint("trial.h5",save_best_only=True)]
epochs = 10
model.fit(train_gen, epochs=epochs, validation_data=test_gen, callbacks=callbacks)
其中 train_gen
和 test_gen
是构建为 tf.keras.utils.Sequence
subclass 的数据生成器。
我认为当您使用 UpSampling1D
时,每个值都会重复两次。这意味着最后一步的输入包含 pair-wise 个重复值。然后它将为相邻字符给出相同的预测 class 。如果我的猜测是正确的,您将始终看到 2k 和 2k+1 个字符的相同预测。
您可以通过检查
中的输入 x
来确认
outputs = tf.keras.layers.Conv1D(n_classes,1,activation="softmax",padding="same")(x)
它应该看起来像 [a, a, b, b, c, c, ...]
要解决此问题,您可能可以在 outputs = ...
和 x = tf.keras.layers.UpSampling1D(2)(x)
之间添加一个额外的步骤
我正在尝试训练一维 CNN 来识别文本字符串的特定部分。
输入是 (128,1)
形状的数组,包含 128 个字符,目的是让网络 class 将每个字符化为特定的 class。为了便于说明,输入数组可能如下所示:
array(['3', '!', 'd', 'o', 'g', '.', '?', '8', '7', 'a', 'p', 'p', 'l',
'e', 'f', 'd', '$', '5'], dtype='<U1')
相应的标签如下所示:
array([0, 0, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0])
想法是网络将 class 将字符 "d", "o", "g"
化为 class 1
(比如,动物),将 "a", "p", "p", "l", "e"
化为 class2
(水果)剩下的变成class0
.
计划构建一个架构类似于 U-Net 的网络,但现在我正在试验一个非常简单的 downsample/upsample 网络,如下所示:
def get_model(seq_size,n_classes):
inputs = tf.keras.Input(shape=seq_size)
# Downsample phase
x = tf.keras.layers.Conv1D(32,11,padding="same")(inputs)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.MaxPooling1D(2,padding="same")(x)
x = tf.keras.layers.Conv1D(64,5,padding="same")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.MaxPooling1D(2,padding="same")(x)
# Upsample phase
x = tf.keras.layers.Conv1DTranspose(128,5,padding="same")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.UpSampling1D(2)(x)
x = tf.keras.layers.Conv1DTranspose(256,7,padding="same")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation("relu")(x)
x = tf.keras.layers.UpSampling1D(2)(x)
outputs = tf.keras.layers.Conv1D(n_classes,1,activation="softmax",padding="same")(x)
model = tf.keras.Model(inputs,outputs)
return model
输入形状为 (128,1)
和 n_classes = 5
。
该模型对于基线工作得很好,但它有一个有趣的怪癖,我正在努力解决这个问题:当它对字符进行预测时,它总是 class确定偶数个字符(或“像素”,如果将其视为类似于图像分割任务)。所以在上面的例子中,它会识别 !dog
或 dog.
属于 class 1,而 7apple
或 applef
属于 class 2 .
只有当单词包含奇数个字符时才会出现问题,这让我认为这可能与最大池化和上采样操作有关。我试图通过了解这些操作在 Keras 中的工作方式来找到答案,但这并没有取得成果。因此,如果有人能阐明为什么预测总是偶数个字符,以及我该如何纠正,我将不胜感激!
编辑 来自评论中的建议:
澄清一下,数组仅使用 ord
函数进行编码,然后 min/max 归一化到范围 0:1.
我使用稀疏分类交叉熵作为损失函数,训练设置如下:
loss = tf.keras.losses.SparseCategoricalCrossentropy()
opt = tf.keras.optimizers.Adam()
model.compile(optimizer=opt,loss=loss,metrics=["accuracy"])
callbacks = [tf.keras.callbacks.ModelCheckpoint("trial.h5",save_best_only=True)]
epochs = 10
model.fit(train_gen, epochs=epochs, validation_data=test_gen, callbacks=callbacks)
其中 train_gen
和 test_gen
是构建为 tf.keras.utils.Sequence
subclass 的数据生成器。
我认为当您使用 UpSampling1D
时,每个值都会重复两次。这意味着最后一步的输入包含 pair-wise 个重复值。然后它将为相邻字符给出相同的预测 class 。如果我的猜测是正确的,您将始终看到 2k 和 2k+1 个字符的相同预测。
您可以通过检查
中的输入x
来确认
outputs = tf.keras.layers.Conv1D(n_classes,1,activation="softmax",padding="same")(x)
它应该看起来像 [a, a, b, b, c, c, ...]
要解决此问题,您可能可以在 outputs = ...
和 x = tf.keras.layers.UpSampling1D(2)(x)