Add custom MLP vision projector and tests#4579
Closed
subawocit wants to merge 11 commits into
Closed
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
PiperOrigin-RevId: 952918614
PiperOrigin-RevId: 952967390
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR adds support for configurable custom vision projectors in MaxText's multimodal models. It introduces new vision projector config options, handles checkpoint weight mismatches when attaching custom projector architectures to pre-trained base models, and adds a test script to verify projector initialization and embedding extraction.
Step 2 of a 5-step Proof-of-Concept for any-to-any multimodal alignment in MaxText. Overall goal is to align a pretrained vision encoders with another text-only LLM, and connecting the two with a dynamically configured adaptation layer (customized MLP connector), special tokenizer mapping for visual placeholder tokens, and train only the MLP using supervised fine tuning.
Changes
Custom MLP Projector Module (
src/maxtext/layers/encoders.py)MultimodalMLPProjector(nnx.Module), supporting configurable layer, activations, hidden size, and layer biases.VisionEncoder._setup_vision_encoder_layers()to conditionally override the default vision projector withMultimodalMLPProjectorwhenevervision_projector_type == "customized_mlp".Config (
src/maxtext/configs/types.py)VisionProjectorconfiguration class for customizing projector architecture.Checkpoint Loading & Weight Mismatch Handling
src/maxtext/common/checkpointing.py_is_custom_vision_projector_problem()to check if missing/mismatched weight keys belong to custom vision projector layers._raise_on_weight_mismatch()to suppress weight mismatch errors for custom projector layers only, and raise a warning instead. This allows restoring base model parameters from standard checkpoints while leaving newly introduced custom projector weights randomly initialized.src/maxtext/utils/model_creation_utils.py_free_device_memoryinsidefrom_pretrained().is_custom) to skip deleting JAX arrays corresponding to custom layers (i.e., layers with "custom_linear" in their name). Reason: Pretrained base model checkpoints do not contain weights for new custom components; if their device memory were freed prior to checkpoint restoration, their initializations would be deleted with no incoming checkpoint weights to replace them. Preserving these arrays ensures their randomly initialized state remains alive in device memory when base model parameters are loaded.Test Script (
src/maxtext/experimental/omni_poc/tests/custom_vision_projector_test.py)qwen3-vl-2b) with a customized MLP vision projector.Example Usage
Tests
1. Unit Tests
Command:
Output:
Command:
Output:
2. Custom Vision Projector Verification
Validates that vision embeddings are the same between the standard model and
custom-projector model before the projector layer,
but differ after the projector layer due to randomly initialized projector.
Command:
python3 src/maxtext/experimental/omni_poc/tests/custom_vision_projector_test.py \ --load_parameters_path=<your-path> \ --hf_access_token=<your-token>Output:
3. End-to-End Multimodal Decoding Checks
A. Baseline Multimodal Run (Default Vision Projector)
Verifies standard multimodal inference functions normally.
Command:
python3 -m maxtext.inference.decode \ src/maxtext/configs/base.yml \ model_name=qwen3-vl-2b \ tokenizer_path=Qwen/Qwen3-VL-2B-Instruct \ tokenizer_type=huggingface \ load_parameters_path=<your-path> \ per_device_batch_size=1 \ scan_layers=false \ use_multimodal=true \ prompt='Describe this image' \ image_path='tests/assets/test_image.jpg' \ max_prefill_predict_length=512 \ max_target_length=768 \ ici_tensor_parallelism=4 \ override_model_config=true \ attention='dot_product' \ hf_access_token=<your-token>Output:
B. Custom Vision Projector Pipeline Run
Verifies end-to-end data flow with custom projector flags (
customized_mlp). Output text is bad due to random weight initialization, as expected.Command:
python3 -m maxtext.inference.decode \ src/maxtext/configs/base.yml \ model_name=qwen3-vl-2b \ tokenizer_path=Qwen/Qwen3-VL-2B-Instruct \ tokenizer_type=huggingface \ load_parameters_path=<your-path> \ per_device_batch_size=1 \ run_name=runner_image_2026-06-26-20-40 \ scan_layers=false \ use_multimodal=true \ prompt='Describe this image' \ image_path='tests/assets/test_image.jpg' \ max_prefill_predict_length=512 \ max_target_length=768 \ ici_tensor_parallelism=4 \ override_model_config=true \ attention='dot_product' \ hf_access_token=<your-token> \ vision_projector_type=customized_mlp \ vision_connector_num_layers=2 \ vision_connector_activation=gelu \ vision_connector_use_bias=trueOutput:
4. Text-Only Decoding Regression Check
Verifies custom vision projector configurations do not affect text-only inference mode (
use_multimodal=false).Command:
python3 -m maxtext.inference.decode \ src/maxtext/configs/base.yml \ model_name=qwen3-vl-2b \ tokenizer_path=Qwen/Qwen3-VL-2B-Instruct \ tokenizer_type=huggingface \ load_parameters_path=<your-path> \ per_device_batch_size=1 \ max_prefill_predict_length=128 \ max_target_length=256 \ scan_layers=false \ use_multimodal=false \ prompt='How many times does the letter r appear in the word Strawberry?' \ override_model_config=true \ ici_fsdp_parallelism=1 \ ici_tensor_parallelism=-1 \ hf_access_token=<your-token> \ vision_projector_type=customized_mlp \ vision_connector_num_layers=2 \ vision_connector_activation=gelu \ vision_connector_use_bias=trueOutput:
Step-by-Step Objectives
√Multi-Directory Checkpoint Restoration: Implemented selective sub-tree parameter loading using Orbax such that we can initiaze a multimodal model with parameters from multiple checkpoints.√Dynamic MLP Connector: Add omni modal adapter layer (MLP) to connect vision tower output to the LLM decodernextSpecial Tokenizer & Placeholder Masking: Add special token <|image|> to tokenizer and make sure the masking is correct in the decoder.Checklist
Before submitting this PR, please make sure (put X in square brackets):
gemini-reviewlabel.