Repository navigation
[PyTorch] Add FusedAdam.reload_master_param - #3825
CarlosGomes98 wants to merge 1 commit into
Conversation
Add a method that replaces a parameter and its FP32 master weights without resetting Adam history, for use after loading model weights without optimizer state. It accepts full FP32 values when store_param_remainders is enabled, writing the BF16 high bits with the same rounding convention as AdamFunctorMasterParamRemainder, and supports quantized and DTensor parameters. The method rejects tensors the optimizer does not own (e.g. param.data), DTensor masters whose mesh, placements or shape differ from the parameter, non-FP32 values and mismatched local shapes, all before mutating any state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Carlos Gomes <cgomes@nvidia.com>
timmoon10
left a comment
There was a problem hiding this comment.
Overall this is a good idea. The optimizer state for master weights can get weird and esoteric (especially the BF16 remainders trick) and it's not reasonable for users to interact with it directly. With some minor tweaks, this API is very straightforward.
| self.set_scaled_state(param, "master_param", master) | ||
|
|
||
| @torch.no_grad() | ||
| def reload_master_param(self, param: torch.nn.Parameter, master_param: torch.Tensor) -> None: |
There was a problem hiding this comment.
This is fine functionally, but I think the following signature would be cleaner:
def update_param(self, param: torch.nn.Parameter, values: torch.Tensor) -> None:The docstring makes you think you're getting rid of param and using master_param for the rest of the training run, when you're actually just updating values in param and in the optimizer state master weights.
| """Replace a parameter and its master weights without resetting Adam history. | ||
|
|
||
| Use this after loading model weights without loading optimizer state. Unlike | ||
| :meth:`set_scaled_state`, this method accepts full FP32 values even when | ||
| ``store_param_remainders`` is enabled. It updates the model parameter as well, | ||
| including the high bits needed to reconstruct the master from its remainders. | ||
| Moment buffers are initialized if absent; existing moments and step counters | ||
| are preserved. Distributed callers must synchronize updated parameter shards. | ||
| Call this outside CUDA graph capture. The configured master-weight dtype still | ||
| determines storage precision when remainders are disabled. |
There was a problem hiding this comment.
This is a very Claude-y docstring, which is more showing off its debugging history than providing useful guidance for users.
In fact, it seems we could get across everything important with just:
| """Replace a parameter and its master weights without resetting Adam history. | |
| Use this after loading model weights without loading optimizer state. Unlike | |
| :meth:`set_scaled_state`, this method accepts full FP32 values even when | |
| ``store_param_remainders`` is enabled. It updates the model parameter as well, | |
| including the high bits needed to reconstruct the master from its remainders. | |
| Moment buffers are initialized if absent; existing moments and step counters | |
| are preserved. Distributed callers must synchronize updated parameter shards. | |
| Call this outside CUDA graph capture. The configured master-weight dtype still | |
| determines storage precision when remainders are disabled. | |
| """Update parameter values and optimizer state for master weights. |
I prefer implementing functions with a clear idea of what they are supposed to do, rather than prescribing when you're supposed to call them. Also, store_param_remainders is an internal implementation detail.
| ---------- | ||
|
|
||
| .. autoapiclass:: transformer_engine.pytorch.optimizers.FusedAdam | ||
| :members: reload_master_param, get_unscaled_state, set_scaled_state |
There was a problem hiding this comment.
Do we want to treat get_unscaled_state/set_scaled_state as part of the user-facing API? I imagine it as an internal helper function that expert users like Mcore might use. How much does Mcore depend on it as a stable interface?
Add a method that replaces a parameter and its FP32 master weights without resetting Adam history, for use after loading model weights without optimizer state. It accepts full FP32 values when store_param_remainders is enabled, writing the BF16 high bits with the same rounding convention as AdamFunctorMasterParamRemainder, and supports quantized and DTensor parameters.
The method rejects tensors the optimizer does not own (e.g. param.data), DTensor masters whose mesh, placements or shape differ from the parameter, non-FP32 values and mismatched local shapes, all before mutating any state.
Description
Please include a brief summary of the changes, relevant motivation and context.
Fixes # (issue)
Type of change
Changes
Please list the changes introduced in this PR:
Checklist: