Download validate_gemma2.py from maxffarrell/embeddinggemma-2-CoreAI: direct link, hf CLI and curl.
- Browser
- Download file 5.25 kB
-
https://huggingface.co/maxffarrell/embeddinggemma-2-CoreAI/resolve/main/validate_gemma2.py
- Command line
-
hf download hf://maxffarrell/embeddinggemma-2-CoreAI/validate_gemma2.py
-
curl -L -o validate_gemma2.py https://huggingface.co/maxffarrell/embeddinggemma-2-CoreAI/resolve/main/validate_gemma2.py
5.25 kB
| import asyncio | |
| import json | |
| import shutil | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| from PIL import Image | |
| from transformers import AutoModel, AutoProcessor | |
| from coreai_embeddinggemma2 import EmbeddingGemma2CoreAI | |
| def cases(): | |
| wave = (0.2 * np.sin(2 * np.pi * 440 * np.arange(16000) / 16000)).astype(np.float32) | |
| image = Image.new("RGB", (320, 192), (20, 80, 210)) | |
| gradient = Image.fromarray( | |
| np.tile(np.arange(256, dtype=np.uint8)[None, :, None], (128, 1, 3)) | |
| ) | |
| frames = np.stack( | |
| [ | |
| np.asarray(image.resize((128, 96))), | |
| np.asarray(Image.new("RGB", (128, 96), (210, 50, 20))), | |
| ] | |
| ) | |
| return [ | |
| ( | |
| "query", | |
| {"text": "What causes the northern lights?", "prompt_name": "SearchQuery"}, | |
| ), | |
| ( | |
| "document", | |
| { | |
| "text": "The northern lights are caused by charged particles from the sun.", | |
| "prompt_name": "Document", | |
| }, | |
| ), | |
| ("unprefixed", {"text": "An apple a day keeps the doctor away."}), | |
| ( | |
| "code", | |
| {"text": "def add(a, b): return a + b", "prompt_name": "CodeRetrieval"}, | |
| ), | |
| ("spanish", {"text": "La biblioteca está abierta hasta las ocho."}), | |
| ("japanese", {"text": "桜の花が春に咲きます。"}), | |
| ("short", {"text": "cat"}), | |
| ("long", {"text": "The oceans contain diverse ecosystems. " * 90}), | |
| ("near-capacity", {"text": "The oceans contain diverse ecosystems. " * 160}), | |
| ("image", {"images": image}), | |
| ("gradient", {"images": gradient}), | |
| ("image-text", {"text": "<|image|> A blue rectangle.", "images": image}), | |
| ("two-images", {"images": [image, gradient]}), | |
| ("audio", {"audio": wave}), | |
| ("audio-text", {"text": "<|audio|> A musical tone.", "audio": wave}), | |
| ("audio-short", {"audio": wave[:3200]}), | |
| ("audio-long", {"audio": np.tile(wave, 5)}), | |
| ("audio-limit", {"audio": np.pad(np.tile(wave, 11), (0, 3200))}), | |
| ("video", {"videos": frames}), | |
| ("video-text", {"text": "<|video|> Colors changing.", "videos": frames}), | |
| ( | |
| "mixed", | |
| { | |
| "text": "<|image|> Blue. <|audio|> Tone. <|video|> Colors.", | |
| "images": image, | |
| "audio": wave, | |
| "videos": frames, | |
| }, | |
| ), | |
| ] | |
| async def main(): | |
| torch.set_num_threads(4) | |
| root = Path("bundles/embeddinggemma-2-CoreAI") | |
| source = Path("upstream/embeddinggemma-2") | |
| for f in source.iterdir(): | |
| if f.is_file() and f.suffix in [".json", ".jinja", ".model"]: | |
| shutil.copy2(f, root / f.name) | |
| m = AutoModel.from_pretrained( | |
| source, | |
| dtype=torch.float32, | |
| attn_implementation={ | |
| "language_model": "sdpa", | |
| "vision_tower": "sdpa", | |
| "audio_tower": "eager", | |
| }, | |
| ).eval() | |
| p = AutoProcessor.from_pretrained(source) | |
| print("Loading Core AI", flush=True) | |
| runtime = await EmbeddingGemma2CoreAI.load(root) | |
| print("Loaded Core AI", flush=True) | |
| records = [] | |
| for name, args in cases(): | |
| data = dict(args) | |
| prompt = data.pop("prompt_name", None) | |
| if prompt: | |
| data["text"] = runtime.prompts[prompt] + data["text"] | |
| if "audio" in data: | |
| data["sampling_rate"] = 16000 | |
| if "videos" in data: | |
| data["videos_kwargs"] = {"do_sample_frames": False} | |
| e = p(**data, return_tensors="pt") | |
| with torch.no_grad(): | |
| h = m(**e).last_hidden_state | |
| mask = e["attention_mask"][:, :, None].float() | |
| reference = torch.nn.functional.normalize( | |
| (h * mask).sum(1) / mask.sum(1), dim=-1 | |
| )[0].numpy() | |
| actual = await runtime.encode_processed({k: v.numpy() for k, v in e.items()}) | |
| record = { | |
| "name": name, | |
| "tokens": e["input_ids"].shape[1], | |
| "max_abs_error": float(np.max(np.abs(actual - reference))), | |
| "cosine": float(actual @ reference), | |
| "finite": bool(np.isfinite(actual).all()), | |
| "unit_norm": float(np.linalg.norm(actual)), | |
| } | |
| print(record, flush=True) | |
| records.append(record) | |
| assert ( | |
| record["finite"] | |
| and record["cosine"] > 0.99999 | |
| and record["max_abs_error"] < 0.00005 | |
| ), record | |
| for dim in [128, 256, 512]: | |
| actual = await runtime.encode("The sky is blue.", truncate_dim=dim) | |
| assert actual.shape == (dim,) and abs(np.linalg.norm(actual) - 1) < 1e-5 | |
| try: | |
| await runtime.encode("word " * 3000) | |
| except ValueError: | |
| pass | |
| else: | |
| raise AssertionError("Over-capacity input accepted") | |
| (root / "validation.json").write_text( | |
| json.dumps( | |
| { | |
| "thresholds": {"min_cosine": 0.99999, "max_abs_error": 0.00005}, | |
| "cases": records, | |
| "truncation_dims": [128, 256, 512], | |
| "oversized_input_rejected": True, | |
| }, | |
| indent=2, | |
| ) | |
| + "\n" | |
| ) | |
| if __name__ == "__main__": | |
| asyncio.run(main()) | |