PaddlePaddle/PaddleOCR · error · ValueError

No metric score found.

Error message

No metric score found.

What it means

ValueError from save_load.py's train result bookkeeping when saving a 'best' checkpoint but the metric dict contains none of the three recognized keys: 'acc', 'precision', or 'exp_rate'. The best-model ranking needs a scalar score, so an evaluation metric named anything else (e.g. 'hmean' variants not under precision, 'recall', 'f1', 'editdistance') has no score to record.

Source

Thrown at ppocr/utils/save_load.py:383

                label_dict_path = ""
        train_results["label_dict"] = label_dict_path
        train_results["train_log"] = "train.log"
        train_results["visualdl_log"] = ""
        train_results["config"] = "config.yaml"
        train_results["models"] = {}
        for i in range(1, last_num + 1):
            train_results["models"][f"last_{i}"] = {}
        train_results["models"]["best"] = {}
    train_results["done_flag"] = done_flag
    if "best" in prefix:
        if "acc" in metric_info["metric"]:
            metric_score = metric_info["metric"]["acc"]
        elif "precision" in metric_info["metric"]:
            metric_score = metric_info["metric"]["precision"]
        elif "exp_rate" in metric_info["metric"]:
            metric_score = metric_info["metric"]["exp_rate"]
        else:
            raise ValueError("No metric score found.")
        train_results["models"]["best"]["score"] = metric_score
        for tag in save_model_tag:
            if tag == "pdparams" and encrypted:
                train_results["models"]["best"][tag] = os.path.join(
                    prefix,
                    (
                        f"{prefix}.encrypted.{tag}"
                        if tag != "pdstates"
                        else f"{prefix}.states"
                    ),
                )
            else:
                train_results["models"]["best"][tag] = os.path.join(
                    prefix,
                    f"{prefix}.{tag}" if tag != "pdstates" else f"{prefix}.states",
                )
        for key in save_inference_files:
            train_results["models"]["best"][key] = os.path.join(

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Make your Metric return one of the supported keys — conventionally 'acc' for recognition, 'precision' for detection.
  2. Or stop requesting best-model saving (drop 'best' from save_model_tag / disable save_best_model) and keep 'latest' checkpoints only.
  3. If a custom score is required, extend the lookup list in this function to include your metric key.

Example fix

# before (custom metric returns only unsupported key)
return {"metric": {"cer": 0.08}}

# after (expose a supported score alongside)
return {"metric": {"acc": 1 - 0.08, "cer": 0.08}}
Defensive patterns

Strategy: validation

Validate before calling

SUPPORTED_SCORE_KEYS = ("acc", "precision", "exp_rate")

def metric_has_supported_score(metric_info) -> bool:
    return any(k in metric_info.get("metric", {}) for k in SUPPORTED_SCORE_KEYS)

if "best" in prefix and not metric_has_supported_score(metric_info):
    prefix = prefix.replace("best", "latest")  # degrade to latest-only saving

Type guard

def is_supported_metric_dict(m) -> bool:
    return isinstance(m, dict) and any(k in m.get("metric", {}) for k in ("acc", "precision", "exp_rate"))

Prevention

When it happens

Trigger: Training with save_best_model/save_model_tag including 'best' while the task's Eval metric returns only unsupported keys — e.g. a custom recognizer metric returning 'cer'/'edit_dist', or a KIE/SDMGR-style metric dict without precision.

Common situations: Custom datasets/tasks with custom Metric classes; users renaming metric outputs; new model types whose evaluation returns niche keys; snapshot/export tools (model list generation) that hit the best branch.

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/4beb4d2c3e2950c7. Report an issue: GitHub.