如何将分离的 tfrecord 数据集:trainX图像和 trainY标签传递给 model.fit()?

问题描述

问题

由于我的模型中有一个自定义层,我需要在训练期间将标签和图像一起传递给模型。所以这就是我调用 fit 方法的方式:

history = model.fit((trainX,trainY),trainY,epochs=epochs,batch_size=batch_size,validation_data=((testX,testY),callbacks=callbacks,shuffle=True)

当 trainX、trainY、testX 和 testY 是 numpy 数组时,我可以毫无问题地训练模型。 所以我认为如果我分别创建它们每个的 tfrecords,加载它们并将它们传递给 fit 方法,它会起作用。但我收到以下错误

ValueError: `y` argument is not supported when using dataset as input.

我了解错误发生的原因,但我正在寻找解决方案。我的训练数据集很大,我无法将它们作为 numpy 数组加载到内存中。

我也知道数据集应该只作为一个参数传递,例如:

history = model.fit(train_dataset,validation_data=val_dataset,shuffle=True)

然而,这没有用,因为我需要将标签传递给模型。所以这就是为什么我想到了拆分数据集并像 (trainX,trainY 一样传递它的想法。

无论如何可以做我想做的事吗?我更喜欢 tfrecords,因为与从目录中读取图像作为文件相比,它们速度更快,而且不需要一次加载整个文件

我的解决方

我找到了一个解决方案,现在可以解决我的问题,那就是使用 tf.GradientTape() 实现一个完全自定义的训练循环:

@tf.function
def train_step(x,y):
    with tf.GradientTape() as tape:
        # Here you pass arguments in your desired way
        logits = model((x,y),training=True)
        loss_value = loss_fn(y,logits)
    grads = tape.gradient(loss_value,model.trainable_weights)
    opt.apply_gradients(zip(grads,model.trainable_weights))
    train_acc_metric.update_state(y,logits)
    return loss_value

# Prepare the loss function
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# Prepare the metrics.
train_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy()
val_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy()

for epoch in range(epochs):
    epoch_losses = []
    for img,label in zip(trainX,trainY):
        loss_value = train_step(img,label)
        epoch_losses.append(loss_value.numpy())
    
    # display metrics at the end of each epoch.
    train_acc = train_acc_metric.result()
    train_epoch_loss = np.mean(epoch_losses)
    
    # Reset training metrics at the end of each epoch
    train_acc_metric.reset_states()

解决方法

为了社区的利益,我在这里提供@Niel_Eenterm 解决方案

我找到了一个解决方案,现在可以解决我的问题,那就是 使用 tf.GradientTape() 实现完全自定义的训练循环:

@tf.function
def train_step(x,y):
    with tf.GradientTape() as tape:
        # Here you pass arguments in your desired way
        logits = model((x,y),training=True)
        loss_value = loss_fn(y,logits)
    grads = tape.gradient(loss_value,model.trainable_weights)
    opt.apply_gradients(zip(grads,model.trainable_weights))
    train_acc_metric.update_state(y,logits)
    return loss_value

# Prepare the loss function
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# Prepare the metrics.
train_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy()
val_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy()

for epoch in range(epochs):
    epoch_losses = []
    for img,label in zip(trainX,trainY):
        loss_value = train_step(img,label)
        epoch_losses.append(loss_value.numpy())
    
    # Display metrics at the end of each epoch.
    train_acc = train_acc_metric.result()
    train_epoch_loss = np.mean(epoch_losses)
    
    # Reset training metrics at the end of each epoch
    train_acc_metric.reset_states()