keras-team/keras · error · ValueError

FBetaScore expects 2D inputs with shape (batch_size, output_

Error message

FBetaScore expects 2D inputs with shape (batch_size, output_dim), with output_dim fully defined (not None). Received input shapes: y_pred.shape={y_pred_shape} and y_true.shape={y_true_shape}.

What it means

Raised from FBetaScore._build during update_state when inputs are rank 2 but the last dimension (output_dim / num_classes) is None, i.e. not statically known. The metric must allocate per-class state variables, so it needs a concrete class count.

Source

Thrown at keras/src/metrics/f_score_metrics.py:131

        self.average = average
        self.beta = beta
        self.threshold = threshold
        self.axis = None
        self._built = False

        if self.average != "micro":
            self.axis = 0

    def _build(self, y_true_shape, y_pred_shape):
        if len(y_pred_shape) != 2 or len(y_true_shape) != 2:
            raise ValueError(
                "FBetaScore expects 2D inputs with shape "
                "(batch_size, output_dim). Received input "
                f"shapes: y_pred.shape={y_pred_shape} and "
                f"y_true.shape={y_true_shape}."
            )
        if y_pred_shape[-1] is None or y_true_shape[-1] is None:
            raise ValueError(
                "FBetaScore expects 2D inputs with shape "
                "(batch_size, output_dim), with output_dim fully "
                "defined (not None). Received input "
                f"shapes: y_pred.shape={y_pred_shape} and "
                f"y_true.shape={y_true_shape}."
            )
        num_classes = y_pred_shape[-1]
        if self.average != "micro":
            init_shape = (num_classes,)
        else:
            init_shape = ()

        def _add_zeros_variable(name):
            return self.add_variable(
                name=name,
                shape=init_shape,
                initializer=initializers.Zeros(),
                dtype=self.dtype,

View on GitHub (pinned to 7a34a03db6)

Solutions

  1. Pass tensors with statically-known last dim (e.g. TensorSpec([None, num_classes])).
  2. Build the metric with a fixed shape or update it outside jit-traced code on concrete tensors.
  3. Prefer model.compile(metrics=[...]) so Keras builds metrics from the model's output shape.

Example fix

# before
@tf.function
def step(x, y):
    m.update_state(y, model(x))  # output dim unknown under trace

# after
m = keras.metrics.FBetaScore(beta=1.0, average='macro')
m.build((None, 10), (None, 10))  # fix the class dim, update on concrete tensors
Defensive patterns

Strategy: validation

Validate before calling

assert y_pred.shape[-1] is not None and y_true.shape[-1] is not None, 'output_dim must be static'

Type guard

def has_static_last_dim(t) -> bool:
    return t.shape[-1] is not None

Prevention

When it happens

Trigger: Calling update_state inside tf.function/JAX jit traced with dynamic output shapes; a Functional model whose output shape is (batch, None); symbolic tensors with unspecified last dim.

Common situations: Custom train steps traced with jit; XLA/jit dynamic axes; building metrics from symbolic tensors instead of model.compile.

Related errors


AI-assisted analysis of keras-team/keras@7a34a03db6 (2026-08-25). Data as JSON: /api/errors/15e54f1c2606e445. Report an issue: GitHub.