|
19 | 19 | import os |
20 | 20 | from typing import Sequence |
21 | 21 |
|
22 | | -from flax import nnx, linen as nn |
23 | | -from flax.core.spmd import composite_rules, from_sharding_rules, get_logical_axis_rules |
| 22 | +from flax import linen as nn, nnx |
24 | 23 | from flax.linen import partitioning as nn_partitioning |
25 | 24 | from flax.training.train_state import TrainState |
26 | | - |
27 | | -import numpy as np |
28 | | - |
29 | 25 | import jax |
30 | | -import jax.numpy as jnp |
31 | | -from jax.sharding import AxisType, Mesh, NamedSharding, PartitionSpec |
32 | 26 | from jax.experimental import mesh_utils |
33 | 27 | from jax.experimental.serialize_executable import deserialize_and_load |
34 | | - |
35 | | -import optax |
36 | | -import orbax.checkpoint.experimental.emergency.checkpoint_manager as emergency_checkpoint_manager |
37 | | -import orbax.checkpoint.experimental.emergency.replicator_checkpoint_manager as emergency_replicator_checkpoint_manager |
38 | | - |
39 | | -from maxtext.configs import pyconfig |
| 28 | +import jax.numpy as jnp |
| 29 | +from jax.sharding import AxisType, Mesh, NamedSharding |
| 30 | +from maxtext.common import checkpointing |
40 | 31 | from maxtext.common.common_types import ( |
41 | 32 | AttentionType, |
42 | 33 | DecoderBlockType, |
43 | | - MODEL_MODE_PREFILL, |
44 | 34 | MODEL_MODE_AUTOREGRESSIVE, |
| 35 | + MODEL_MODE_PREFILL, |
45 | 36 | ReorderStrategy, |
46 | 37 | ShardMode, |
47 | 38 | ) |
| 39 | +from maxtext.configs import pyconfig |
48 | 40 | from maxtext.configs import types |
49 | | -from maxtext.common import checkpointing |
50 | 41 | from maxtext.multimodal import processor as mm_processor |
| 42 | +from maxtext.utils import elastic_utils |
51 | 43 | from maxtext.utils import gcs_utils |
52 | 44 | from maxtext.utils import max_logging |
53 | 45 | from maxtext.utils import max_utils |
54 | | -from maxtext.utils import sharding |
55 | | -from maxtext.utils import elastic_utils |
56 | 46 | from maxtext.utils import maxtext_utils_nnx |
| 47 | +from maxtext.utils import sharding |
| 48 | +import numpy as np |
| 49 | +import optax |
| 50 | +import orbax.checkpoint.experimental.emergency.checkpoint_manager as emergency_checkpoint_manager |
| 51 | +import orbax.checkpoint.experimental.emergency.replicator_checkpoint_manager as emergency_replicator_checkpoint_manager |
57 | 52 |
|
58 | 53 | OVERWRITE_WITH_GRADIENT = "_overwrite_with_gradient" |
59 | 54 |
|
@@ -1612,88 +1607,6 @@ def move(path, x): |
1612 | 1607 | ) |
1613 | 1608 |
|
1614 | 1609 |
|
1615 | | -def get_nnx_named_sharding_with_scan_axis(abs_var_state: nnx.State, mesh) -> nnx.State: |
1616 | | - """Compute NamedSharding for each NNX variable, correctly handling the scan (stacked layers) axis. |
1617 | | -
|
1618 | | - Unlike flax.nnx.spmd.get_var_pspec (used inside nnx.get_abstract_model), this function also |
1619 | | - inserts the partition_name axis at the correct scan_axis position for parameters created by |
1620 | | - _create_scanned_layers. Without this, scanned parameters get a 2D partition spec applied to a |
1621 | | - 3D tensor, placing sharding on the stacked-layers dimension instead of the embedding dimension. |
1622 | | -
|
1623 | | - Args: |
1624 | | - abs_var_state: NNX abstract variable state from nnx.split(nnx.eval_shape(...)). |
1625 | | - mesh: JAX physical mesh. |
1626 | | -
|
1627 | | - Returns: |
1628 | | - Same tree structure as abs_var_state but each Variable's value replaced with NamedSharding. |
1629 | | - """ |
1630 | | - |
1631 | | - def _make_named_sharding(v): |
1632 | | - val = v.get_value() |
1633 | | - if not hasattr(val, "shape"): |
1634 | | - # `val` is either truly leafless (e.g. optax MaskedNode) or a composite |
1635 | | - # pytree of tensors (e.g. AQT QTensor on serve-mode quantized variables — |
1636 | | - # a `qvalue` int8 array + a list of `scale` bf16 arrays). For the latter |
1637 | | - # we must emit a parallel tree of NamedSharding leaves so the downstream |
1638 | | - # `jax.tree.map(lambda a, s: ShapeDtypeStruct(..., sharding=s), abs, names)` |
1639 | | - # finds a real Sharding at every position. Replicated sharding is a safe |
1640 | | - # default — AQT serve-mode QTensors are normally small (per-channel scale |
1641 | | - # factors and packed int8 weights) and don't need axis-aware sharding. |
1642 | | - if jax.tree_util.tree_leaves(val): |
1643 | | - replicated = NamedSharding(mesh, PartitionSpec()) |
1644 | | - return v.replace(jax.tree.map(lambda _: replicated, val)) |
1645 | | - return v |
1646 | | - metadata = v.get_metadata() |
1647 | | - out_sharding = metadata.get("out_sharding") or metadata.get("sharding_names") or metadata.get("sharding") |
1648 | | - if not out_sharding: |
1649 | | - pspec = PartitionSpec() |
1650 | | - else: |
1651 | | - # Insert the scan axis for parameters created by _create_scanned_layers. |
1652 | | - # _add_scan_metadata stores the axis name in nnx.PARTITION_NAME and the |
1653 | | - # axis index in "param_scan_axis". flax.nnx.spmd.get_var_pspec ignores these. |
1654 | | - if nnx.PARTITION_NAME in metadata: |
1655 | | - partition_name = metadata[nnx.PARTITION_NAME] |
1656 | | - # Always use param_scan_axis from metadata. OptVariable (optimizer state) inherits |
1657 | | - # param_scan_axis=1 from the model Param via to_opt_state(), so we must not hardcode |
1658 | | - # scan_axis=0 for non-Param types. stacked_rest non-Param variables have |
1659 | | - # param_scan_axis=0 set explicitly by _add_scan_metadata, so this is always correct. |
1660 | | - scan_axis = metadata.get("param_scan_axis", 0) |
1661 | | - out_sharding = [out_sharding] if isinstance(out_sharding, str) else list(out_sharding) |
1662 | | - # Guard against double-insertion: Flax 0.12.6 _remap_sharding_metadata renames |
1663 | | - # 'sharding' -> 'out_sharding', so _add_scan_metadata may have already inserted |
1664 | | - # the scan axis. Only insert if not already present. |
1665 | | - if partition_name not in out_sharding: |
1666 | | - out_sharding.insert(scan_axis, partition_name) |
1667 | | - out_sharding = tuple(out_sharding) |
1668 | | - # Convert logical axis names to physical mesh axes using current context rules. |
1669 | | - context_rules = get_logical_axis_rules() |
1670 | | - local_rules = metadata.get("sharding_rules", ()) |
1671 | | - if context_rules or local_rules: |
1672 | | - rules = composite_rules(context_rules, local_rules) |
1673 | | - raw_sharding = from_sharding_rules(out_sharding, rules) |
1674 | | - mesh_axis_names = mesh.axis_names if mesh is not None else () |
1675 | | - |
1676 | | - # from_sharding_rules leaves a logical name with no matching rule unchanged, so a |
1677 | | - # name missing from logical_axis_rules (e.g. concat_embed on the MTP kernel) |
1678 | | - # reaches NamedSharding and is rejected as an unknown mesh axis. Map any such |
1679 | | - # leftover name to None (replicated), matching Linen, whose logical_to_mesh_axes |
1680 | | - # replicates unmatched names. |
1681 | | - def _sanitize(x): |
1682 | | - if isinstance(x, list): |
1683 | | - x = tuple(x) |
1684 | | - if x is None or (isinstance(x, str) and x in mesh_axis_names) or isinstance(x, tuple): |
1685 | | - return x |
1686 | | - return None |
1687 | | - |
1688 | | - sanitized_sharding = [_sanitize(x) for x in raw_sharding] |
1689 | | - pspec = PartitionSpec(*sanitized_sharding) |
1690 | | - else: |
1691 | | - pspec = PartitionSpec(*out_sharding) |
1692 | | - return v.replace(NamedSharding(mesh, pspec)) |
1693 | | - |
1694 | | - return jax.tree.map(_make_named_sharding, abs_var_state, is_leaf=lambda x: isinstance(x, nnx.Variable)) |
1695 | | - |
1696 | | - |
1697 | 1610 | def get_abstract_state_nnx(config, mesh, nnx_init_trainstate_fn, is_training=True): |
1698 | 1611 | """Calculates the abstract sharded state and memory placement for an NNX TrainState. |
1699 | 1612 |
|
@@ -1724,26 +1637,29 @@ def get_abstract_state_nnx(config, mesh, nnx_init_trainstate_fn, is_training=Tru |
1724 | 1637 |
|
1725 | 1638 | with nn_partitioning.axis_rules(config.logical_axis_rules): |
1726 | 1639 | # Use nnx.eval_shape + nnx.split instead of nnx.get_abstract_model, so we can apply |
1727 | | - # get_nnx_named_sharding_with_scan_axis which correctly inserts the stacked-layers |
| 1640 | + # nnx_construct_named_sharding which correctly inserts the stacked-layers |
1728 | 1641 | # axis into the partition spec. nnx.get_abstract_model uses get_var_pspec internally |
1729 | 1642 | # which ignores nnx.PARTITION_NAME / param_scan_axis metadata set by _create_scanned_layers, |
1730 | 1643 | # causing the 2D partition spec to be misapplied to the 3D stacked parameter tensor. |
1731 | 1644 | # Do NOT wrap nnx.eval_shape in jax.set_mesh: Flax 0.12.6's _to_variable calls |
1732 | 1645 | # var.shape for every variable when a global mesh is active, but masked optimizer |
1733 | 1646 | # state variables (e.g. from trainable_parameters_mask) have value=MaskedNode() |
1734 | | - # which has no .shape and would raise AttributeError. We handle sharding |
1735 | | - # ourselves via get_nnx_named_sharding_with_scan_axis, so auto-assignment is not |
1736 | | - # needed here. |
| 1647 | + # which has no .shape and would raise AttributeError. We handle sharding |
| 1648 | + # ourselves via nnx_construct_named_sharding, so auto-assignment is not needed here. |
1737 | 1649 | abs_model = nnx.eval_shape(nnx_init_trainstate_fn) |
1738 | 1650 | _, abs_var_state = nnx.split(abs_model) |
1739 | | - named_sharding_state = get_nnx_named_sharding_with_scan_axis(abs_var_state, mesh) |
| 1651 | + named_sharding_state = sharding.nnx_construct_named_sharding( |
| 1652 | + abs_var_state, mesh |
| 1653 | + ) |
1740 | 1654 | abstract_state = jax.tree.map( |
1741 | 1655 | lambda a, s: jax.ShapeDtypeStruct(a.shape, a.dtype, sharding=s), |
1742 | 1656 | abs_var_state, |
1743 | 1657 | named_sharding_state, |
1744 | 1658 | ) |
1745 | 1659 |
|
1746 | | - state_mesh_shardings = maxtext_utils_nnx.get_named_sharding_nnx(abstract_state) |
| 1660 | + state_mesh_shardings = maxtext_utils_nnx.nnx_extract_named_sharding( |
| 1661 | + abstract_state |
| 1662 | + ) |
1747 | 1663 |
|
1748 | 1664 | if is_training and config.shard_optimizer_over_data: |
1749 | 1665 | # Add data to sharding for optimizer state |
@@ -1849,10 +1765,10 @@ def _nnx_cache_partition_specs(abstract_model, config, mesh): |
1849 | 1765 | way it does for the Linen helpers below. |
1850 | 1766 | """ |
1851 | 1767 | _, cache_state, _ = nnx.split(abstract_model, nnx.Cache, ...) |
1852 | | - # get_nnx_named_sharding_with_scan_axis reads logical axis rules from the |
| 1768 | + # nnx_construct_named_sharding reads logical axis rules from the |
1853 | 1769 | # active flax partitioning context, so wrap. |
1854 | 1770 | with nn_partitioning.axis_rules(config.logical_axis_rules): |
1855 | | - named_state = get_nnx_named_sharding_with_scan_axis(cache_state, mesh) |
| 1771 | + named_state = sharding.nnx_construct_named_sharding(cache_state, mesh) |
1856 | 1772 | return jax.tree.map(lambda s: s.spec, named_state.to_pure_dict()) |
1857 | 1773 |
|
1858 | 1774 |
|
|
0 commit comments