Tensorflow 自定义训练步骤失败 "Unexpected result of train_function"

Tensorflow custom training step fails with "Unexpected result of train_function"

我已根据 here 中描述的过程对 tensorflow.keras.models.Model class 进行了子class 编写并编写了自定义 train_step。该模型将两个二维数组作为输入(它是一个多输入模型)并产生一个浮点值作为输出。

我正在使用以下方法将 TFRecord 数据集传递给模型,其中 parse_element_func returns 一个包含 4 个项目的元组:(二维数组、二维数组、浮点数、浮点数)。第一项和第二项是输入数据,第三项是目标值,最后一项是自定义损失函数中使用的数字,因训练示例而异。这些项目中的每一项都在训练期间扩展了 1 个维度,因为它们是批处理的。

train_dataset = tf.data.TFRecordDataset(records_train).map(parse_element_func).batch(batch_size).prefetch(tf.data.AUTOTUNE)

class 看起来像这样:

import tensorflow.keras.backend as K
from tensorflow.keras.metrics import Mean
from tensorflow.keras.models import Model

loss_tracker = Mean(name="loss")
custom_metric_tracker = Mean(name="custom_metric")
magic_number = 4


class CustomModel(Model):


    def __init__(self, *args, clip_global_norm: float = 1.0, **kwargs):
        super(CustomModel, self).__init__(*args, **kwargs)
        self.clip_global_norm = clip_global_norm

    def train_step(self, data):
        # unpack data
        x_input_1, x_input_2, y_true, loss_modifier = data

        with tf.GradientTape() as tape:
            # predict
            y_pred = self((x_input_1, x_input_2), training=True)
            
            # calculate loss
            weights = K.pow(K.square(loss_modifier + magic_number), -1)
            squared_error = K.square(y_pred - y_true)
            loss = K.mean(weights * squared_error, axis=0)
            
            # calculate custom metric
            num = K.sum(K.square(y_pred - y_true), axis=0)
            denom = K.sum(y_true - K.mean(y_true), axis=0)
            custom_metric_value = 1 - num / (denom + 0.000001)  # to prevent being 0            

        trainable_vars = self.trainable_variables
        gradients = tape.gradient(loss, trainable_vars)

        gradients, _ = tf.clip_by_global_norm(gradients, clip_norm=self.clip_global_norm)

        self.optimizer.apply_gradients(zip(gradients, trainable_vars))

        loss_tracker.update_state(loss)
        custom_metric_tracker.update_state(custom_metric_value)

        return {"loss": loss_tracker.result(), "custom_metric": custom_metric_tracker.result()}

模型构建和编译都很好,我已经使用 plot_model 检查了所有形状是否正确。当我测试加载数据时,一切都以正确的形状和值存在。无论如何,我得到相同的 ValueError:

ValueError: Unexpected result of `train_function` (Empty logs).

这是我收到的唯一消息。除了与训练功能有关外,它没有告诉我任何错误,并且它发生在 model.fit 期间。当我调用它时,它在我的脚本中看起来像这样:

    train_dataset = tf.data.TFRecordDataset(records_train).map(parse_element_func).batch(batch_size).prefetch(tf.data.AUTOTUNE)
    val_dataset = tf.data.TFRecordDataset(records_val).map(parse_element_func).batch(batch_size).prefetch(tf.data.AUTOTUNE)

    model = build_model_func(**model_build_params)
    model.compile(optimizer="adam")

    history = model.fit(
        train_dataset,
        batch_size=batch_size,
        epochs=epochs,
        validation_data=val_dataset,
    )

我是否运行热切与否并没有什么不同。我想也许我的数据集传递一个包含 4 个值的元组可能是问题所在,但据我所知,通过文档它应该没问题,甚至我修改了 TFRecord 数据集元素解析器以仅提供输入和输出而不提供其他值(所以 2 个值而不是 4 个),我仍然得到同样的错误。

我在这上面花了好几个小时,只是不知道为什么会出现这个错误,也不知道这个函数或我的过程有什么问题。谁能帮忙弄清楚如何解决这个错误?

我终于弄明白了,同时根据 M.Innat 的建议创建了可重现的代码。错误消息让我相信它与自定义训练功能有关,但实际上与 TFRecordDataset 有关。

事实证明,在脚本中的某个时刻,records_train 原本有一个 tfrecord 文件名列表,变成了一个空列表。所以基本上没有数据传递给 model.fit.

作为参考,这是产生错误的代码行:

history = model.fit(
    train_dataset,
    batch_size=batch_size,
    epochs=epochs,
    validation_data=val_dataset,
)

但错误的实际来源是这一行,其中 records_train = []:

train_dataset = tf.data.TFRecordDataset(records_train).map(parse_element_func).batch(batch_size).prefetch(tf.data.AUTOTUNE)

这是错误信息:

ValueError: Unexpected result of `train_function` (Empty logs).

一个非常无用的错误消息,但也许这个 post 会对以后的人有所帮助。