{"record":{"id":"4beb4d2c3e2950c7","repo":"PaddlePaddle/PaddleOCR","slug":"no-metric-score-found","errorCode":null,"errorMessage":"No metric score found.","messagePattern":"No metric score found\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"ppocr/utils/save_load.py","lineNumber":383,"sourceCode":"                label_dict_path = \"\"\n        train_results[\"label_dict\"] = label_dict_path\n        train_results[\"train_log\"] = \"train.log\"\n        train_results[\"visualdl_log\"] = \"\"\n        train_results[\"config\"] = \"config.yaml\"\n        train_results[\"models\"] = {}\n        for i in range(1, last_num + 1):\n            train_results[\"models\"][f\"last_{i}\"] = {}\n        train_results[\"models\"][\"best\"] = {}\n    train_results[\"done_flag\"] = done_flag\n    if \"best\" in prefix:\n        if \"acc\" in metric_info[\"metric\"]:\n            metric_score = metric_info[\"metric\"][\"acc\"]\n        elif \"precision\" in metric_info[\"metric\"]:\n            metric_score = metric_info[\"metric\"][\"precision\"]\n        elif \"exp_rate\" in metric_info[\"metric\"]:\n            metric_score = metric_info[\"metric\"][\"exp_rate\"]\n        else:\n            raise ValueError(\"No metric score found.\")\n        train_results[\"models\"][\"best\"][\"score\"] = metric_score\n        for tag in save_model_tag:\n            if tag == \"pdparams\" and encrypted:\n                train_results[\"models\"][\"best\"][tag] = os.path.join(\n                    prefix,\n                    (\n                        f\"{prefix}.encrypted.{tag}\"\n                        if tag != \"pdstates\"\n                        else f\"{prefix}.states\"\n                    ),\n                )\n            else:\n                train_results[\"models\"][\"best\"][tag] = os.path.join(\n                    prefix,\n                    f\"{prefix}.{tag}\" if tag != \"pdstates\" else f\"{prefix}.states\",\n                )\n        for key in save_inference_files:\n            train_results[\"models\"][\"best\"][key] = os.path.join(","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/ppocr/utils/save_load.py#L365-L401","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make your Metric return one of the supported keys — conventionally 'acc' for recognition, 'precision' for detection.","Or stop requesting best-model saving (drop 'best' from save_model_tag / disable save_best_model) and keep 'latest' checkpoints only.","If a custom score is required, extend the lookup list in this function to include your metric key."],"exampleFix":"# before (custom metric returns only unsupported key)\nreturn {\"metric\": {\"cer\": 0.08}}\n\n# after (expose a supported score alongside)\nreturn {\"metric\": {\"acc\": 1 - 0.08, \"cer\": 0.08}}","handlingStrategy":"validation","validationCode":"SUPPORTED_SCORE_KEYS = (\"acc\", \"precision\", \"exp_rate\")\n\ndef metric_has_supported_score(metric_info) -> bool:\n    return any(k in metric_info.get(\"metric\", {}) for k in SUPPORTED_SCORE_KEYS)\n\nif \"best\" in prefix and not metric_has_supported_score(metric_info):\n    prefix = prefix.replace(\"best\", \"latest\")  # degrade to latest-only saving","typeGuard":"def is_supported_metric_dict(m) -> bool:\n    return isinstance(m, dict) and any(k in m.get(\"metric\", {}) for k in (\"acc\", \"precision\", \"exp_rate\"))","tryCatchPattern":null,"preventionTips":["Design custom Metric classes to publish a scalar under 'acc' or 'precision'.","When introducing a new task type, extend the score-key lookup in save_load.py alongside the metric.","Smoke-test one save_model_tag cycle with a tiny run before launching full training."],"tags":["checkpoint","metrics","training","config"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}