{"record":{"id":"f4de4823f614620d","repo":"TheAlgorithms/Python","slug":"predicted-probabilities-must-sum-to-approximately","errorCode":null,"errorMessage":"Predicted probabilities must sum to approximately 1.","messagePattern":"Predicted probabilities must sum to approximately 1\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"machine_learning/loss_functions.py","lineNumber":145,"sourceCode":"    >>> categorical_cross_entropy(true_labels, pred_probs)\n    Traceback (most recent call last):\n        ...\n    ValueError: y_true must be one-hot encoded.\n    >>> true_labels = np.array([[1, 0, 0], [0, 1, 0]])\n    >>> pred_probs = np.array([[0.9, 0.1, 0.1], [0.2, 0.7, 0.1]])\n    >>> categorical_cross_entropy(true_labels, pred_probs)\n    Traceback (most recent call last):\n        ...\n    ValueError: Predicted probabilities must sum to approximately 1.\n    \"\"\"\n    if y_true.shape != y_pred.shape:\n        raise ValueError(\"Input arrays must have the same shape.\")\n\n    if np.any((y_true != 0) & (y_true != 1)) or np.any(y_true.sum(axis=1) != 1):\n        raise ValueError(\"y_true must be one-hot encoded.\")\n\n    if not np.all(np.isclose(np.sum(y_pred, axis=1), 1, rtol=epsilon, atol=epsilon)):\n        raise ValueError(\"Predicted probabilities must sum to approximately 1.\")\n\n    y_pred = np.clip(y_pred, epsilon, 1)  # Clip predictions to avoid log(0)\n    return -np.sum(y_true * np.log(y_pred))\n\n\ndef categorical_focal_cross_entropy(\n    y_true: np.ndarray,\n    y_pred: np.ndarray,\n    alpha: np.ndarray = None,\n    gamma: float = 2.0,\n    epsilon: float = 1e-15,\n) -> float:\n    \"\"\"\n    Calculate the mean categorical focal cross-entropy (CFCE) loss between true\n    labels and predicted probabilities for multi-class classification.\n\n    CFCE loss is a generalization of binary focal cross-entropy for multi-class\n    classification. It addresses class imbalance by focusing on hard examples.","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/TheAlgorithms/Python/blob/f5988cc09713315817df6a7e327e258013a94440/machine_learning/loss_functions.py#L127-L163","documentation":"Raised by categorical_cross_entropy when rows of y_pred do not sum to approximately 1 within the epsilon tolerance (np.isclose with rtol=atol=epsilon). Cross-entropy against a one-hot target is only a proper probability loss when predictions form a distribution per row, so non-normalized outputs are rejected.","triggerScenarios":"Passing raw logits (unnormalized model outputs) as y_pred, e.g. [[0.9, 0.1, 0.1], [0.2, 0.7, 0.1]] whose first row sums to 1.1 (the exact doctest example), or forgetting a softmax layer.","commonSituations":"Taking the layer before the softmax from a neural net, applying sigmoid instead of softmax for multiclass output, or manually dividing by the wrong normalizer.","solutions":["Apply softmax to logits before calling: y_pred = np.exp(logits) / np.exp(logits).sum(axis=1, keepdims=True).","Check row sums in your eval harness: np.allclose(y_pred.sum(axis=1), 1).","Ensure the model's final layer is softmax (not linear/sigmoid) for this loss."],"exampleFix":"# before\nlogits = model(x)  # unnormalized\ncategorical_cross_entropy(y_true, logits)\n\n# after\nlogits = model(x)\nprobs = np.exp(logits) / np.exp(logits).sum(axis=1, keepdims=True)\ncategorical_cross_entropy(y_true, probs)","handlingStrategy":"validation","validationCode":"def softmax_rows(logits: np.ndarray) -> np.ndarray:\n    e = np.exp(logits - logits.max(axis=1, keepdims=True))\n    return e / e.sum(axis=1, keepdims=True)\n\nprobs = softmax_rows(np.asarray(y_pred))\nassert np.allclose(probs.sum(axis=1), 1.0)\nloss = categorical_cross_entropy(y_true, probs)","typeGuard":"def is_row_stochastic(y_pred: np.ndarray) -> bool:\n    return y_pred.ndim == 2 and np.all(np.isclose(y_pred.sum(axis=1), 1.0))","tryCatchPattern":"try:\n    categorical_cross_entropy(y_true, y_pred)\nexcept ValueError as e:\n    if \"sum to approximately 1\" in str(e):\n        e_ = np.exp(y_pred - y_pred.max(axis=1, keepdims=True))\n        return categorical_cross_entropy(y_true, e_ / e_.sum(axis=1, keepdims=True))\n    raise","preventionTips":["Apply softmax (with the max-subtraction trick) before the loss call.","Never feed logits directly into categorical_cross_entropy.","Use is_row_stochastic checks in evaluation smoke tests."],"tags":["machine-learning","loss-function","cross-entropy","softmax","normalization"],"backgroundTag":null,"analyzedSha":"f5988cc09713315817df6a7e327e258013a94440","analyzedAt":"2026-08-14T17:30:07.041Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}