1919import json
2020import os
2121import re
22- from typing import Any , Optional
22+ from typing import Optional
2323
2424from flax import nnx , linen as nn
2525from flax .linen import partitioning as nn_partitioning
@@ -465,10 +465,9 @@ def _build_lora_provider(mt_config: pyconfig.HyperParameters) -> qwix.LoraProvid
465465 return qwix .LoraProvider (** lora_kwargs )
466466
467467
468- def _prepare_dummy_inputs () -> tuple [jnp .ndarray , jnp .ndarray ]:
468+ def _prepare_dummy_inputs (dummy_bs : int = 1 ) -> tuple [jnp .ndarray , jnp .ndarray ]:
469469 """Builds dummy decoder inputs used to materialize LoRA parameters."""
470470 # Keep LoRA warmup as small as possible to minimize compile/memory overhead.
471- dummy_bs = 1
472471 seq_len = 1
473472 decoder_input_tokens = jnp .zeros ((dummy_bs , seq_len ), dtype = jnp .int32 )
474473 decoder_positions = jnp .zeros ((dummy_bs , seq_len ), dtype = jnp .int32 )
@@ -483,30 +482,50 @@ def is_lora_enabled(model: nnx.Module) -> bool:
483482 return False
484483
485484
486- def _verify_lora_parameters (lora_model : nnx .Module , mt_config : pyconfig .HyperParameters ):
485+ def _verify_lora_parameters (lora_model : nnx .Module , mt_config : pyconfig .HyperParameters ) -> None :
487486 """Validates that LoRA is active or that target modules were matched."""
488487
489488 if is_lora_enabled (lora_model ):
489+ wrapped_modules = set ()
490+ for path , value in nnx .iter_graph (lora_model ):
491+ if isinstance (value , nnx .LoRAParam ):
492+ if len (path ) > 1 :
493+ parent_path = "/" .join (str (p ) for p in path [:- 1 ])
494+ wrapped_modules .add (parent_path )
495+
496+ if wrapped_modules :
497+ wrapped_modules = sorted (list (wrapped_modules ))
498+ max_logging .log (
499+ f"LoRA configured: module_path='{ _get_lora_module_path (mt_config )} ' successfully matched "
500+ f"{ len (wrapped_modules )} target submodules."
501+ )
502+ preview_limit = 20
503+ preview_modules = wrapped_modules [:preview_limit ]
504+ max_logging .log (f"Sample matched submodules ({ len (preview_modules )} of { len (wrapped_modules )} ): { preview_modules } " )
505+ else :
506+ max_logging .log ("LoRA is enabled. (Detailed submodules match report skipped due to mock model or empty state)" )
490507 return
491508
492509 lora_module_path = _get_lora_module_path (mt_config )
493510 compiled_module_path = re .compile (lora_module_path )
494- matched_module_paths = []
495- sample_module_paths = []
496511
512+ matched_module_paths = []
497513 for path , _ in nnx .iter_modules (lora_model ):
498514 module_path = "/" .join (str (p ) for p in path )
499- if len (sample_module_paths ) < 100 :
500- sample_module_paths .append (module_path )
501- if compiled_module_path .search (module_path ):
515+ if module_path and compiled_module_path .search (module_path ):
502516 matched_module_paths .append (module_path )
503517
504518 if not matched_module_paths :
505- max_logging .log (
506- f"LoRA module_path='{ lora_module_path } ' did not match any weights. " f"Sample module paths: { sample_module_paths } "
507- )
519+ max_logging .log (f"Error: LoRA module_path='{ lora_module_path } ' did not match any weights." )
508520 raise ValueError ("LoRA enabled but no LoRA parameters found in decoder/model state." )
509521
522+ # Simplify matched paths by replacing numeric layer indices with "*" to avoid redundant output
523+ simplified_matches = sorted (
524+ {"/" .join ("*" if p .isdigit () else p for p in path .split ("/" )) for path in matched_module_paths }
525+ )
526+ max_logging .log (f"LoRA target verification: successfully matched { len (matched_module_paths )} modules." )
527+ max_logging .log (f"Matched submodule patterns: { simplified_matches } " )
528+
510529 raise ValueError (
511530 "LoRA module path matched target modules, but nnx.LoRAParam is still "
512531 "missing. For Tunix PeftTrainer, LoRA params must be materialized before "
@@ -572,8 +591,12 @@ def apply_lora_to_model(
572591
573592 lora_provider = _build_lora_provider (mt_config )
574593
594+ dp_size = 1
595+ if mesh is not None and "data" in mesh .shape :
596+ dp_size = mesh .shape ["data" ]
597+
575598 model_rngs = getattr (model .decoder , "rngs" , None )
576- decoder_input_tokens , decoder_positions = _prepare_dummy_inputs ()
599+ decoder_input_tokens , decoder_positions = _prepare_dummy_inputs (dummy_bs = dp_size )
577600
578601 lora_model = qwix .apply_lora_to_model (
579602 model ,
@@ -621,20 +644,27 @@ def _safe_reshard(var, sharding_spec):
621644 return lora_model
622645
623646
624- def restore_lora_from_path (trainer : Any , mt_config : pyconfig .HyperParameters ) -> Any :
625- """Restores LoRA parameter weights from an external Orbax checkpoint for a fresh run."""
647+ def restore_lora_from_path (model : nnx .Module , mt_config : pyconfig .HyperParameters ) -> nnx .Module :
648+ """Restores LoRA parameter weights from an external Orbax checkpoint.
649+
650+ This function performs the restore in-place on the model's parameters and
651+ returns the model with the restored weights applied.
652+
653+ Args:
654+ model: The JAX/Flax NNX model (nnx.Module).
655+ mt_config: The HyperParameters config containing the lora configuration.
656+
657+ Returns:
658+ The model with the restored LoRA weights applied in-place.
659+
660+ Raises:
661+ ValueError: If LoRA is not enabled on the model, but a restore path is set.
662+ """
626663 lora_restore_path = mt_config .lora .lora_restore_path
627664 if not lora_restore_path :
628- return trainer
629-
630- train_steps = getattr (trainer , "train_steps" , 0 )
631- if train_steps > 0 :
632- max_logging .log (
633- f"PeftTrainer restored current run at step { train_steps } ; " f"ignoring lora_restore_path '{ lora_restore_path } '."
634- )
635- return trainer
665+ return model
636666
637- if not is_lora_enabled (trainer . model ):
667+ if not is_lora_enabled (model ):
638668 lora_module_path = _get_lora_module_path (mt_config )
639669 if not mt_config .lora .enable_lora :
640670 raise ValueError (
@@ -644,7 +674,7 @@ def restore_lora_from_path(trainer: Any, mt_config: pyconfig.HyperParameters) ->
644674
645675 sync_lora_metadata (mt_config )
646676
647- abstract_lora_params = nnx .state (trainer . model , nnx .LoRAParam )
677+ abstract_lora_params = nnx .state (model , nnx .LoRAParam )
648678
649679 target_for_restore = jax .tree .map (
650680 lambda v : {"value" : v .value },
@@ -700,9 +730,9 @@ def _map_to_state(path, variable):
700730 is_leaf = lambda n : isinstance (n , nnx .Variable ),
701731 )
702732
703- nnx .update (trainer . model , abstract_lora_params )
733+ nnx .update (model , abstract_lora_params )
704734 max_logging .log (f"LoRA restore complete from '{ lora_restore_path } '." )
705- return trainer
735+ return model
706736
707737
708738# NNX-shaped LoRA helpers.
0 commit comments