@@ -544,9 +544,15 @@ def get_layout_map(
544544 cross-attention are fused into a single `merged_attention` layer
545545 (see `T5Gemma2MergedAttention`), which shares the same
546546 query/key/value/attention_output naming as the encoder's
547- self-attention and is covered by the same rules. Vision/interleave/
548- EOI-embedding weights are left replicated for now -- see Limitations
549- in the PR description.
547+ self-attention and is covered by the same rules.
548+
549+ When a `Gemma3VisionEncoder` is attached, its transformer weights are
550+ also sharded (2-D `(in, out)` Dense kernels, following the Megatron
551+ column-/row-parallel convention). Weights that are intentionally left
552+ replicated -- the patch `embedding_conv`, `position_embedding`,
553+ pooling, all vision norms/biases, and the interleave/EOI embeddings --
554+ are small and do not benefit from sharding; see the vision-encoder
555+ rules below.
550556
551557 Args:
552558 device_mesh: keras.distribution.DeviceMesh. The device mesh
@@ -587,26 +593,37 @@ def get_layout_map(
587593 model_dim ,
588594 data_dim ,
589595 )
596+ # reverse_embeddings is the output-projection tensor of shape
597+ # (hidden, vocab); the vocab-parallel output projection wants vocab on
598+ # the model axis, mirroring token_embedding's (vocab, hidden) =
599+ # (model_dim, data_dim). Only untied embeddings materialize this
600+ # tensor, but tie_word_embeddings is a real constructor arg propagated
601+ # from HF configs, so it must not be left silently replicated.
590602 layout_map ["decoder_token_embedding/reverse_embeddings" ] = (
591- model_dim ,
592603 data_dim ,
604+ model_dim ,
593605 )
594- # Key/value kernels are sized by num_key_value_heads (GQA), which is
595- # independent of and typically much smaller than num_query_heads --
596- # sharding that small axis would raise an IndivisibleError whenever
597- # the mesh dimension doesn't evenly divide it, so that axis is left
598- # replicated; the large hidden_dim axis is still sharded via
599- # model_dim. The broad "attention" wildcard covers both the
600- # encoder's self_attention and the decoder's merged (self+cross)
601- # attention, which share the same
606+ # QKV kernels are (hidden, num_heads, head_dim) (einsum btd,dnh->btnh):
607+ # the query kernel shards the contracting hidden dim on data_dim and
608+ # the head dim on model_dim (Megatron column-parallel), and its
609+ # attention_output (num_heads, head_dim, hidden) shards heads on
610+ # model_dim to match -- so the two collectives cancel into one
611+ # all-reduce per attention block. Key/value kernels are sized by
612+ # num_key_value_heads (GQA), which is independent of and typically much
613+ # smaller than num_query_heads -- sharding that small head axis on
614+ # model_dim would raise an IndivisibleError whenever the mesh dimension
615+ # doesn't evenly divide it, so it is left replicated; the large hidden
616+ # dim is still sharded on data_dim for memory. The broad "attention"
617+ # wildcard covers both the encoder's self_attention and the decoder's
618+ # merged (self+cross) attention, which share the same
602619 # query/key/value/attention_output naming.
603620 layout_map ["encoder_layer.*attention.*query.kernel" ] = (
604- model_dim ,
605621 data_dim ,
622+ model_dim ,
606623 None ,
607624 )
608625 layout_map ["encoder_layer.*attention.*(key|value).kernel" ] = (
609- model_dim ,
626+ data_dim ,
610627 None ,
611628 None ,
612629 )
@@ -616,12 +633,12 @@ def get_layout_map(
616633 data_dim ,
617634 )
618635 layout_map ["decoder_layer.*attention.*query.kernel" ] = (
619- model_dim ,
620636 data_dim ,
637+ model_dim ,
621638 None ,
622639 )
623640 layout_map ["decoder_layer.*attention.*(key|value).kernel" ] = (
624- model_dim ,
641+ data_dim ,
625642 None ,
626643 None ,
627644 )
@@ -636,6 +653,29 @@ def get_layout_map(
636653 layout_map ["decoder_layer.*gate_proj.kernel" ] = (data_dim , model_dim )
637654 layout_map ["decoder_layer.*up_proj.kernel" ] = (data_dim , model_dim )
638655 layout_map ["decoder_layer.*down_proj.kernel" ] = (model_dim , data_dim )
656+
657+ # Vision encoder (Gemma3VisionEncoder), present only for multimodal
658+ # variants. All 2-D Dense kernels `(in, out)`: qkv/mlp_dense_1 are
659+ # column-parallel (fused output on model_dim), out_proj/mlp_dense_2 and
660+ # the vision_input_projection are row-parallel. The patch
661+ # `embedding_conv`, `position_embedding`, pooling, all vision
662+ # norms/biases, and the interleave/EOI embeddings are intentionally
663+ # left replicated (small; sharding them adds divisibility risk for no
664+ # memory benefit). These rules are inert when `vision_encoder=None`.
665+ layout_map [
666+ "image_encoder.*multi_head_attention.*"
667+ "(query_proj|key_proj|value_proj).kernel"
668+ ] = (data_dim , model_dim )
669+ layout_map ["image_encoder.*multi_head_attention.*out_proj.kernel" ] = (
670+ model_dim ,
671+ data_dim ,
672+ )
673+ layout_map ["image_encoder.*mlp_dense_1.kernel" ] = (data_dim , model_dim )
674+ layout_map ["image_encoder.*mlp_dense_2.kernel" ] = (model_dim , data_dim )
675+ layout_map ["vision_output_encoder.*vision_input_projection.kernel" ] = (
676+ model_dim ,
677+ data_dim ,
678+ )
639679 return layout_map
640680
641681 @classmethod
0 commit comments