{"record":{"id":"a4eefafa0817dac9","repo":"unslothai/unsloth","slug":"ema-decay-must-be-in-0-1-0-disables-the-ema-ad","errorCode":null,"errorMessage":"ema_decay must be in [0, 1); 0 disables the EMA adapter","messagePattern":"ema_decay must be in \\[0, 1\\); 0 disables the EMA adapter","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/training/diffusion_train_common.py","lineNumber":1094,"sourceCode":"        if resolved_family in CHECKPOINTLESS_FAMILIES:\n            if resume_from_checkpoint:\n                raise ValueError(\n                    f\"resume_from_checkpoint is not supported for {resolved_family}: its trainer \"\n                    f\"writes no checkpoint bundle, so there is nothing to continue from and the \"\n                    f\"run would silently start over and overwrite its output. Start a fresh run.\"\n                )\n            if save_steps:\n                raise ValueError(\n                    f\"save_steps is not supported for {resolved_family}: its trainer writes no \"\n                    f\"checkpoint bundle. Leave it at 0; the adapter is still saved at the end.\"\n                )\n        try:\n            ema_decay = float(self.ema_decay or 0.0)\n        except (TypeError, ValueError) as exc:\n            raise ValueError(f\"ema_decay must be a number, got {self.ema_decay!r}\") from exc\n        # decay = 1.0 would freeze the shadow at its init forever; the update is shadow * decay + param * (1 - decay), so valid decays live in [0, 1).\n        if not 0.0 <= ema_decay < 1.0:\n            raise ValueError(\"ema_decay must be in [0, 1); 0 disables the EMA adapter\")\n        # A blank cond_cache_dir (the Studio default when unset) means \"off\", not cwd.\n        cond_cache_dir = (\n            str(self.cond_cache_dir).strip() if self.cond_cache_dir is not None else \"\"\n        ) or None\n        compile_transformer = str(self.compile_transformer or \"auto\").strip().lower()\n        if compile_transformer not in (\"off\", \"on\", \"auto\"):\n            raise ValueError(\"compile_transformer must be one of off / on / auto\")\n        base_precision = str(self.base_precision or \"nf4\").strip().lower()\n        if base_precision not in (\"nf4\", \"bf16\", \"int8\", \"fp8\", \"mxfp8\", \"auto\"):\n            raise ValueError(\"base_precision must be one of nf4 / bf16 / int8 / fp8 / mxfp8 / auto\")\n        # base_precision is a DiT-only lever, so the dense-mode gates apply only to the DiT families. The mode-name check above still runs for every family.\n        if resolved_family != \"sdxl\" and base_precision in (\"bf16\", \"int8\", \"fp8\", \"mxfp8\"):\n            if repo_is_prequantized(self.base_model):\n                raise ValueError(\n                    f\"base_precision={base_precision!r} needs a dense base repo, but \"\n                    f\"'{self.base_model}' is already bitsandbytes-quantized. Pick the \"\n                    f\"family's dense (bf16) base repo for this mode, or use nf4/auto.\"\n                )","sourceCodeStart":1076,"sourceCodeEnd":1112,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/training/diffusion_train_common.py#L1076-L1112","documentation":"The validator rejected ema_decay outside [0.0, 1.0). The EMA update is shadow = shadow * decay + param * (1 - decay); decay = 1.0 makes the second term zero, freezing the shadow at its initialization forever, and decay > 1 diverges. 0 is the documented 'disable the EMA adapter' value. The upper bound is exclusive by design.","triggerScenarios":"Passing ema_decay=1.0 ('perfect' averaging intuition), 1.0 via float rounding, or values > 1. Often from configs that treat EMA decay as a probability-style 0..1 inclusive range, or sweeps that include the endpoint 1.0.","commonSituations":"Copying momentum-style 0.9999 values (fine) but also sweeping to 1.0; assuming 1.0 means 'full average'; UI sliders with an inclusive 0..1 range.","solutions":["Use a decay strictly below 1.0 — typical values are 0.999 or 0.9995.","Use ema_decay=0 (or None/omitted) to disable EMA entirely.","Make slider/sweep endpoints exclusive at 1.0: max bound 0.9999."],"exampleFix":"# before\nconfig = TrainConfig(ema_decay=1.0)\n\n# after\nconfig = TrainConfig(ema_decay=0.999)","handlingStrategy":"validation","validationCode":"def check_ema_decay(v) -> float:\n    d = float(v or 0.0)\n    if not 0.0 <= d < 1.0:\n        raise ValueError(f\"ema_decay must be in [0, 1); 1.0 freezes the EMA shadow forever, got {v!r}\")\n    return d","typeGuard":"def is_valid_ema_decay_range(v) -> bool:\n    try:\n        return 0.0 <= float(v or 0.0) < 1.0\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    session.submit_training(config)\nexcept ValueError as e:\n    if \"ema_decay must be in\" in str(e):\n        config.ema_decay = 0.999\n        session.submit_training(config)\n    else:\n        raise","preventionTips":["Use 0.999/0.9995-style values; never 1.0.","Make sweep and slider upper bounds exclusive of 1.0 (use 0.9999).","Disable EMA with 0/None — do not attempt it via decay values."],"tags":["training","ema","configuration","validation"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}