Repository navigation
Fix registry argument validation for muon_hyperball - #312
Conversation
validate_optimizer_args built the set of accepted names from
signature(opt_cls) alone. MuonHyperball.__init__ takes
(*args, hyperball_radius, hyperball_eps=1e-15, **kwargs) and hands
everything else to Muon, so the check only allowed the literal names
args, hyperball_radius, hyperball_eps and kwargs. Every real Muon
argument was rejected, e.g.
get_configured_optimizer_cls("muon_hyperball", lr=0.01, hyperball_radius=1.0)
raised TypeError for lr, while args=... and kwargs=... passed.
Walk the MRO instead and collect the named parameters of each __init__,
continuing to the parent only while the current __init__ takes
**kwargs. *args and **kwargs themselves are no longer treated as
argument names. Optimizers without **kwargs, which is every other
registered one, stop at their own __init__ and keep the same accepted
names as before.
Signed-off-by: Dmitry Voropaev <dy.voropaev@gmail.com>
[Medium impact] The PR appears safe to merge; no actionable issues were found. SummaryThe PR lets
Reviews (2) · Last reviewed commit: "Use a plain loop to collect init params ..." · Reviewed by Greptile |
skyw
left a comment
There was a problem hiding this comment.
Largely LGTM. Couple of small comments, will approve after.
Thanks for the contribution. It has been on my list to redesign the registry, it is very limited and not definitely not bullet proof at the moment.
| """Checks if kwargs are valid for the optimizer class signature.""" | ||
| sig = signature(opt_cls) | ||
| supported_params = set[str]() | ||
| for klass in opt_cls.__mro__: |
There was a problem hiding this comment.
Renamed it to cls, class itself is a keyword.
| continue | ||
| init_params = signature(vars(klass)["__init__"]).parameters.values() | ||
| supported_params.update( | ||
| p.name for p in init_params if p.kind not in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD) |
There was a problem hiding this comment.
hard to read comprehension, favor plain for loop.
There was a problem hiding this comment.
Done, it's a plain loop now.
Signed-off-by: Dmitry Voropaev <dy.voropaev@gmail.com>
|
/ok to test 521873f |
registry.get_configured_optimizer_cls("muon_hyperball", lr=0.01, hyperball_radius=1.0)raisesTypeError: Optimizer 'MuonHyperball' does not accept arguments: {'lr'}. The same happens for momentum, nesterov and every other Muon argument.validate_optimizer_argsonly looks atsignature(opt_cls), andMuonHyperball.__init__is(*args, hyperball_radius, hyperball_eps=1e-15, **kwargs), so the only names it accepts areargs,hyperball_radius,hyperball_epsandkwargs. The nanogpt example works around this by hand inbuild_optimizerby merging in Muon's signature when the name ismuon_hyperball.The validation now walks the class MRO and collects the named parameters of each
__init__, moving on to the parent only while the current__init__takes**kwargs. The*argsand**kwargsentries are no longer counted as argument names. MuonHyperball is the only registered optimizer with**kwargsin its signature, so every other optimizer stops at its own__init__and accepts the same names as before. For MuonHyperball the walk stops at Muon, so names that only exist further up, likescaled_orthogonalize_fnon OrthogonalizedOptimizer, are still rejected.One side effect is that
weight_update_hooknow passes validation formuon_hyperball, since Muon's__init__takes it. MuonHyperball still raises KeyError for it when the optimizer is constructed, so the error shows up there instead of at validation time. I didn't special case it in the registry. I also left the nanogpt workaround alone. That example looks up classes through the registry but does its own argument check and never callsvalidate_optimizer_args, so it can be simplified separately.I added two tests to
tests/test_registry.py. One buildsmuon_hyperballthroughget_configured_optimizer_clswithlr,momentum,nesterovandhyperball_radiusand constructs it on a unit norm parameter. The other checks thatnonexistent_arg,args,kwargsandscaled_orthogonalize_fnare rejected for MuonHyperball. On main the first test errors with TypeError and theargsandkwargscases fail, and with the change all of them pass. I ranpython tests/test_registry.py --device=cpu --seed=42(18 tests OK) andpython tests/test_orthogonalized_optimizer.py(145 tests OK) on CPU with torch 2.14.1 and Python 3.12. I also looped over all 27 registered optimizers and compared the accepted names before and after. They are identical for all of them exceptmuon_hyperball, which now accepts every Muon argument and no longer acceptsargsorkwargs. ruff 0.9.9 (check, isort and format) is clean on both changed files and mypy 1.19.1 is clean onregistry.py.