【发布时间】:2020-10-01 12:20:44
【问题描述】:
我想从头开始训练一个多出多类分类模型(使用自定义fit())。我想要一些建议。为了学习机会,在这里我将更详细地展示整个场景。希望它对任何人都有帮助。
数据集和目标
我正在使用来自here 的数据;这是一个孟加拉语手写字符识别挑战,每个样本都有 3 个相互关联的输出以及每个样本的多个类。请看下图:
在上图中,如您所见,ক্ট্রো由3个组件(ক্ট、ো、্র)组成,即字根、元音变音符号和辅音变音符号,它们一起被称为字形。 Grapheme Root 也有 168 个不同的类别,并且与其他类别相同(11 和 7)。增加的复杂性导致 ~13,000 个不同的字形变体(与英语的 250 个字形单位相比)。
目标是对每张图像中的字形组成进行分类。
初步方法(没有问题)
我在here 上实现了一个训练管道,它使用旧的keras(不是tf.keras)进行了演示,它具有方便的功能,例如model.compile、callbacks 等。我定义了一个custom data generator 和定义了一个类似于下面的模型架构。
input_tensor = Input(input_dim)
curr_output = base_model(input_tensor)
oputput1 = Dense(168, activation='softmax', name='gra') (curr_output)
oputput2 = Dense(11, activation='softmax', name='vow') (curr_output)
oputput3 = Dense(7, activation='softmax', name='cons') (curr_output)
output_tensor = [oputput1, oputput2, oputput3]
model = Model(input_tensor, output_tensor)
并编译模型如下:
model.compile(
optimizer = Adam(learning_rate=0.001),
loss = {'gra' : 'categorical_crossentropy',
'vow' : 'categorical_crossentropy',
'cons': 'categorical_crossentropy'},
loss_weights = {'gra' : 1.0,
'vow' : 1.0,
'cons': 1.0},
metrics={'gra' : 'accuracy',
'vow' : 'accuracy',
'cons': 'accuracy'}
)
如您所见,我可以使用特定的 loss、loss_weights 和 accuracy 清晰地控制每个输出。而使用.fit()方法,模型可以使用任意callbacks函数。
新方法(以及一些问题)
现在,我想用tf.keras 的新功能重新实现它。例如模型子类化和自定义拟合训练。但是,数据加载器没有变化。模型定义如下:
def __init__(self, dim):
super(Net, self).__init__()
self.efnet = EfficientNetB0(input_shape=dim,
include_top = False,
weights = 'imagenet')
self.gap = KL.GlobalAveragePooling2D()
self.output1 = KL.Dense(168, activation='softmax', name='gra')
self.output2 = KL.Dense(11, activation='softmax', name='vow')
self.output3 = KL.Dense(7, activation='softmax', name='cons')
def call(self, inputs, training=False):
x = self.efnet(inputs)
x = self.gap(x)
y_gra = self.output1(x)
y_vow = self.output2(x)
y_con = self.output3(x)
return [y_gra, y_vow, y_con]
现在我面临的主要问题是为我的每个输出正确定义metrics、loss 和loss_weights 函数。但是,我是这样开始的:
optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)
loss_fn = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
train_acc_metric = tf.keras.metrics.Accuracy()
@tf.function
def train_step(x, y):
with tf.GradientTape(persistent=True) as tape:
logits = model(x, training=True) # Logits for this minibatch
train_loss_value = loss_fn(y, logits)
grads = tape.gradient(train_loss_value, model.trainable_weights)
optimizer.apply_gradients(zip(grads, model.trainable_weights))
train_acc_metric.update_state(y, logits)
return train_loss_value
for epoch in range(2):
# Iterate over the batches of the dataset.
for step, (x_batch_train, y_batch_train) in enumerate(train_generator):
train_loss_value = train_step(x_batch_train, y_batch_train)
# Reset metrics at the end of each epoch
train_acc_metric.reset_states()
除了上述设置之外,我还尝试了其他许多方法来处理此类问题。例如,我定义了 3 个损失函数和 3 个指标,但事情并没有正常工作。 loss/acc 变成了 nan 类型的东西。
这是我在这种情况下的几个直接查询:
- 如何定义
loss、metrics和loss_weights - 如何有效利用所有
callbacks功能
而且只是为了学习的机会,如果它还有 regression 类型的输出(连同其余的 3 多输出,那么总共 4);如何在自定义fit 中处理所有这些?我访问过这个SO,给出了一些关于不同类型输出的提示(classification + regression)。
【问题讨论】:
-
您是否尝试过使用功能 API - 就像您使用标准
keras所做的那样(而不是模型子类化)? -
是的,正如我上面提到的,使用函数式API,这是我的notebook。
-
我的意思是使用
tf.keras的功能 api - 因为您使用keras和功能 API 的方法取得了成功,为什么不继续使用tf.keras中的功能 api? (如果我理解正确,你的目标是从keras移动到tf.keras) -
哦,对不起。我误解了。并感谢您的提问。是的,我也试过了。它按预期工作。仅供参考,
tf.keras、functional api+model.fit和model subclassing+model.fit工作得很好。我的最终目标是尝试了解tf.keras中针对此类问题案例的自定义fit方法。并且也在寻找一种方便的方式来利用callbacks函数的特性。 -
好吧,如果您不使用
.fit并使用自己的训练循环 - 回调是无关紧要的,因为您现在可以控制训练。关于自定义损失,我相信它是完全相同的 - 您可以从您的call()方法返回一个字典,然后在训练步骤中使用 3 种不同的损失来计算梯度并自己应用它们。这也允许您使用loss_weights
标签: python tensorflow machine-learning keras deep-learning