|
67 | 67 | from maxtext.common.common_types import MODEL_MODE_TRAIN |
68 | 68 | from maxtext.checkpoint_conversion.utils.hf_model_configs import HF_MODEL_CONFIGS |
69 | 69 | 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 |
71 | 72 | from maxtext.inference.inference_utils import str2bool |
72 | 73 | from maxtext.layers import quantizations |
73 | 74 | from maxtext.models import models |
@@ -341,151 +342,7 @@ def get_maxtext_model_info(config): |
341 | 342 | return maxtext_abstract_dict, abstract_params_treedef |
342 | 343 |
|
343 | 344 |
|
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. |
443 | 345 |
|
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 |
489 | 346 |
|
490 | 347 |
|
491 | 348 | def _get_maxtext_indices_and_shapes(mt_param_key_or_keys, maxtext_abstract_dict): |
|
0 commit comments