Download convert_gemma2.py from maxffarrell/embeddinggemma-2-CoreAI: direct link, hf CLI and curl.
- Browser
- Download file 5.32 kB
-
https://huggingface.co/maxffarrell/embeddinggemma-2-CoreAI/resolve/main/convert_gemma2.py
- Command line
-
hf download hf://maxffarrell/embeddinggemma-2-CoreAI/convert_gemma2.py
-
curl -L -o convert_gemma2.py https://huggingface.co/maxffarrell/embeddinggemma-2-CoreAI/resolve/main/convert_gemma2.py
5.32 kB
| import json | |
| from pathlib import Path | |
| import torch | |
| import torch.nn.functional as F | |
| from coreai_torch import TorchConverter, get_decomp_table | |
| from transformers import AutoModel, AutoProcessor | |
| def unfold(x, dimension, size, step): | |
| dimension %= x.ndim | |
| return torch.stack( | |
| [ | |
| x.narrow(dimension, i, size) | |
| for i in range(0, x.shape[dimension] - size + 1, step) | |
| ], | |
| dim=dimension, | |
| ).movedim(dimension + 1, -1) | |
| def save(model, sample, path): | |
| with torch.no_grad(): | |
| ep = torch.export.export(model, sample, strict=False) | |
| table = get_decomp_table() | |
| table[torch.ops.aten.unfold.default] = unfold | |
| ep = ep.run_decompositions(table) | |
| print("Converting", path, flush=True) | |
| TorchConverter().add_exported_program(ep).to_coreai().save_asset(path) | |
| return list(ep.graph_signature.user_outputs) | |
| class Text(torch.nn.Module): | |
| def __init__(self, m): | |
| super().__init__() | |
| self.m = m.language_model | |
| def forward(self, input_ids, attention_mask, media_embeddings, media_mask): | |
| emb = self.m.embed_tokens(input_ids) | |
| emb = torch.where(media_mask, media_embeddings.to(emb.dtype), emb) | |
| n = input_ids.shape[1] | |
| pos = torch.arange(n, device=input_ids.device).unsqueeze(0) | |
| mask = attention_mask[:, None, None, :].bool().expand(1, 1, n, n) | |
| sliding = ( | |
| pos[:, :, None] - pos[:, None, :] | |
| ).abs() <= self.m.config.sliding_window | |
| hidden = self.m( | |
| inputs_embeds=emb, | |
| position_ids=pos, | |
| attention_mask={ | |
| "full_attention": mask, | |
| "sliding_attention": mask & sliding[:, None], | |
| }, | |
| ).last_hidden_state | |
| weights = attention_mask[:, :, None].float() | |
| pooled = (hidden.float() * weights).sum(1) / weights.sum(1).clamp(min=1) | |
| return F.normalize(pooled, dim=-1) | |
| class Vision(torch.nn.Module): | |
| def __init__(self, m): | |
| super().__init__() | |
| self.v = m.vision_tower | |
| self.project = m.embed_vision | |
| def forward(self, pixel_values, pixel_position_ids): | |
| v = self.v | |
| pad = (pixel_position_ids == -1).all(-1) | |
| emb = v.patch_embedder(pixel_values, pixel_position_ids, pad) | |
| hidden = v.encoder( | |
| inputs_embeds=emb, | |
| attention_mask=~pad, | |
| pixel_position_ids=pixel_position_ids, | |
| ).last_hidden_state | |
| hidden, mask = v.pooler( | |
| hidden, pixel_position_ids, pad, pixel_values.shape[1] // 9 | |
| ) | |
| return self.project(hidden.to(emb.dtype)), mask | |
| class Audio(torch.nn.Module): | |
| def __init__(self, m): | |
| super().__init__() | |
| self.a = m.audio_tower | |
| self.project = m.embed_audio | |
| def forward(self, input_features, input_features_mask): | |
| out = self.a(input_features, attention_mask=input_features_mask) | |
| return self.project(out.last_hidden_state), out.attention_mask | |
| if __name__ == "__main__": | |
| torch.set_num_threads(4) | |
| root = Path("bundles/embeddinggemma-2-CoreAI") | |
| root.mkdir(parents=True, exist_ok=True) | |
| m = AutoModel.from_pretrained( | |
| "upstream/embeddinggemma-2", | |
| dtype=torch.float32, | |
| attn_implementation={ | |
| "language_model": "sdpa", | |
| "vision_tower": "sdpa", | |
| "audio_tower": "eager", | |
| }, | |
| ).eval() | |
| print("Loaded", flush=True) | |
| manifest = { | |
| "source": "google/embeddinggemma-2", | |
| "revision": "914f7f89142e33e77833254d9c9b90c3cef7303b", | |
| "precision": "float32", | |
| "sequence_length": 1024, | |
| "transformers_revision": "536ecc007387a50e77603bb5d92100e9b07514cc", | |
| } | |
| sample = ( | |
| torch.ones(1, 1024, dtype=torch.int32), | |
| torch.ones(1, 1024, dtype=torch.int32), | |
| torch.zeros(1, 1024, 512), | |
| torch.zeros(1, 1024, 1, dtype=torch.bool), | |
| ) | |
| manifest["text_outputs"] = save(Text(m).eval(), sample, root / "embedding.aimodel") | |
| (root / "manifest.json").write_text(json.dumps(manifest, indent=2)) | |
| p = AutoProcessor.from_pretrained("upstream/embeddinggemma-2") | |
| from PIL import Image | |
| e = p(images=Image.new("RGB", (224, 224)), return_tensors="pt") | |
| print("Image shapes", [(k, tuple(v.shape)) for k, v in e.items()], flush=True) | |
| sample = (e["pixel_values"].float(), e["image_position_ids"].int()) | |
| manifest["vision_outputs"] = save(Vision(m).eval(), sample, root / "vision.aimodel") | |
| manifest["vision_shape"] = list(sample[0].shape) | |
| (root / "manifest.json").write_text(json.dumps(manifest, indent=2)) | |
| import numpy as np | |
| e = p( | |
| audio=np.sin(np.arange(16000) * 0.1).astype(np.float32), | |
| sampling_rate=16000, | |
| return_tensors="pt", | |
| ) | |
| print("Audio shapes", [(k, tuple(v.shape)) for k, v in e.items()], flush=True) | |
| sample = ( | |
| F.pad( | |
| e["input_features"].float(), (0, 0, 0, 1120 - e["input_features"].shape[1]) | |
| ), | |
| F.pad( | |
| e["input_features_mask"].bool(), | |
| (0, 1120 - e["input_features_mask"].shape[1]), | |
| ), | |
| ) | |
| manifest["audio_outputs"] = save(Audio(m).eval(), sample, root / "audio.aimodel") | |
| manifest["audio_shape"] = list(sample[0].shape) | |
| (root / "manifest.json").write_text(json.dumps(manifest, indent=2)) | |