Skip to content

Commit 0a06fcc

Browse files
Add on-the-fly dynamic SafeTensors loading support and remove redundant tensor handling logic
PiperOrigin-RevId: 930868709
1 parent 9fcf589 commit 0a06fcc

8 files changed

Lines changed: 614 additions & 170 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, param_key_parts_from_path, 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, param_key_parts_from_path, 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
@@ -340,151 +341,7 @@ def get_maxtext_model_info(config):
340341
return maxtext_abstract_dict, abstract_params_treedef
341342

342343

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

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

489346

490347
def _get_maxtext_indices_and_shapes(mt_param_key_or_keys, maxtext_abstract_dict):

0 commit comments

Comments
 (0)