chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+837
View File
@@ -0,0 +1,837 @@
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import logging
import os
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from paddle import Tensor
import paddle
from paddle import _C_ops
from paddle.base import framework
from paddle.distributed.flex_checkpoint.dcp.sharded_weight import (
ShardedStateDict,
ShardedWeight,
create_sharded_weight_with_new_local,
)
from ..nn.clip import GradientClipBase
from .optimizer import Optimizer
# Debug logging for Muon optimizer
_logger = logging.getLogger(__name__)
MUON_DEBUG = os.environ.get("MUON_DEBUG", "0") == "1"
__all__ = []
# ------------------------------------------------------------------
# Parameter metadata dataclasses
# ------------------------------------------------------------------
@dataclass
class MuonParamInfo:
"""Muon update metadata for a single parameter.
This replaces the previous approach of setting dynamic attributes
directly on param objects.
Attributes:
use_muon: If True, use Muon (orthogonal) updates; otherwise AdamW.
split_concat_func: Optional callable that implements the slice strategy.
Signature: split_concat_func(matrix, ortho_fn, **kwargs) -> sliced_matrix
If None, whole-matrix orthogonalisation is used.
"""
use_muon: bool = True
split_concat_func: Callable | None = None
# Type alias for the parameter info mapping
MuonParamInfoMap = dict[str, MuonParamInfo]
# ------------------------------------------------------------------
# Newton-Schulz coefficient sets
# ------------------------------------------------------------------
_NS_COEFFICIENT_SETS = {
# Simple coefficient set (original)
"simple": [
(3.4445, -4.7750, 2.0315),
],
# Quintic iteration with optimized coefficients
# Source: https://leloykun.github.io/ponder/muon-opt-coeffs/
"quintic": [
(4.0848, -6.8946, 2.9270),
(3.9505, -6.3029, 2.6377),
(3.7418, -5.5913, 2.3037),
(2.8769, -3.1427, 1.2046),
(2.8366, -3.0525, 1.2012),
],
# Polar Express iteration from https://arxiv.org/abs/2505.16932
"polar_express": [
(8.2051, -22.9019, 16.4607),
(4.0664, -2.8612, 0.5184),
(3.9096, -2.8234, 0.5250),
(3.2856, -2.4153, 0.4853),
(2.2779, -1.6198, 0.3985),
(1.8726, -1.2307, 0.3585),
(1.8564, -1.2132, 0.3568),
(1.8750, -1.2500, 0.3750),
],
# AOL coefficients from https://github.com/thib-s/flash-newton-schulz
"aol": [
(4.0098, -7.0585, 2.4635),
(3.4585, -5.5479, 2.5959),
(2.7573, -3.2939, 1.4254),
(2.7215, -3.0494, 1.3169),
],
"deepseekv4":
# From DeepSeekV4: https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro/resolve/main/DeepSeek_V4.pdf
[(3.4445, -4.7750, 2.0315)] * 8 + [(2.0, -1.5, 0.5)] * 2,
}
# ------------------------------------------------------------------
# Default parameter classification
# ------------------------------------------------------------------
def _default_should_use_muon(name, shape, exclude_patterns):
"""Default fallback logic for determining if a parameter should use Muon.
This is only used when param.is_muon is not set. The actual exclusion
patterns must be configured via training_args.muon_exclude_patterns in yaml.
Args:
name: Parameter name.
shape: Parameter shape.
exclude_patterns: List of substrings to exclude from Muon updates.
Must be provided (e.g., ['embed', 'bias', 'lm_head', 'mlp.gate']).
Returns:
True if the parameter should use Muon (orthogonal) updates.
Raises:
ValueError: If exclude_patterns is None.
"""
if exclude_patterns is None:
raise ValueError(
"muon_exclude_patterns must be set in yaml config. "
"Example: muon_exclude_patterns: ['embed', 'bias', 'lm_head', 'mlp.gate']"
)
if len(shape) not in (2, 3):
return False
name_lower = name.lower()
for pattern in exclude_patterns:
if pattern.lower() in name_lower:
return False
return True
class Muon(Optimizer):
r"""
Muon optimizer for MuonShardingOptimizer (Sharding Stage1 V3) usage.
For 2-D weight matrices (identified by :func:`_default_should_use_muon`), Muon
applies orthogonal gradient updates via Newton-Schulz iteration. For all
other parameters (embeddings, biases, expert weights, …) it falls back to
a standard AdamW update.
Designed for ``MuonShardingOptimizer`` (Sharding Stage1 V3), where 2D parameters are
assigned as whole tensors to ranks. Currently we do not support TP=1, no sharding gather
or TP communication is needed during the optimizer step.
Args:
learning_rate (float | LRScheduler): Learning rate. Default: ``0.02``.
parameters (list[Tensor]): Flat list of parameters to optimize.
momentum (float): Momentum coefficient for the Muon update. Default: ``0.95``.
adam_beta1 (float): β₁ for the AdamW fallback. Default: ``0.9``.
adam_beta2 (float): β₂ for the AdamW fallback. Default: ``0.95``.
weight_decay (float): Decoupled weight decay. Default: ``0.01``.
ns_steps (int): Newton-Schulz iteration steps. Default: ``5``.
ns_coeff_type (str): Preset name for Newton-Schulz coefficients.
Options: ``"simple"``, ``"quintic"``, ``"polar_express"``,
``"aol"``, ``"deepseekv4"``, ``"custom"``. Default: ``"simple"``.
ns_coeffs (list[tuple[float, float, float]] | None): Custom
Newton-Schulz coefficient set. Each tuple is ``(a, b, c)``
for one iteration step. Default: ``None``.
Only used when ns_coeff_type=``custom``.
nesterov (bool): Use Nesterov momentum in Muon. Default: ``True``.
adam_epsilon (float): ε for numerical stability in AdamW. Default: ``1e-9``.
grad_clip (GradientClipBase | None): Gradient clipping. Default: ``None``.
apply_decay_param_fun (callable | None): Function to select which
parameters receive weight decay. Default: ``None``.
muon_version (int): Scaling-function version (1/2/3). Default: ``1``.
muon_exclude_patterns (list[str] | None): Parameter names containing
any of these substrings will use AdamW instead of Muon.
Example: ``['embed', 'bias', 'lm_head', 'mlp.gate']``.
Default: ``None``.
muon_extra_scale_factor (float): Extra multiplicative scale applied
after the dimension-dependent scaling in ``_scaling_fn``.
Default: ``0.2``.
muon_param_info_map (MuonParamInfoMap | None): Per-parameter metadata
dict mapping param name to :class:`MuonParamInfo` (use_muon,
split_concat_func). Built by Trainer and passed in.
Default: ``None``.
ns_matmul_dtype (paddle.dtype | None): Dtype for Newton-Schulz matmul
iterations. ``None`` = auto-detect: bfloat16 on Ampere+ (capability
>= 8.0), float32 on V100 and older. Pass ``paddle.float32``
explicitly to force float32. Default: ``None``.
multi_precision (bool): Maintain FP32 master weights when training in
BF16/FP16. Default: ``False``.
name (str | None): Optional name for the optimizer instance.
"""
_moment_acc_str = "moment1"
_moment2_acc_str = "moment2"
_beta1_pow_acc_str = "beta1_pow_acc"
_beta2_pow_acc_str = "beta2_pow_acc"
def __init__(
self,
learning_rate=0.02,
parameters=None,
momentum=0.95,
adam_beta1=0.9,
adam_beta2=0.95,
weight_decay=0.01,
ns_steps=5,
ns_coeff_type="simple",
ns_coeffs=None,
nesterov=True,
adam_epsilon=1e-9,
grad_clip=None,
lr_ratio: Callable[[Tensor], float] | None = None,
apply_decay_param_fun: Callable[[str], bool] | None = None,
muon_version=1,
muon_exclude_patterns=None,
muon_extra_scale_factor=0.2,
muon_param_info_map: MuonParamInfoMap | None = None,
ns_matmul_dtype=None,
multi_precision=False,
name=None,
**kwargs,
):
if parameters is None:
raise ValueError(
"parameters argument given to the Optimizer should not be None."
)
if not isinstance(parameters, list):
raise TypeError("parameters must be a list.")
if len(parameters) > 0 and isinstance(parameters[0], dict):
raise TypeError(
"Muon optimizer only supports a flat list of parameters, "
"not a list of parameter groups."
)
if grad_clip is not None and not isinstance(
grad_clip, GradientClipBase
):
raise TypeError(
"'grad_clip' should be an instance of GradientClipBase's derived class"
)
defaults = {
"momentum": momentum,
"adam_beta1": adam_beta1,
"adam_beta2": adam_beta2,
"weight_decay": weight_decay,
"ns_steps": ns_steps,
"nesterov": nesterov,
"epsilon": adam_epsilon,
"muon_version": muon_version,
"ns_coeff_type": ns_coeff_type,
}
super().__init__(
learning_rate=learning_rate,
parameters=parameters,
weight_decay=weight_decay,
grad_clip=grad_clip,
name=name,
)
self._multi_precision = multi_precision
self._master_weights = {}
self._lr_ratio = lr_ratio
self._apply_decay_param_fun = apply_decay_param_fun
self._muon_split_logged = False
self._muon_exclude_patterns = muon_exclude_patterns
self._muon_extra_scale_factor = muon_extra_scale_factor
self._ns_coeff_type = ns_coeff_type
if ns_coeff_type == "custom":
assert ns_coeffs is not None, (
"ns_coeffs must be provided when ns_coeff_type is 'custom'."
)
self._ns_coeffs = ns_coeffs
else:
assert ns_coeff_type in _NS_COEFFICIENT_SETS, (
f"Invalid ns_coeff_type: {ns_coeff_type}"
)
self._ns_coeffs = _NS_COEFFICIENT_SETS[ns_coeff_type]
self._muon_param_info_map = muon_param_info_map or {}
# Dtype for Newton-Schulz matmul.
# None = auto: bfloat16 on Ampere+ (capability >= 8.0), float32 on older.
if ns_matmul_dtype is None:
cap = (
paddle.device.cuda.get_device_capability()
if paddle.is_compiled_with_cuda()
else (0, 0)
)
self._ns_matmul_dtype = (
paddle.bfloat16 if cap[0] >= 8 else paddle.float32
)
else:
self._ns_matmul_dtype = ns_matmul_dtype
self._default_dict.update(defaults)
# ------------------------------------------------------------------
# Accumulator management
# ------------------------------------------------------------------
def _ensure_accumulators(self, param, use_muon, group):
"""Create optimizer accumulators for *param* if they do not exist yet."""
if (
self._moment_acc_str in self._accumulators
and param.name in self._accumulators[self._moment_acc_str]
):
return
# FP32 master weight for mixed-precision training
if self._multi_precision and self._is_dtype_fp16_or_bf16(param.dtype):
if param.name not in self._master_weights:
self._create_master_weight(param)
self._add_accumulator(
self._moment_acc_str,
param,
dtype=paddle.float32,
fill_value=0.0,
shape=param.shape,
type=framework.core.VarDesc.VarType.DENSE_TENSOR,
)
if not use_muon:
# AdamW-specific states
self._add_accumulator(
self._moment2_acc_str,
param,
dtype=paddle.float32,
fill_value=0.0,
shape=param.shape,
type=framework.core.VarDesc.VarType.DENSE_TENSOR,
)
for acc_name, init_val in [
(self._beta1_pow_acc_str, group.get("adam_beta1", 0.9)),
(self._beta2_pow_acc_str, group.get("adam_beta2", 0.95)),
]:
self._add_accumulator(
acc_name,
param,
dtype=paddle.float32,
fill_value=init_val,
shape=[1],
type=framework.core.VarDesc.VarType.DENSE_TENSOR,
)
def _create_accumulators(self, block, parameters):
"""Standard entry-point used by checkpoint-resume infrastructure."""
if isinstance(parameters, dict):
parameters = self._update_param_group(parameters)
for p in parameters:
param_info = self._muon_param_info_map.get(p.name)
if param_info is not None:
use_muon = param_info.use_muon
else:
use_muon = _default_should_use_muon(
p.name,
getattr(p, "original_shape", p.shape),
self._muon_exclude_patterns,
)
self._ensure_accumulators(p, use_muon, self._default_dict)
# ------------------------------------------------------------------
# Newton-Schulz orthogonalisation
# ------------------------------------------------------------------
@staticmethod
def _zeropower_via_newtonschulz5(
X,
steps=5,
eps=1e-9,
ns_coeffs=None,
ns_matmul_dtype=paddle.bfloat16,
):
"""Approximate the matrix sign function via Newton-Schulz iteration.
Args:
X: Input tensor to orthogonalize. Must be 2D (M, N) or
3D (B, M, N) for batched operation.
steps: Number of Newton-Schulz iterations.
eps: Small constant for numerical stability.
ns_coeffs: List of (a, b, c) coefficient tuples for iteration.
If None, uses the "simple" preset.
ns_matmul_dtype: Dtype for matmul iterations. Defaults to
bfloat16. Pass paddle.float32 for V100 compatibility.
"""
if X.ndim < 2 or X.ndim > 3:
raise ValueError(
f"Input tensor X must be 2D or 3D (batched), got {X.ndim}D"
)
coeff_sets = (
ns_coeffs
if ns_coeffs is not None
else _NS_COEFFICIENT_SETS["simple"]
)
if X.shape[-2] > X.shape[-1]:
X = paddle.transpose(
X,
perm=[1, 0] if X.ndim == 2 else [0, 2, 1],
)
transpose = True
else:
transpose = False
orig_shape = X.shape
X_flat = X.flatten(start_axis=-2)
X_flat = paddle.nn.functional.normalize(
X_flat, p=2, axis=-1, epsilon=eps
)
X = X_flat.reshape(orig_shape).astype(ns_matmul_dtype)
if X.ndim == 3:
ns_step_fn = Muon._batched_newton_schulz_step
else:
ns_step_fn = Muon._newton_schulz_step
for i in range(steps):
a, b, c = coeff_sets[i % len(coeff_sets)]
X = ns_step_fn(X, a, b, c)
if transpose:
X = paddle.transpose(X, perm=[1, 0] if X.ndim == 2 else [0, 2, 1])
return X
@staticmethod
def _newton_schulz_step(X, a, b, c):
"""Single Newton-Schulz iteration step for 2D input."""
A = paddle.matmul(X, X, transpose_y=True)
B = paddle.addmm(input=A, x=A, y=A, beta=b, alpha=c)
X = paddle.addmm(input=X, x=B, y=X, beta=a, alpha=1.0)
return X
@staticmethod
def _batched_newton_schulz_step(X, a, b, c):
"""Single Newton-Schulz iteration step for 3D batched input."""
A = paddle.matmul(X, X, transpose_y=True)
B = paddle.baddbmm(A, A, A, beta=b, alpha=c)
X = paddle.baddbmm(X, B, X, beta=a, alpha=1.0)
return X
@staticmethod
def _scaling_fn(orthogonal_update, version, extra_scale_factor=1.0):
"""Apply dimension-dependent scaling to the orthogonal update."""
din, dout = orthogonal_update.shape[-2], orthogonal_update.shape[-1]
if version == 1:
scale = max(1, dout / din) ** 0.5
elif version == 2:
scale = (dout / din) ** 0.5
else: # version == 3 (default)
scale = max(dout, din) ** 0.5
return orthogonal_update * scale * extra_scale_factor
# ------------------------------------------------------------------
# Per-parameter update rules
# ------------------------------------------------------------------
def _adamw_update(
self,
param,
grad,
lr,
moment1,
moment2,
beta1_pow,
beta2_pow,
beta1,
beta2,
epsilon,
weight_decay,
):
"""In-place AdamW update for 1-D sharded parameters."""
lr_ratio = 1.0 if self._lr_ratio is None else self._lr_ratio(param)
with_decay = True
if (
self._apply_decay_param_fun is not None
and not self._apply_decay_param_fun(param.name)
):
with_decay = False
find_master = param.name in self._master_weights
master_weight = (
self._master_weights[param.name] if find_master else None
)
_, _, _, _, _, _, _ = _C_ops.adamw_(
param,
grad,
lr,
moment1,
moment2,
None, # moment2_max
beta1_pow,
beta2_pow,
master_weight,
None, # found_inf
beta1,
beta2,
epsilon,
lr_ratio,
weight_decay,
with_decay,
False, # lazy_mode
1000,
find_master,
False,
False, # amsgrad
)
def _muon_update(
self,
param,
grad,
lr,
momentum_buffer,
momentum_beta,
ns_steps,
nesterov,
epsilon,
weight_decay,
version,
):
"""In-place Muon update for a 2D parameter tensor.
Applies Newton-Schulz orthogonalisation to the 2D weight matrix and
updates the parameter in-place. MuonShardingOptimizer assigns whole
2D tensors to ranks, so no sharding gather or TP communication is needed.
"""
param_shape = getattr(param, "original_shape", param.shape)
param_info = self._muon_param_info_map.get(param.name)
with paddle.no_grad():
grad_f32 = (
grad.astype(momentum_buffer.dtype)
if grad.dtype != momentum_buffer.dtype
else grad
)
# Step 1: Momentum update
new_momentum = paddle.lerp(
momentum_buffer, grad_f32, 1.0 - momentum_beta
)
paddle.assign(new_momentum, momentum_buffer)
update_buffer = (
paddle.lerp(grad_f32, momentum_buffer, momentum_beta)
if nesterov
else momentum_buffer
)
# Step 2: Reshape update buffer to 2D matrix.
# MuonShardingOptimizer assigns whole 2D tensors to ranks, so params
# are already 2D/3D (no sharding gather needed).
matrix_2d_global = update_buffer.reshape(param_shape)
# Shared NS + scaling closure (captures ns_steps, epsilon, version, ns_coeffs)
def ortho_fn(m):
ns_out = Muon._zeropower_via_newtonschulz5(
m,
steps=ns_steps,
eps=epsilon,
ns_coeffs=self._ns_coeffs,
ns_matmul_dtype=self._ns_matmul_dtype,
)
scaled = Muon._scaling_fn(
ns_out, version, self._muon_extra_scale_factor
)
return scaled
# Step 3: Newton-Schulz orthogonalisation
# Use split_concat_func from param_info if provided, otherwise default to whole matrix
if (
param_info is not None
and param_info.split_concat_func is not None
):
# Use slice function defined in model configuration
orthogonal_update = param_info.split_concat_func(
matrix_2d_global, ortho_fn
)
if MUON_DEBUG:
_global_rank = paddle.distributed.get_rank()
if _global_rank == 0:
_sf = param_info.split_concat_func
_logger.info(
f"[Muon] Using split_concat_func: param={param.name}, "
f"split_concat_func={_sf.func.__name__}, "
f"args={_sf.args}, kwargs={_sf.keywords}"
)
else:
# Default: whole matrix orthogonalisation
orthogonal_update = ortho_fn(matrix_2d_global)
find_master = param.name in self._master_weights
master_weight = (
self._master_weights[param.name] if find_master else None
)
with_decay = True
if (
self._apply_decay_param_fun is not None
and not self._apply_decay_param_fun(param.name)
):
with_decay = False
if with_decay and weight_decay > 0:
if find_master:
master_weight.scale_(1.0 - lr * weight_decay)
else:
param.scale_(1.0 - lr * weight_decay)
final_step = orthogonal_update * lr
if find_master:
master_weight.subtract_(final_step)
paddle.assign(master_weight.astype(param.dtype), param)
else:
param.subtract_(final_step.astype(param.dtype))
# ------------------------------------------------------------------
# Core optimization step
# ------------------------------------------------------------------
def _apply_optimize(self, loss, startup_program, params_grads):
if not framework.in_dygraph_mode():
raise NotImplementedError(
"Muon optimizer only supports dygraph mode."
)
if self._grad_clip is not None:
params_grads = self._grad_clip(params_grads)
# apply for zcc
self._maybe_refuse()
group = self._default_dict
lr = self._learning_rate
if isinstance(lr, paddle.optimizer.lr.LRScheduler):
lr = lr()
wd = group.get("weight_decay", 0.0)
muon_params = []
adamw_params = []
for param, grad in params_grads:
if grad is None:
continue
param_info = self._muon_param_info_map.get(param.name)
assert param_info is not None, (
f"muon_param_info_map does not have {param.name}"
)
use_muon = param_info.use_muon
self._ensure_accumulators(param, use_muon, group)
if use_muon:
muon_params.append((param, grad))
else:
adamw_params.append((param, grad))
# --- Pass 1: Muon updates (large temporary tensors) ---
lr_tensor = paddle.to_tensor(lr, dtype=paddle.float32)
lr_tensor_f64 = paddle.to_tensor(lr, dtype=paddle.float64)
for param, grad in muon_params:
self._muon_update(
param,
grad,
lr_tensor,
self._get_accumulator(self._moment_acc_str, param),
group.get("momentum", 0.95),
group.get("ns_steps", 5),
group.get("nesterov", True),
group.get("epsilon", 1e-9),
wd,
version=group.get("muon_version", 3),
)
# --- Pass 2: AdamW updates ---
for param, grad in adamw_params:
self._adamw_update(
param,
grad,
lr_tensor_f64,
self._get_accumulator(self._moment_acc_str, param),
self._get_accumulator(self._moment2_acc_str, param),
self._get_accumulator(self._beta1_pow_acc_str, param),
self._get_accumulator(self._beta2_pow_acc_str, param),
group.get("adam_beta1", 0.9),
group.get("adam_beta2", 0.95),
group.get("epsilon", 1e-9),
wd,
)
@framework.dygraph_only
def step(self) -> None:
params_grads = [
(param, param._grad_ivar())
for param in self._parameter_list
if not param.stop_gradient and param._grad_ivar() is not None
]
self._apply_optimize(
loss=None, startup_program=None, params_grads=params_grads
)
def sharded_state_dict(
self,
model_sharded_state_dict: ShardedStateDict,
) -> ShardedStateDict:
"""Build a sharded optimizer state dict for flex checkpoint save/load.
The layout mirrors :class:`paddle.optimizer.AdamW`'s implementation so
that the same ``dist.save_state_dict`` / ``dist.load_state_dict`` path
works for Muon checkpoints.
Args:
model_sharded_state_dict: Sharded model state dict produced by
``model.sharded_state_dict()``.
Returns:
A dict mapping ``"<struct_name>.<state_type>"`` keys to
:class:`ShardedWeight` objects.
"""
_FP32_MASTER = "fp32_master_0"
_optimizer_scalar_names = [
"beta1_pow_acc_0",
"beta2_pow_acc_0",
]
_optimizer_vector_names = [
"moment1_0",
"moment2_0",
]
def _split_state_name(vname):
if _FP32_MASTER in vname:
return tuple(vname.split("_" + _FP32_MASTER + "_", 1))
for suffix in _optimizer_scalar_names + _optimizer_vector_names:
if vname.endswith(suffix):
return vname[: -(len(suffix) + 1)], suffix
raise ValueError(
f"Cannot parse optimizer state variable name: {vname!r}"
)
model_sharded_state_dict = dict(
sorted(model_sharded_state_dict.items())
)
# Build static-name → struct-name mapping (handles shared weights)
static_to_struct = {}
for struct_name, sw in model_sharded_state_dict.items():
local_name = sw.local_tensor.name
if local_name not in static_to_struct:
static_to_struct[local_name] = struct_name
optimizer_state_dict = self.state_dict()
master_weights = optimizer_state_dict.pop("master_weights", None)
optimizer_state_dict.pop("LR_Scheduler", None)
sharded_state: ShardedStateDict = {}
# Optimizer states (moment1, moment2, beta_pow scalars)
for key, tensor in optimizer_state_dict.items():
static_name, state_type = _split_state_name(key)
struct_name = static_to_struct[static_name]
sharded_param = model_sharded_state_dict[struct_name]
unified_name = f"{struct_name}.{state_type}"
if state_type in _optimizer_vector_names:
# Vector states share the same sharding layout as the parameter
if tensor.is_dist():
sharded_state[unified_name] = ShardedWeight(
key=unified_name,
local_tensor=tensor,
local_shape=tensor.shape,
global_shape=tensor.shape,
global_offset=sharded_param.global_offset,
)
else:
# Reshape accumulator if numel matches but shape differs.
# MoE: grouped_gemm_experts param.shape is 3D
# [n_experts, H, I] but model.state_dict() returns actual
# C++ storage shape 2D [n_experts*H, I]. moment1 was
# created with 3D shape, so we need to reshape here.
# V2 is unaffected: its moments are always 1D shards,
# so shape always matches and reshape is never triggered.
target_shape = sharded_param.local_shape
if (
tuple(tensor.shape) != tuple(target_shape)
and tensor.numel()
== paddle.to_tensor(list(target_shape)).prod().item()
):
tensor = tensor.reshape(target_shape)
sharded_state[unified_name] = (
create_sharded_weight_with_new_local(
unified_name, tensor, sharded_param
)
)
else:
# Scalar states (beta_pow) are replicated save as-is
sharded_state[unified_name] = ShardedWeight(
key=unified_name,
local_tensor=tensor,
local_shape=(1,),
global_shape=(1,),
global_offset=(0,),
)
# FP32 master weights
if master_weights:
for weight_key, tensor in master_weights.items():
struct_name = static_to_struct[weight_key]
sharded_param = model_sharded_state_dict[struct_name]
unified_name = f"{struct_name}.w_0"
if tensor.is_dist():
sharded_state[unified_name] = ShardedWeight(
key=unified_name,
local_tensor=tensor,
local_shape=tensor.shape,
global_shape=tensor.shape,
global_offset=sharded_param.global_offset,
)
else:
sharded_state[unified_name] = (
create_sharded_weight_with_new_local(
unified_name, tensor, sharded_param
)
)
return sharded_state