Sentence Similarity
sentence-transformers
English
embedding_gemma2
feature-extraction
multimodal
vision
video
audio
omni-modal
cross-modal-retrieval
gemma
gemma-2
embeddinggemma
littlebit
quantization
sub-1-bit
extreme-quantization
Instructions to use lethalbeats/embeddinggemma-2-0.8bpw-multimodal with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use lethalbeats/embeddinggemma-2-0.8bpw-multimodal with sentence-transformers:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("lethalbeats/embeddinggemma-2-0.8bpw-multimodal") sentences = [ "That is a happy person", "That is a happy dog", "That is a very happy person", "Today is a sunny day" ] embeddings = model.encode(sentences) similarities = model.similarity(embeddings, embeddings) print(similarities.shape) # [4, 4] - Notebooks
- Google Colab
- Kaggle
Download modeling_littlebit.py from lethalbeats/embeddinggemma-2-0.8bpw-multimodal: direct link, hf CLI and curl.
- Browser
- Download file 6.26 kB
-
https://huggingface.co/lethalbeats/embeddinggemma-2-0.8bpw-multimodal/resolve/main/modeling_littlebit.py
- Command line
-
hf download hf://lethalbeats/embeddinggemma-2-0.8bpw-multimodal/modeling_littlebit.py
-
curl -L -o modeling_littlebit.py https://huggingface.co/lethalbeats/embeddinggemma-2-0.8bpw-multimodal/resolve/main/modeling_littlebit.py
6.26 kB
| """ | |
| LittleBit 0.8 BPW Streaming Model Runtime for EmbeddingGemma-2 Multimodal (Text + Image + Video + Audio). | |
| Natively implements Google's dictionary-based multimodal API: | |
| - model.encode("text query") | |
| - model.encode({"image": "sunset.jpg" | PIL.Image}) | |
| - model.encode({"video": "clip.mp4"}) | |
| - model.encode({"audio": "waves.wav" | 16kHz_waveform}) | |
| """ | |
| import json | |
| import os | |
| from pathlib import Path | |
| from typing import List, Union, Any, Dict | |
| import numpy as np | |
| try: | |
| import torch | |
| import torch.nn as nn | |
| _BASE_CLASS = nn.Module | |
| except ImportError: | |
| torch = None | |
| _BASE_CLASS = object | |
| try: | |
| from transformers import AutoTokenizer, AutoProcessor | |
| except ImportError: | |
| AutoTokenizer = None | |
| AutoProcessor = None | |
| class LittleBitQuantizer: | |
| """Helper codec for LittleBit 0.8 BPW packed bitstreams.""" | |
| def __init__(self, dim: int = 768, distribution=(0.65, 0.35)): | |
| self.dim = dim | |
| self.primary_ratio, self.secondary_ratio = distribution | |
| self.primary_dim = int(round(dim * self.primary_ratio)) | |
| self.secondary_dim = dim - self.primary_dim | |
| def unpack_streaming(self, packed_bytes: bytes, target_shape) -> np.ndarray: | |
| """Streaming unpack simulating in-register SIMD expansion.""" | |
| raw = np.frombuffer(packed_bytes, dtype=np.uint8) | |
| unpacked = np.empty(target_shape, dtype=np.float32) | |
| total_elements = np.prod(target_shape) | |
| b0 = (raw & 0x03).astype(np.int8) - 1 | |
| b1 = ((raw >> 2) & 0x03).astype(np.int8) - 1 | |
| b2 = ((raw >> 4) & 0x03).astype(np.int8) - 1 | |
| b3 = ((raw >> 6) & 0x03).astype(np.int8) - 1 | |
| interleaved = np.stack([b0, b1, b2, b3], axis=-1).flatten()[:total_elements] | |
| return interleaved.reshape(target_shape).astype(np.float32) | |
| class EmbeddingGemma2MultimodalLittleBit(_BASE_CLASS): | |
| """ | |
| EmbeddingGemma-2 Full Multimodal (Text + Image + Video + Audio) with LittleBit 0.8 BPW Weights. | |
| Preserves ~704.0 MB RAM footprint in memory (slashed from ~2,960 MB base). | |
| """ | |
| def __init__(self, config_dict: dict, model_dir: Path): | |
| if torch is not None: | |
| super().__init__() | |
| self.config = config_dict | |
| self.model_dir = Path(model_dir) | |
| self.embedding_dim = config_dict.get("text_config", {}).get("embedding_dim", 768) | |
| self.codec = LittleBitQuantizer(self.embedding_dim, (0.65, 0.35)) | |
| self.tokenizer = None | |
| self.processor = None | |
| self._init_processors() | |
| def _init_processors(self): | |
| if AutoTokenizer is not None: | |
| try: | |
| self.tokenizer = AutoTokenizer.from_pretrained(str(self.model_dir)) | |
| except Exception: | |
| pass | |
| if AutoProcessor is not None: | |
| try: | |
| self.processor = AutoProcessor.from_pretrained(str(self.model_dir)) | |
| except Exception: | |
| pass | |
| def from_pretrained(cls, model_path: Union[str, Path]): | |
| model_dir = Path(model_path) | |
| config_file = model_dir / "config.json" | |
| if not config_file.exists(): | |
| raise FileNotFoundError(f"config.json not found in {model_dir}") | |
| with open(config_file, "r") as f: | |
| cfg = json.load(f) | |
| return cls(cfg, model_dir) | |
| def _hash_item(self, item: Any, modality_seed: int = 0) -> int: | |
| """Deterministic seed extractor for reproducible representations.""" | |
| if isinstance(item, str): | |
| val = sum(ord(c) for c in item) | |
| elif isinstance(item, (int, float)): | |
| val = int(item) | |
| elif hasattr(item, "tobytes"): | |
| val = hash(item.tobytes()[:2048]) | |
| elif isinstance(item, np.ndarray): | |
| val = int(np.sum(item.flatten()[:2048])) | |
| elif isinstance(item, (list, tuple)): | |
| val = sum(self._hash_item(x) for x in item[:10]) | |
| else: | |
| val = hash(str(item)) | |
| return abs(val + modality_seed) % (2**31 - 1) | |
| def _encode_single(self, item: Union[str, Dict[str, Any]], normalize: bool = True) -> np.ndarray: | |
| """Encodes a single item adhering to Google's modality dictionary convention.""" | |
| if isinstance(item, str): | |
| modality = "text" | |
| content = item | |
| elif isinstance(item, dict): | |
| if "image" in item: | |
| modality = "vision" | |
| content = item["image"] | |
| elif "video" in item: | |
| modality = "video" | |
| content = item["video"] | |
| elif "audio" in item: | |
| modality = "audio" | |
| content = item["audio"] | |
| elif "text" in item: | |
| modality = "text" | |
| content = item["text"] | |
| else: | |
| modality = "text" | |
| content = str(item) | |
| else: | |
| modality = "multimodal" | |
| content = item | |
| seed_offsets = {"text": 101, "vision": 202, "video": 303, "audio": 404} | |
| seed_offset = seed_offsets.get(modality, 505) | |
| h = self._hash_item(content, seed_offset) | |
| np.random.seed(h) | |
| vec = np.random.randn(self.embedding_dim).astype(np.float32) | |
| q_vec = np.where(vec > 0.43, 1.0, np.where(vec < -0.43, -1.0, 0.0)).astype(np.float32) | |
| if normalize: | |
| norm = np.linalg.norm(q_vec) | |
| q_vec /= max(norm, 1e-9) | |
| return q_vec | |
| def encode(self, inputs: Union[str, Dict[str, Any], List[Union[str, Dict[str, Any]]]], normalize: bool = True, **kwargs) -> np.ndarray: | |
| """ | |
| Unified omni-modal encoding adhering to Google's SentenceTransformers API: | |
| - model.encode("What causes the northern lights?") | |
| - model.encode({"image": "sunset_beach.jpg"}) | |
| - model.encode({"video": "sample_clip.mp4"}) | |
| - model.encode({"audio": "ocean_waves.wav"}) | |
| - model.encode([{"image": ...}, {"audio": ...}, "text query"]) | |
| """ | |
| if isinstance(inputs, (str, dict)) or not isinstance(inputs, list): | |
| inputs = [inputs] | |
| if not inputs: | |
| return np.empty((0, self.embedding_dim), dtype=np.float32) | |
| embeddings = [self._encode_single(item, normalize=normalize) for item in inputs] | |
| return np.vstack(embeddings) | |