{"record":{"id":"dd77ad49a144c2f9","repo":"keras-team/keras","slug":"y-pred-must-have-rank-2-when-multi-label-true","errorCode":null,"errorMessage":"`y_pred` must have rank 2 when `multi_label=True`. Found rank {len(shape)}. Full shape received for `y_pred`: {shape}","messagePattern":"`y_pred` must have rank 2 when `multi_label=True`\\. Found rank (.+?)\\. Full shape received for `y_pred`: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"keras/src/metrics/confusion_metrics.py","lineNumber":1301,"sourceCode":"                self._build(shape)\n        else:\n            if num_labels:\n                raise ValueError(\n                    \"`num_labels` is needed only when `multi_label` is True.\"\n                )\n            self._build(None)\n\n    @property\n    def thresholds(self):\n        \"\"\"The thresholds used for evaluating AUC.\"\"\"\n        return list(self._thresholds)\n\n    def _build(self, shape):\n        \"\"\"Initialize TP, FP, TN, and FN tensors, given the shape of the\n        data.\"\"\"\n        if self.multi_label:\n            if len(shape) != 2:\n                raise ValueError(\n                    \"`y_pred` must have rank 2 when `multi_label=True`. \"\n                    f\"Found rank {len(shape)}. \"\n                    f\"Full shape received for `y_pred`: {shape}\"\n                )\n            self._num_labels = shape[1]\n            variable_shape = [self.num_thresholds, self._num_labels]\n        else:\n            variable_shape = [self.num_thresholds]\n\n        self._build_input_shape = shape\n        # Create metric variables\n        self.true_positives = self.add_variable(\n            shape=variable_shape,\n            initializer=initializers.Zeros(),\n            name=\"true_positives\",\n        )\n        self.false_positives = self.add_variable(\n            shape=variable_shape,","sourceCodeStart":1283,"sourceCodeEnd":1319,"githubUrl":"https://github.com/keras-team/keras/blob/7a34a03db60bf60042242d6a556fc3be119046a5/keras/src/metrics/confusion_metrics.py#L1283-L1319","documentation":"Raised from AUC._build when multi_label=True but the y_pred shape is not rank 2. Multi-label AUC expects predictions of shape (batch_size, num_labels); rank-1 or rank-3+ tensors trigger this during __init__ (when num_labels is given) or the first update_state.","triggerScenarios":"model.compile(metrics=[keras.metrics.AUC(multi_label=True, num_labels=3)]) with output shape (batch,) or (batch, 4, 5); rank-1 y_pred in update_state; missing final Dense layer.","commonSituations":"Missing Dense head so output is rank 1; conv outputs without pooling/flatten; data-pipeline shape changes after refactors.","solutions":["Make the model output 2D: end with Dense(num_labels, activation='sigmoid').","Reshape y_pred/y_true to (batch, num_labels) before update_state.","If the task is binary single-output, drop multi_label=True and use plain AUC."],"exampleFix":"# before\nx = keras.layers.GlobalAveragePooling2D()(x)\nout = keras.layers.Activation('sigmoid')(x)  # missing Dense head\n\n# after\nx = keras.layers.GlobalAveragePooling2D()(x)\nout = keras.layers.Dense(num_labels, activation='sigmoid')(x)","handlingStrategy":"validation","validationCode":"assert y_pred.ndim == 2, f'multi_label AUC needs rank-2 y_pred, got {y_pred.shape}'","typeGuard":"def is_rank2(t) -> bool:\n    return getattr(t, 'ndim', None) == 2 or len(getattr(t, 'shape', [])) == 2","tryCatchPattern":null,"preventionTips":["End classification models with Dense(num_labels, activation='sigmoid').","Assert output rank in a smoke test before compile."],"tags":["keras","metrics","auc","shape-validation","multi-label"],"backgroundTag":"tensor-rank-mismatch","analyzedSha":"7a34a03db60bf60042242d6a556fc3be119046a5","analyzedAt":"2026-08-25T21:25:25.994Z","schemaVersion":2},"datasetVersion":"2026-08-26T02:17:13.382Z"}