| --- |
| license: apache-2.0 |
| --- |
| |
| ```python |
| import torch |
| from transformers import AutoProcessor |
| from transformers.models.gemma4_unified.configuration_gemma4_unified import ( |
| Gemma4UnifiedConfig, |
| Gemma4UnifiedTextConfig, |
| Gemma4UnifiedVisionConfig, |
| ) |
| from transformers.models.gemma4_unified.modeling_gemma4_unified import Gemma4UnifiedForConditionalGeneration |
| |
| |
| save_dir = "./tiny-random-gemma4-unified-it" |
| |
| # Tiny text config mirroring the 12B gemma4_unified architecture: |
| # - PLE disabled (no hidden_size_per_layer_input field exists on unified text config) |
| # - attention_k_eq_v=True so full-attention layers fuse v_proj into k_proj |
| # - num_global_key_value_heads=1, global_head_dim larger than head_dim |
| # - num_kv_shared_layers=0 (12B does not share KV across layers) |
| # - use_bidirectional_attention="vision" |
| text_config = Gemma4UnifiedTextConfig( |
| hidden_size=32, |
| intermediate_size=64, |
| num_hidden_layers=4, |
| num_attention_heads=4, |
| num_key_value_heads=2, |
| head_dim=16, |
| global_head_dim=32, |
| num_global_key_value_heads=1, |
| vocab_size=262144, |
| max_position_embeddings=512, |
| rms_norm_eps=1e-6, |
| hidden_activation="gelu_pytorch_tanh", |
| sliding_window=64, |
| layer_types=["sliding_attention", "sliding_attention", "sliding_attention", "full_attention"], |
| num_kv_shared_layers=0, |
| attention_k_eq_v=True, |
| use_double_wide_mlp=False, |
| use_bidirectional_attention="vision", |
| final_logit_softcapping=30.0, |
| tie_word_embeddings=True, |
| ) |
| |
| # Vision is an encoder-free embedder: model_patch_size = patch_size * pooling_kernel_size. |
| # mm_embed_dim / output_proj_dims must match the text hidden_size. |
| vision_config = Gemma4UnifiedVisionConfig( |
| patch_size=16, |
| pooling_kernel_size=3, |
| mm_embed_dim=32, |
| output_proj_dims=32, |
| mm_posemb_size=128, |
| rms_norm_eps=1e-6, |
| ) |
| |
| config = Gemma4UnifiedConfig( |
| text_config=text_config.to_dict(), |
| vision_config=vision_config.to_dict(), |
| audio_config=None, |
| boi_token_id=255999, |
| eoi_token_id=258882, |
| image_token_id=258880, |
| video_token_id=258884, |
| boa_token_id=256000, |
| eoa_token_index=258883, |
| audio_token_id=258881, |
| tie_word_embeddings=True, |
| ) |
| |
| # Seed before init so the random weights are reproducible. This seed produces a fixture |
| # whose greedy generation has no near-ties, so OV-vs-transformers token equality is stable |
| # under the small (~1e-4) numerical differences of OpenVINO inference. |
| torch.manual_seed(42) |
| model = Gemma4UnifiedForConditionalGeneration(config) |
| model = model.to(dtype=torch.float32) |
| model.eval() |
| |
| print(f"Model parameters: {sum(p.numel() for p in model.parameters()):,}") |
| |
| model.save_pretrained(save_dir) |
| |
| # Reuse the reference processor but shrink the soft-token budget so the tiny |
| # position embedding table (mm_posemb_size=64) is large enough. |
| processor = AutoProcessor.from_pretrained("google/gemma-4-12b-it") |
| processor.image_processor.max_soft_tokens = 70 |
| processor.image_processor.image_seq_length = 70 |
| processor.save_pretrained(save_dir) |
| |
| print(f"Tiny Gemma4Unified model saved to {save_dir}") |
| |
| # Sanity forward pass |
| input_ids = torch.randint(0, 262144, (1, 10)) |
| with torch.no_grad(): |
| out = model(input_ids=input_ids) |
| print("logits shape:", out.logits.shape) |
| print("Forward pass OK!") |
| |
| ``` |