fix(config): --set 拦下不存在的键,并支持续训时重开学习率曲线
用户反馈"聚类配置里改不了 lr/weight_decay",实测是两件事:
1. --set 的键打错字母会凭空造出一个谁也不读的键,训练照跑但改动无效。
实例:simclr.optmizer.weight_decay=0.0(optimizer 少个 i)被静默接受。
现在 apply_overrides 默认只允许覆盖已存在的键,报错时给出同级可用键
与最接近的拼写("是不是想写 'optimizer'?")。需要新增键时可 strict=False。
2. lr 其实生效了,但从 2229/2500 续训时 cosine 只剩满值的 3%,
新设的 1e-3 落到 2.87e-5。新增 restart_schedule 开关:按剩余轮数重开曲线,
新 lr 从满值开始;SimCLR 与检测训练都支持。续训日志补上调度进度,
并在 lr 不足 initial_lr 的 10% 时警告并给出两种解法。
顺带:严格校验立刻暴露出 configs/cluster/film.yaml 里根本没有 scatter 段
(代码 cfg.get("scatter", {}) 兜底走默认值,所以功能正常但配置项形同虚设),已补上。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
506fde0290
commit
92c88b785c
+26
-2
@@ -134,21 +134,45 @@ def parse_override(item: str) -> tuple[str, Any]:
|
||||
return key.strip(), parsed if isinstance(parsed, (bool, int, float, list, type(None))) else raw
|
||||
|
||||
|
||||
def apply_overrides(cfg: dict, overrides: Iterable[str]) -> dict:
|
||||
def apply_overrides(cfg: dict, overrides: Iterable[str], strict: bool = True) -> dict:
|
||||
"""把 `a.b.c=value` 写进配置。
|
||||
|
||||
`strict` 时只允许覆盖**已存在**的键:`--set` 打错一个字母(optimizer -> optmizer)
|
||||
会凭空造出一个谁也不读的键,训练照跑但改动毫无效果,事后极难发现。
|
||||
"""
|
||||
for item in overrides:
|
||||
key, value = parse_override(item)
|
||||
parts = key.split(".")
|
||||
node = cfg
|
||||
for part in parts[:-1]:
|
||||
for depth, part in enumerate(parts[:-1]):
|
||||
nxt = node.get(part)
|
||||
if not isinstance(nxt, dict):
|
||||
if strict and part not in node:
|
||||
raise KeyError(_unknown_key_message(key, ".".join(parts[:depth]), node, part))
|
||||
nxt = {}
|
||||
node[part] = nxt
|
||||
node = nxt
|
||||
if strict and parts[-1] not in node:
|
||||
raise KeyError(_unknown_key_message(key, ".".join(parts[:-1]), node, parts[-1]))
|
||||
node[parts[-1]] = value
|
||||
return cfg
|
||||
|
||||
|
||||
def _unknown_key_message(full_key: str, prefix: str, node: Mapping, missing: str) -> str:
|
||||
"""报错时给出同级可用键与最接近的拼写,方便一眼看出打错在哪。"""
|
||||
import difflib
|
||||
|
||||
available = sorted(k for k in node if not k.startswith("_"))
|
||||
close = difflib.get_close_matches(missing, available, n=3, cutoff=0.6)
|
||||
where = f"`{prefix}` 下" if prefix else "配置顶层"
|
||||
hint = f",是不是想写 {close[0]!r}?" if close else ""
|
||||
return (
|
||||
f"--set 里的 {full_key!r} 在配置中不存在:{where}没有 {missing!r}{hint} "
|
||||
f"同级可用的键有 {available}。"
|
||||
"(只允许覆盖已存在的键;确实要新增请先写进 yaml)"
|
||||
)
|
||||
|
||||
|
||||
def load_config(path: str | Path, overrides: Iterable[str] = ()) -> Config:
|
||||
cfg = _load_raw(Path(path), set())
|
||||
cfg = apply_overrides(cfg, overrides)
|
||||
|
||||
Reference in New Issue
Block a user