Skip to content

Commit 2b1cdce

Browse files
Add on-the-fly dynamic SafeTensors loading support and remove redundant tensor handling logic
PiperOrigin-RevId: 930868709
1 parent 63abe03 commit 2b1cdce

8 files changed

Lines changed: 515 additions & 163 deletions

File tree

src/maxtext/checkpoint_conversion/to_maxtext.py

Lines changed: 2 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@
6767
from maxtext.common.common_types import MODEL_MODE_TRAIN
6868
from maxtext.checkpoint_conversion.utils.hf_model_configs import HF_MODEL_CONFIGS
6969
from maxtext.checkpoint_conversion.utils.param_mapping import HOOK_FNS, PARAM_MAPPING
70-
from maxtext.checkpoint_conversion.utils.utils import MemoryMonitorTqdm, apply_hook_fns, load_hf_dict_from_transformers, load_hf_dict_from_safetensors, print_peak_memory, print_ram_usage, save_weights_to_checkpoint, validate_and_filter_param_map_keys
70+
from maxtext.checkpoint_conversion.utils.tensor_handling import apply_hook_fns, _get_hf_loading_function
71+
from maxtext.checkpoint_conversion.utils.utils import MemoryMonitorTqdm, load_hf_dict_from_transformers, load_hf_dict_from_safetensors, print_peak_memory, print_ram_usage, save_weights_to_checkpoint, validate_and_filter_param_map_keys
7172
from maxtext.inference.inference_utils import str2bool
7273
from maxtext.layers import quantizations
7374
from maxtext.models import models
@@ -341,151 +342,7 @@ def get_maxtext_model_info(config):
341342
return maxtext_abstract_dict, abstract_params_treedef
342343

343344

344-
def _build_multi_axis_stacked_tensor(
345-
hf_source_keys: List[List[str]],
346-
tensor_getter_fn: Callable[[str], np.ndarray],
347-
hook_fns: Any,
348-
target_shape: tuple,
349-
config,
350-
) -> np.ndarray:
351-
"""Builds a MaxText tensor by stacking HF weights along two axes (experts and layers).
352-
353-
This function handles the complex case for scanned MoE layers, producing a tensor
354-
with the shape (num_experts, num_layers, ...).
355-
356-
Args:
357-
hf_source_keys: A nested (2D) list of Hugging Face parameter names.
358-
Outer list iterates experts, inner list iterates layers.
359-
tensor_getter_fn: A callable that takes a HF key and returns the tensor (as numpy array).
360-
hook_fns: The hook function(s) to apply to each individual weight.
361-
target_shape: The final shape of the target MaxText tensor.
362-
config: The MaxText pyconfig object.
363-
364-
Returns:
365-
The final, assembled NumPy array for the MaxText parameter.
366-
"""
367-
all_expert_tensors = []
368-
# The hook function needs the shape of an individual slice, not the full stacked tensor.
369-
# For multi-axis stacking (experts, layers, ...), the slice shape is target_shape[2:]
370-
mt_slice_shape = target_shape[2:]
371-
372-
# Outer loop iterates through experts
373-
for layer_keys_for_expert in hf_source_keys:
374-
layer_tensors_for_expert = []
375-
# Inner loop iterates through layers for the current expert
376-
for hf_key_single in layer_keys_for_expert:
377-
if isinstance(hf_key_single, (list, tuple)):
378-
hf_tensor_numpy = tuple(tensor_getter_fn(k) for k in hf_key_single)
379-
else:
380-
hf_tensor_numpy = tensor_getter_fn(hf_key_single)
381-
processed_hf_tensor = apply_hook_fns(hf_tensor_numpy, mt_slice_shape, hook_fns)
382-
layer_tensors_for_expert.append(processed_hf_tensor)
383-
all_expert_tensors.append(np.stack(layer_tensors_for_expert, axis=0))
384-
return np.stack(all_expert_tensors, axis=0)
385-
386-
387-
def _build_single_axis_stacked_tensor(
388-
hf_source_keys: List[str],
389-
tensor_getter_fn: Callable[[str], np.ndarray],
390-
hook_fns: Any,
391-
target_shape: tuple,
392-
config,
393-
) -> np.ndarray:
394-
"""Builds a MaxText tensor by stacking HF weights along a single axis.
395-
396-
This function handles both standard scanned layers (e.g., attention) and
397-
unscanned MoE layers (which are stacked along the expert axis).
398-
399-
Args:
400-
hf_source_keys: A 1D list of Hugging Face parameter names.
401-
tensor_getter_fn: A callable that takes a HF key and returns the tensor (as numpy array).
402-
hook_fns: The hook function(s) to apply to each individual weight.
403-
target_shape: The final shape of the target MaxText tensor.
404-
config: The MaxText pyconfig object.
405-
406-
Returns:
407-
The final, assembled NumPy array for the MaxText parameter.
408-
"""
409-
tensors_to_stack = []
410-
411-
if config.scan_layers:
412-
# If it's a standard scanned layer, we use the configured param_scan_axis.
413-
axis_to_stack = config.param_scan_axis
414-
else:
415-
# Otherwise, if an unscanned MoE layer, and we stack along the expert axis (0).
416-
axis_to_stack = 0
417-
418-
# The hook function needs the shape of an individual slice, not the full stacked tensor.
419-
# We calculate it by removing the stacking dimension from the final target shape.
420-
mt_slice_shape_list = list(target_shape)
421-
del mt_slice_shape_list[axis_to_stack]
422-
mt_slice_shape = tuple(mt_slice_shape_list)
423-
424-
for hf_key_single in hf_source_keys:
425-
if isinstance(hf_key_single, (list, tuple)):
426-
hf_tensor_numpy = tuple(tensor_getter_fn(k) for k in hf_key_single)
427-
else:
428-
hf_tensor_numpy = tensor_getter_fn(hf_key_single)
429-
processed_hf_tensor = apply_hook_fns(hf_tensor_numpy, mt_slice_shape, hook_fns)
430-
tensors_to_stack.append(processed_hf_tensor)
431-
432-
# Stack all processed tensors along the determined axis.
433-
return np.stack(tensors_to_stack, axis=axis_to_stack)
434-
435-
436-
def _get_hf_loading_function(hf_source_keys_or_key, tensor_getter, hook_fn, mt_target_shape_or_shapes, config):
437-
"""Determine the loading function for HF keys.
438-
439-
This function natively supports `composite_hf_key` mapping (where multiple HF keys
440-
combine into a single MaxText parameter, like Qwen3.5's qkv and z -> in_proj_qkvz).
441-
If the input is a tuple of strings, they are fetched as a tuple of arrays and passed
442-
together into the model hook.
443345

444-
HF keys can take four forms:
445-
Case 1: Unscanned (single string)
446-
Case 2: Scanned (list of strings)
447-
Case 3: Unscanned with expert stacking (list of strings)
448-
Case 4: Scanned with expert stacking (nested list of strings)
449-
"""
450-
load_fn = None
451-
if not isinstance(hf_source_keys_or_key, list):
452-
# Case 1: Single hf key (str)
453-
def _loader(getter, key, shape, hook):
454-
if isinstance(key, (list, tuple)):
455-
tensors = tuple(getter(k) for k in key)
456-
return apply_hook_fns(tensors, shape, hook)
457-
return apply_hook_fns(getter(key), shape, hook)
458-
459-
load_fn = partial(
460-
_loader,
461-
tensor_getter,
462-
hf_source_keys_or_key,
463-
mt_target_shape_or_shapes,
464-
hook_fn,
465-
)
466-
# Stacked mapping
467-
elif not isinstance(hf_source_keys_or_key[0], list):
468-
# Case 2 or 3: Single-Axis Stacked hf keys (un-nested list)
469-
load_fn = partial(
470-
_build_single_axis_stacked_tensor,
471-
hf_source_keys_or_key,
472-
tensor_getter,
473-
hook_fn,
474-
mt_target_shape_or_shapes,
475-
config,
476-
)
477-
else:
478-
# isinstance(hf_source_keys_or_key[0], list)
479-
# Case 4: Multi-Axis Stacked hf keys (nested list)
480-
load_fn = partial(
481-
_build_multi_axis_stacked_tensor,
482-
hf_source_keys_or_key,
483-
tensor_getter,
484-
hook_fn,
485-
mt_target_shape_or_shapes,
486-
config,
487-
)
488-
return load_fn
489346

490347

491348
def _get_maxtext_indices_and_shapes(mt_param_key_or_keys, maxtext_abstract_dict):

0 commit comments

Comments
 (0)