6767from maxtext .layers .quantizations import AqtQuantization as Quant
6868from maxtext .inference import kvcache
6969from maxtext .inference .kvcache import KVQuant
70- from maxtext .utils .sharding import maybe_shard_with_logical , create_sharding
70+ from maxtext .utils .sharding import maybe_shard_with_logical , create_sharding , logical_to_mesh_axes
7171
7272# pylint: disable=line-too-long, g-doc-args, g-doc-return-or-yield, bad-continuation, g-inconsistent-quotes
7373# pytype: disable=attribute-error
@@ -555,6 +555,18 @@ def __init__(
555555 debug_sharding = config .debug_sharding ,
556556 )
557557
558+ def _logical_to_mesh_axes (self , logical_name ):
559+ logical_rules = None if self .config .using_pipeline_parallelism else self .config .logical_axis_rules
560+ return logical_to_mesh_axes (logical_name , mesh = self .mesh , rules = logical_rules )
561+
562+ def _validate_kv_heads (self ) -> None :
563+ """Validates the number of key/value heads."""
564+ if self .num_kv_heads == - 1 :
565+ raise ValueError ("num_kv_heads is not defined." )
566+
567+ if self .num_query_heads % self .num_kv_heads != 0 :
568+ raise ValueError ("Invalid num_kv_heads for GQA." )
569+
558570 def _init_projections (self , inputs_q_shape : Tuple , inputs_kv_shape : Tuple ) -> None :
559571 """Initializes the query, key, value, and output projections."""
560572 if self .config .fused_qkv :
@@ -622,11 +634,7 @@ def init_kv_w(self, inputs_kv_shape: Tuple) -> nnx.Module:
622634 Returns:
623635 A DenseGeneral module that performs the key or value projection.
624636 """
625- if self .num_kv_heads == - 1 :
626- raise ValueError ("num_kv_heads is not defined." )
627-
628- if self .num_query_heads % self .num_kv_heads != 0 :
629- raise ValueError ("Invalid num_kv_heads for GQA." )
637+ self ._validate_kv_heads ()
630638
631639 kernel_axes = (
632640 (None , None , None )
@@ -672,12 +680,15 @@ def kv_projection(self, inputs_kv: Array, proj_name: str, out_sharding: NamedSha
672680 raise ValueError (f"proj_name must be 'key' or 'value', but got { proj_name } " )
673681
674682 def init_qkv_w (self , inputs_shape : Tuple ) -> nnx .Module :
683+ """Initializes the a fused QKV projection using only one DenseGeneral module."""
684+ self ._validate_kv_heads ()
685+
675686 return DenseGeneral (
676687 in_features_shape = self .convert_dense_general_inputs_shape (inputs_shape ),
677- out_features_shape = (3 , self .num_query_heads , self .head_dim ),
688+ out_features_shape = (self .num_query_heads + 2 * self . num_kv_heads , self .head_dim ),
678689 axis = - 1 ,
679690 kernel_init = self .kernel_init ,
680- kernel_axes = ("embed" , "qkv" , " heads" , "kv" ),
691+ kernel_axes = ("embed" , "heads" , "kv" ),
681692 dtype = self .dtype ,
682693 weight_dtype = self .weight_dtype ,
683694 quant = self .quant ,
@@ -692,8 +703,22 @@ def qkv_projection(self, inputs: Array, proj_name: str, out_sharding: NamedShard
692703
693704 qkv_proj = self .qkv_proj (inputs , out_sharding )
694705 qkv_proj = checkpoint_name (qkv_proj , "qkv_proj" )
695- query , key , value = qkv_proj [:, :, 0 , ...], qkv_proj [:, :, 1 , ...], qkv_proj [:, :, 2 , ...]
696- return query , key , value
706+
707+ # Since fused QKV projection places all heads along the same axis which could be tensor
708+ # parallel partitioned, we must use shard_map to split into equally partitioned Q, K, V arrays.
709+ q_bshd = self ._logical_to_mesh_axes (self .query_axis_names )
710+ k_bshd = self ._logical_to_mesh_axes (self .key_axis_names )
711+ v_bshd = self ._logical_to_mesh_axes (self .value_axis_names )
712+
713+ @jax .shard_map (mesh = self .mesh , in_specs = (q_bshd ,), out_specs = (q_bshd , k_bshd , v_bshd ))
714+ def split_qkv (qkv_proj : Array ) -> tuple [Array , Array , Array ]:
715+ num_local_heads = qkv_proj .shape [2 ]
716+ num_query_heads = (num_local_heads * self .num_query_heads ) // (self .num_query_heads + 2 * self .num_kv_heads )
717+ num_kv_heads = (num_local_heads - num_query_heads ) // 2
718+
719+ return tuple (jnp .split (qkv_proj , [num_query_heads , num_query_heads + num_kv_heads ], axis = 2 ))
720+
721+ return split_qkv (qkv_proj )
697722
698723 @property
699724 def out_head_dim (self ) -> int :
0 commit comments