rnagabh commited on
Commit
5388aa0
·
verified ·
1 Parent(s): 93f12f3

Initial upload: Gemma 4 vision encoder (569.6M, 27-layer ViT with 2D RoPE)

Browse files
Files changed (1) hide show
  1. README.md +51 -34
README.md CHANGED
@@ -16,7 +16,18 @@ pipeline_tag: image-feature-extraction
16
  base_model: google/gemma-4-31B-it
17
  model-index:
18
  - name: gemma4-vision-encoder
19
- results: []
 
 
 
 
 
 
 
 
 
 
 
20
  ---
21
 
22
  # Gemma 4 Vision Encoder (27-layer ViT with 2D RoPE)
@@ -75,10 +86,11 @@ Unlike the audio encoder (which is identical across E2B and E4B), the vision enc
75
 
76
  ```python
77
  import torch
78
- from transformers import Gemma4VisionModel, Gemma4VisionConfig
79
  from safetensors.torch import load_file
 
80
 
81
- # Load vision encoder from this repo
82
  cfg = Gemma4VisionConfig.from_pretrained("rnagabh/gemma4-vision-encoder")
83
  vision_model = Gemma4VisionModel(cfg)
84
  state_dict = load_file("path/to/model.safetensors") # or download from repo
@@ -86,42 +98,47 @@ vision_model.load_state_dict(state_dict, strict=True)
86
  vision_model = vision_model.to(dtype=torch.bfloat16, device="cuda")
87
  vision_model.eval()
88
 
89
- # Prepare image: patchify and create position IDs
90
- # Image must have sides divisible by patch_size (16) AND
91
- # num_patches must be divisible by pooling_kernel^2 (9)
92
- # Good sizes: 864 (54 patches/side), 768 (48), 576 (36)
93
- P = 16
94
- img_size = 864
95
- patches_per_side = img_size // P # 54
96
-
97
- # Patchify: (B, C, H, W) → (B, num_patches, C*P*P)
98
- img = torch.randn(1, 3, img_size, img_size, dtype=torch.bfloat16, device="cuda")
99
- patches = img.unfold(2, P, P).unfold(3, P, P)
100
- patches = patches.contiguous().view(1, 3, -1, P, P)
101
- patches = patches.permute(0, 2, 1, 3, 4)
102
- patches = patches.reshape(1, -1, 3 * P * P) # (1, 2916, 768)
103
-
104
- # Position IDs: (batch, num_patches, 2) as (x, y) coordinates
105
- ys, xs = torch.meshgrid(
106
- torch.arange(patches_per_side),
107
- torch.arange(patches_per_side),
108
- indexing="ij",
109
- )
110
- position_ids = torch.stack([xs.flatten(), ys.flatten()], dim=-1)
111
- position_ids = position_ids.unsqueeze(0).to(device="cuda") # (1, 2916, 2)
112
 
113
  with torch.no_grad():
114
- output = vision_model(pixel_values=patches, pixel_position_ids=position_ids)
115
- embeddings = output.last_hidden_state # (324, 1152) — pooled tokens
 
 
 
116
  ```
117
 
118
- > **Image size constraints:** The number of patches must be divisible by the pooling kernel² (9).
119
- > This means each image dimension divided by patch_size (16) must be divisible by 3.
120
- > Valid image sizes include: 576, 768, 864, 960, 1152, etc.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
- > **Output shape:** The batch dimension is collapsed the pooler strips padding and returns
123
- > a flat `(num_valid_tokens, hidden_dim)` tensor. For a single 864×864 image, you get
124
- > `(324, 1152)` 324 pooled visual tokens at 1152 dimensions.
125
 
126
  ## Files in This Repo
127
 
 
16
  base_model: google/gemma-4-31B-it
17
  model-index:
18
  - name: gemma4-vision-encoder
19
+ results:
20
+ - task:
21
+ type: image-classification
22
+ name: CIFAR-10 (10-class)
23
+ dataset:
24
+ name: CIFAR-10
25
+ type: cifar10
26
+ split: test
27
+ metrics:
28
+ - type: accuracy
29
+ value: 94.0
30
+ name: Linear Probe Accuracy
31
  ---
32
 
33
  # Gemma 4 Vision Encoder (27-layer ViT with 2D RoPE)
 
86
 
87
  ```python
88
  import torch
89
+ from transformers import Gemma4VisionModel, Gemma4VisionConfig, AutoProcessor
90
  from safetensors.torch import load_file
91
+ from PIL import Image
92
 
93
+ # Load vision encoder
94
  cfg = Gemma4VisionConfig.from_pretrained("rnagabh/gemma4-vision-encoder")
95
  vision_model = Gemma4VisionModel(cfg)
96
  state_dict = load_file("path/to/model.safetensors") # or download from repo
 
98
  vision_model = vision_model.to(dtype=torch.bfloat16, device="cuda")
99
  vision_model.eval()
100
 
101
+ # Use the parent model's image processor for correct preprocessing
102
+ processor = AutoProcessor.from_pretrained("google/gemma-4-31B-it")
103
+ image_processor = processor.image_processor
104
+
105
+ # Process an image
106
+ img = Image.open("your_image.jpg")
107
+ processed = image_processor(images=[img], return_tensors="pt")
108
+
109
+ pixel_values = processed["pixel_values"].to(dtype=torch.bfloat16, device="cuda")
110
+ position_ids = processed["image_position_ids"].to(device="cuda")
111
+ tokens_per_image = processed["num_soft_tokens_per_image"] # for splitting batch output
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  with torch.no_grad():
114
+ output = vision_model(pixel_values=pixel_values, pixel_position_ids=position_ids)
115
+ embeddings = output.last_hidden_state # (num_tokens, 1152)
116
+
117
+ # Mean-pool for a single image vector
118
+ image_embedding = embeddings.float().mean(dim=0) # (1152,)
119
  ```
120
 
121
+ > **Important:** Always use `Gemma4ImageProcessor` from the parent model for preprocessing.
122
+ > It handles resizing, patchification, position ID generation, and pixel normalization.
123
+ > Manual patchification without this processor will produce degraded results.
124
+
125
+ ## Benchmark Results (frozen 1152-dim embeddings, linear probe)
126
+
127
+ ### CIFAR-10 Classification
128
+
129
+ | Metric | Value |
130
+ |---|---|
131
+ | Linear probe accuracy | **94.0%** |
132
+ | Random baseline | 10.0% |
133
+ | Improvement over chance | **9.4×** |
134
+ | Dataset | CIFAR-10 test set (1000 samples, 100 per class) |
135
+ | Probe | Logistic regression on L2-normalized mean-pooled embeddings |
136
+
137
+ Strong performance across all classes: airplane (0.98 F1), ship (0.98 F1), truck (0.97 F1), automobile (0.97 F1). Weakest class is cat (0.86 F1) — a fine-grained category that is inherently harder.
138
 
139
+ > **Important:** Use the parent model's image processor (`Gemma4ImageProcessor` from `google/gemma-4-31B-it`)
140
+ > for correct preprocessing. Manual patchification without proper resizing and position ID generation
141
+ > will produce significantly degraded results.
142
 
143
  ## Files in This Repo
144