| """HF remote-code modeling for the fusion-embedding family (AutoModel + trust_remote_code). |
| |
| One embedding space for text, images, and audio. The checkpoint on this repository holds |
| ONLY the trained components (perceiver-resampler connector, diagonal text whitening, |
| logit scale and — generation 2 — the modality-gated deep adapters); the frozen |
| Qwen3-VL-Embedding-2B base and the frozen Qwen2.5-Omni audio tower are downloaded from |
| their own repositories on first use and are byte-identical to their releases. |
| |
| from transformers import AutoModel |
| model = AutoModel.from_pretrained( |
| "EximiusLabs/fusion-embedding-1-2b-preview", trust_remote_code=True) |
| t = model.embed_text("a dog barks in the distance") |
| a = model.embed_audio("dog.wav") |
| i = model.embed_image("dog.jpg") |
| |
| The embed_* methods reproduce the repository's reference ``inference.py`` exactly (same |
| chat templates, truncation, pooling, whitening, Matryoshka truncation and normalization); |
| outputs are bitwise-identical to that loader on the same hardware. Non-audio inputs never |
| execute the generation-2 adapter branch (the gate returns the frozen layers' output |
| untouched), so text/image outputs are bit-for-bit those of generation 1 and of the base's |
| computation path. |
| |
| Requires: transformers>=4.46 (with the Qwen2.5-Omni model classes), torchvision, pillow, |
| soundfile, librosa. A CUDA GPU is recommended (~14 GB at bf16). |
| """ |
|
|
| from __future__ import annotations |
|
|
| import math |
| import os |
| from typing import Optional, Union |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from transformers import PreTrainedModel |
|
|
| from .configuration_fusion_embedding import FusionEmbeddingConfig |
|
|
| DEFAULT_QUERY_INSTRUCTION = "Retrieve images or text relevant to the user's query." |
| DOC_INSTRUCTION = "Represent the user's input." |
|
|
| _ACTS = {"silu": nn.SiLU, "gelu": nn.GELU, "relu": nn.ReLU} |
|
|
|
|
| |
| |
| |
| def _chat(instruction: str, user_content: str) -> str: |
| """The base's official embedding format: system-turn instruction, assistant opener.""" |
| return (f"<|im_start|>system\n{instruction}<|im_end|>\n" |
| f"<|im_start|>user\n{user_content}<|im_end|>\n" |
| f"<|im_start|>assistant\n") |
|
|
|
|
| def sinusoidal_positions(length: int, dim: int, device, dtype) -> torch.Tensor: |
| if dim % 2 != 0: |
| pe = sinusoidal_positions(length, dim + 1, device, dtype) |
| return pe[:, :dim] |
| pos = torch.arange(length, device=device, dtype=torch.float32).unsqueeze(1) |
| div = torch.exp(torch.arange(0, dim, 2, device=device, dtype=torch.float32) |
| * (-math.log(10000.0) / dim)) |
| pe = torch.zeros(length, dim, device=device, dtype=torch.float32) |
| pe[:, 0::2] = torch.sin(pos * div) |
| pe[:, 1::2] = torch.cos(pos * div) |
| return pe.to(dtype) |
|
|
|
|
| def last_token_pool(hidden: torch.Tensor, attention_mask: torch.Tensor) -> torch.Tensor: |
| lengths = attention_mask.long().sum(dim=1) - 1 |
| lengths = lengths.clamp(min=0) |
| idx = lengths.view(-1, 1, 1).expand(-1, 1, hidden.size(-1)) |
| return hidden.gather(1, idx).squeeze(1) |
|
|
|
|
| def mrl_truncate_normalize(x: torch.Tensor, dim: int) -> torch.Tensor: |
| return F.normalize(x[..., :dim], p=2, dim=-1) |
|
|
|
|
| class TextWhitening(nn.Module): |
| """Diagonal (per-dim, MRL-safe) standardization of frozen text embeddings.""" |
|
|
| def __init__(self, dim: int): |
| super().__init__() |
| self.register_buffer("mean", torch.zeros(dim)) |
| self.register_buffer("std", torch.ones(dim)) |
| self.register_buffer("fitted", torch.zeros((), dtype=torch.uint8)) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| if int(self.fitted) == 0: |
| return x |
| mean = self.mean.to(device=x.device, dtype=x.dtype) |
| std = self.std.to(device=x.device, dtype=x.dtype) |
| return (x - mean) / std |
|
|
|
|
| class _ResamplerBlock(nn.Module): |
| """Pre-norm: latent self-attention -> cross-attention -> FFN.""" |
|
|
| def __init__(self, dim: int, heads: int, ffn_mult: int, dropout: float): |
| super().__init__() |
| self.norm_sa = nn.LayerNorm(dim) |
| self.self_attn = nn.MultiheadAttention(dim, heads, dropout=dropout, batch_first=True) |
| self.norm_q = nn.LayerNorm(dim) |
| self.norm_kv = nn.LayerNorm(dim) |
| self.cross_attn = nn.MultiheadAttention(dim, heads, dropout=dropout, batch_first=True) |
| self.norm_ff = nn.LayerNorm(dim) |
| self.ffn = nn.Sequential( |
| nn.Linear(dim, dim * ffn_mult), |
| nn.GELU(), |
| nn.Dropout(dropout), |
| nn.Linear(dim * ffn_mult, dim), |
| ) |
|
|
| def forward(self, q, kv, key_padding_mask): |
| h = self.norm_sa(q) |
| q = q + self.self_attn(h, h, h, need_weights=False)[0] |
| h = self.norm_q(q) |
| kv_n = self.norm_kv(kv) |
| q = q + self.cross_attn(h, kv_n, kv_n, key_padding_mask=key_padding_mask, |
| need_weights=False)[0] |
| q = q + self.ffn(self.norm_ff(q)) |
| return q |
|
|
|
|
| class FusionResampler(nn.Module): |
| """Perceiver-resampler: variable-length audio frames -> N fixed latent tokens.""" |
|
|
| def __init__(self, cfg: FusionEmbeddingConfig): |
| super().__init__() |
| dr = cfg.d_resampler |
| self.in_proj = nn.Linear(cfg.d_audio, dr) |
| self.queries = nn.Parameter(torch.empty(cfg.n_query, dr)) |
| nn.init.normal_(self.queries, std=0.02) |
| self.blocks = nn.ModuleList( |
| _ResamplerBlock(dr, cfg.resampler_heads, cfg.resampler_ffn_mult, |
| cfg.resampler_dropout) |
| for _ in range(cfg.resampler_depth) |
| ) |
| self.out_proj = nn.Linear(dr, cfg.d_llm) |
| self.out_norm = nn.LayerNorm(cfg.d_llm) |
|
|
| def forward(self, frames: torch.Tensor, frame_mask: Optional[torch.Tensor] = None): |
| B, T, _ = frames.shape |
| if frame_mask is None: |
| frame_mask = torch.ones(B, T, dtype=torch.bool, device=frames.device) |
| kv = self.in_proj(frames) |
| kv = kv + sinusoidal_positions(T, kv.size(-1), kv.device, kv.dtype).unsqueeze(0) |
| key_padding = ~frame_mask |
| fully_masked = key_padding.all(dim=1) |
| if fully_masked.any(): |
| key_padding = key_padding.clone() |
| key_padding[fully_masked, 0] = False |
| q = self.queries.unsqueeze(0).expand(B, -1, -1) |
| for block in self.blocks: |
| q = block(q, kv, key_padding) |
| return self.out_norm(self.out_proj(q)) |
|
|
|
|
| class AdapterGate: |
| """Depth-counted on/off switch shared by every adapter hook (generation 2).""" |
|
|
| __slots__ = ("_depth",) |
|
|
| def __init__(self) -> None: |
| self._depth = 0 |
|
|
| @property |
| def active(self) -> bool: |
| return self._depth > 0 |
|
|
| def __enter__(self) -> "AdapterGate": |
| self._depth += 1 |
| return self |
|
|
| def __exit__(self, *exc) -> None: |
| self._depth -= 1 |
| if self._depth < 0: |
| raise RuntimeError("AdapterGate depth underflow — unbalanced enter/exit") |
|
|
|
|
| class GatedAdapter(nn.Module): |
| """Parallel bottleneck adapter: ``h + up(act(down(LN(h))))``, computed in fp32.""" |
|
|
| def __init__(self, d_model: int, rank: int, act: str = "silu"): |
| super().__init__() |
| self.norm = nn.LayerNorm(d_model) |
| self.down = nn.Linear(d_model, rank, bias=False) |
| self.act = _ACTS[act]() |
| self.up = nn.Linear(rank, d_model, bias=False) |
| nn.init.zeros_(self.up.weight) |
|
|
| def forward(self, h: torch.Tensor) -> torch.Tensor: |
| return self.up(self.act(self.down(self.norm(h.float())))).to(h.dtype) |
|
|
|
|
| def _make_hook(adapter: GatedAdapter, gate: AdapterGate): |
| def hook(_module, _inputs, output): |
| if not gate.active: |
| return None |
| if isinstance(output, tuple): |
| h = output[0] |
| return (h + adapter(h),) + tuple(output[1:]) |
| return output + adapter(output) |
| return hook |
|
|
|
|
| class OmniAudioAdapter(nn.Module): |
| """Frozen Qwen2.5-Omni audio encoder -> (frames [B,T,d_audio], frame_mask [B,T]).""" |
|
|
| def __init__(self, encoder: nn.Module, d_audio: int): |
| super().__init__() |
| self.encoder = encoder |
| self.d_audio = d_audio |
|
|
| @torch.no_grad() |
| def forward(self, mel: torch.Tensor, mel_mask: Optional[torch.Tensor] = None): |
| B, n_mels, Fdim = mel.shape |
| if mel_mask is None: |
| feat_lens = torch.full((B,), Fdim, dtype=torch.long, device=mel.device) |
| else: |
| feat_lens = mel_mask.long().sum(dim=1) |
| dtype = next(self.encoder.parameters()).dtype |
| per_item = [] |
| for i in range(B): |
| Li = max(int(feat_lens[i].item()), 1) |
| feats = mel[i, :, :Li].to(dtype) |
| out = self.encoder(input_features=feats, |
| feature_lens=torch.tensor([Li], device=mel.device)) |
| frames = out.last_hidden_state |
| if frames.dim() == 3: |
| frames = frames[0] |
| per_item.append(frames.float()) |
| T_max = max(f.shape[0] for f in per_item) |
| frames_out = mel.new_zeros(B, T_max, self.d_audio) |
| frame_mask = torch.zeros(B, T_max, dtype=torch.bool, device=mel.device) |
| for i, f in enumerate(per_item): |
| frames_out[i, : f.shape[0]] = f |
| frame_mask[i, : f.shape[0]] = True |
| return frames_out, frame_mask |
|
|
|
|
| |
| |
| |
| class FusionEmbeddingModel(PreTrainedModel): |
| """fusion-embedding for transformers AutoModel (trust_remote_code). |
| |
| The registered submodules are exactly the trained components shipped in this |
| repository's ``model.safetensors`` (resampler + text whitening + logit scale |
| + generation-2 adapters). The frozen base and audio tower load lazily from |
| their own repositories on the first ``embed_*`` call, onto the device the |
| trained components are on at that moment — call ``.to("cuda")`` (or pass |
| ``device_map``) before embedding. |
| """ |
|
|
| config_class = FusionEmbeddingConfig |
| base_model_prefix = "fusion_embedding" |
| main_input_name = "input_ids" |
| _supports_flash_attn_2 = False |
|
|
| def __init__(self, config: FusionEmbeddingConfig): |
| super().__init__(config) |
| self.resampler = FusionResampler(config) |
| self.text_whitening = TextWhitening(config.d_llm) |
| self.logit_scale = nn.Parameter(torch.zeros(1)) |
| self.audio_adapters: Optional[nn.ModuleList] = None |
| if config.adapter_rank and config.adapter_rank > 0: |
| self.audio_adapters = nn.ModuleList( |
| GatedAdapter(config.d_llm, config.adapter_rank, config.adapter_act) |
| for _ in range(config.n_decoder_layers) |
| ) |
| |
| self._rt: dict = {} |
| self.post_init() |
|
|
| def _init_weights(self, module): |
| pass |
|
|
| |
| @property |
| def _device(self) -> torch.device: |
| return self.resampler.out_proj.weight.device |
|
|
| def _ensure_backbones(self) -> None: |
| if "full" in self._rt: |
| return |
| from transformers import (AutoConfig, AutoFeatureExtractor, AutoModel, |
| AutoProcessor) |
|
|
| device, dtype = self._device, torch.bfloat16 |
| full = AutoModel.from_pretrained(self.config.base_model, trust_remote_code=True, |
| dtype=dtype) |
| full = full.to(device).eval() |
| for p in full.parameters(): |
| p.requires_grad_(False) |
| proc = AutoProcessor.from_pretrained(self.config.base_model, trust_remote_code=True) |
|
|
| acfg = AutoConfig.from_pretrained(self.config.audio_model, trust_remote_code=True) |
| audio_cfg = acfg.thinker_config.audio_config |
| tower = self._load_audio_encoder(audio_cfg, dtype).to(device) |
| fe_audio = AutoFeatureExtractor.from_pretrained(self.config.audio_model, |
| trust_remote_code=True) |
|
|
| self._rt.update(full=full, proc=proc, tok=proc.tokenizer, |
| tower=OmniAudioAdapter(tower, self.config.d_audio), |
| fe_audio=fe_audio, gate=AdapterGate(), adapter_handles=[]) |
|
|
| if self.audio_adapters is not None: |
| layers = self._find_decoder_layers(full.language_model) |
| if len(layers) != len(self.audio_adapters): |
| raise RuntimeError( |
| f"decoder has {len(layers)} layers but the checkpoint carries " |
| f"{len(self.audio_adapters)} adapters") |
| gate = self._rt["gate"] |
| self._rt["adapter_handles"] = [ |
| layer.register_forward_hook(_make_hook(ad, gate)) |
| for layer, ad in zip(layers, self.audio_adapters) |
| ] |
|
|
| def _load_audio_encoder(self, audio_cfg, dtype): |
| """Instantiate the Omni audio encoder and load only ``thinker.audio_tower.*``.""" |
| import glob |
|
|
| from huggingface_hub import snapshot_download |
| from safetensors.torch import load_file |
| from transformers.models.qwen2_5_omni import modeling_qwen2_5_omni as mod |
|
|
| snap = snapshot_download(self.config.audio_model, |
| allow_patterns=["*.safetensors", "*.json"]) |
| encoder = mod.Qwen2_5OmniAudioEncoder(audio_cfg) |
| prefix = "thinker.audio_tower." |
| collected = {} |
| for shard in sorted(glob.glob(os.path.join(snap, "*.safetensors"))): |
| for k, v in load_file(shard).items(): |
| if k.startswith(prefix): |
| collected[k[len(prefix):]] = v |
| encoder.load_state_dict(collected, strict=False) |
| return encoder.to(dtype).eval() |
|
|
| @staticmethod |
| def _find_decoder_layers(base_lm: nn.Module) -> nn.ModuleList: |
| best = None |
| for name, mod_ in base_lm.named_modules(): |
| if isinstance(mod_, nn.ModuleList) and name.rsplit(".", 1)[-1] == "layers": |
| if best is None or len(mod_) > len(best): |
| best = mod_ |
| if best is None or len(best) == 0: |
| raise ValueError("no decoder ModuleList named 'layers' found in the base") |
| return best |
|
|
| |
| def _finish(self, pooled: torch.Tensor, dim: Optional[int]) -> torch.Tensor: |
| dim = dim or self.config.mrl_default |
| return mrl_truncate_normalize(pooled.float(), dim).squeeze(0).cpu() |
|
|
| def _encode_text_ids(self, ids_t: torch.Tensor) -> torch.Tensor: |
| full = self._rt["full"] |
| embeds = full.get_input_embeddings()(ids_t) |
| out = full.language_model(inputs_embeds=embeds, |
| attention_mask=torch.ones_like(ids_t)) |
| hidden = out.last_hidden_state if hasattr(out, "last_hidden_state") else out[0] |
| return last_token_pool(hidden, torch.ones_like(ids_t)) |
|
|
| |
| @torch.no_grad() |
| def embed_text(self, text: str, instruction: str = DEFAULT_QUERY_INSTRUCTION, |
| dim: Optional[int] = None) -> torch.Tensor: |
| self._ensure_backbones() |
| if self._rt["gate"].active: |
| raise RuntimeError("adapter gate is open during a text encode — " |
| "non-audio inputs must run with the gate closed") |
| ids = self._rt["tok"].encode(_chat(instruction, text), |
| add_special_tokens=False)[: self.config.max_text_tokens] |
| ids_t = torch.tensor([ids], device=self._device) |
| pooled = self._encode_text_ids(ids_t) |
| return self._finish(self.text_whitening(pooled), dim) |
|
|
| @torch.no_grad() |
| def embed_audio(self, audio: Union[str, "object"], sr: Optional[int] = None, |
| dim: Optional[int] = None) -> torch.Tensor: |
| import librosa |
| import soundfile as sf |
|
|
| self._ensure_backbones() |
| if isinstance(audio, (str, os.PathLike)): |
| wav, sr = sf.read(str(audio), dtype="float32") |
| else: |
| wav = audio |
| assert sr is not None, "pass sr= when embedding a raw array" |
| if getattr(wav, "ndim", 1) > 1: |
| wav = wav.mean(axis=1) |
| fe_audio = self._rt["fe_audio"] |
| target_sr = fe_audio.sampling_rate |
| if sr != target_sr: |
| wav = librosa.resample(wav, orig_sr=sr, target_sr=target_sr) |
| feats = fe_audio(wav, sampling_rate=target_sr, return_tensors="pt", |
| return_attention_mask=True, padding="max_length", truncation=True) |
| mel = feats["input_features"][0] |
| am = feats.get("attention_mask") |
| if am is not None: |
| mel = mel[:, : int(am[0].sum().item())] |
| frames, frame_mask = self._rt["tower"]( |
| mel.unsqueeze(0).to(self._device), |
| torch.ones(1, mel.shape[1], dtype=torch.bool, device=self._device)) |
| audio_tok = self.resampler(frames, frame_mask) |
| cfg = self.config |
| ids = torch.tensor([[cfg.audio_pad_id] * cfg.n_query + [cfg.eos_id]], |
| device=self._device) |
| attention_mask = torch.ones_like(ids) |
| full = self._rt["full"] |
| embeds = full.get_input_embeddings()(ids).clone() |
| embeds[ids == cfg.audio_pad_id] = ( |
| audio_tok.reshape(-1, audio_tok.size(-1)).to(embeds.dtype)) |
| with self._rt["gate"]: |
| out = full.language_model(inputs_embeds=embeds, attention_mask=attention_mask) |
| hidden = out.last_hidden_state if hasattr(out, "last_hidden_state") else out[0] |
| pooled = last_token_pool(hidden, attention_mask) |
| return self._finish(pooled, dim) |
|
|
| @torch.no_grad() |
| def embed_image(self, image, dim: Optional[int] = None) -> torch.Tensor: |
| from PIL import Image |
|
|
| self._ensure_backbones() |
| if self._rt["gate"].active: |
| |
| |
| |
| raise RuntimeError("adapter gate is open during an image embed — " |
| "non-audio inputs must run with the gate closed") |
| if isinstance(image, (str, os.PathLike)): |
| image = Image.open(str(image)) |
| image = image.convert("RGB") |
| text = _chat(DOC_INSTRUCTION, "<|vision_start|><|image_pad|><|vision_end|>") |
| inputs = self._rt["proc"](text=[text], images=[image], |
| return_tensors="pt").to(self._device) |
| h = self._rt["full"](**inputs).last_hidden_state |
| pooled = last_token_pool(h, inputs["attention_mask"]) |
| return self._finish(pooled, dim) |
|
|
| |
| @torch.no_grad() |
| def embed_text_batch(self, texts, instruction: str = DEFAULT_QUERY_INSTRUCTION, |
| dim: Optional[int] = None, |
| max_tokens: Optional[int] = None) -> torch.Tensor: |
| """Batch text embedding [B, dim] (right-padded, mask-aware last-token pooling).""" |
| self._ensure_backbones() |
| if self._rt["gate"].active: |
| raise RuntimeError("adapter gate is open during a text encode — " |
| "non-audio inputs must run with the gate closed") |
| cfg, tok = self.config, self._rt["tok"] |
| max_tokens = max_tokens or cfg.max_text_tokens |
| seqs = [tok.encode(_chat(instruction, t), add_special_tokens=False)[:max_tokens] |
| for t in texts] |
| L = max(len(s) for s in seqs) |
| ids = torch.full((len(seqs), L), cfg.pad_id, dtype=torch.long, device=self._device) |
| mask = torch.zeros(len(seqs), L, dtype=torch.long, device=self._device) |
| for b, s in enumerate(seqs): |
| ids[b, : len(s)] = torch.tensor(s, device=self._device) |
| mask[b, : len(s)] = 1 |
| full = self._rt["full"] |
| out = full.language_model(inputs_embeds=full.get_input_embeddings()(ids), |
| attention_mask=mask) |
| hidden = out.last_hidden_state if hasattr(out, "last_hidden_state") else out[0] |
| pooled = self.text_whitening(last_token_pool(hidden, mask)) |
| return mrl_truncate_normalize(pooled.float(), dim or cfg.mrl_default).cpu() |
|
|
| @torch.no_grad() |
| def embed_audio_batch(self, wavs, sr: int, dim: Optional[int] = None) -> torch.Tensor: |
| """Batch audio embedding [B, dim] from raw waveform arrays at a common rate.""" |
| import librosa |
| import numpy as np |
|
|
| self._ensure_backbones() |
| cfg, fe_audio = self.config, self._rt["fe_audio"] |
| target_sr = fe_audio.sampling_rate |
| prepped = [] |
| for wav in wavs: |
| wav = np.asarray(wav, dtype=np.float32) |
| if wav.ndim > 1: |
| wav = wav.mean(axis=-1) |
| if sr != target_sr: |
| wav = librosa.resample(wav, orig_sr=sr, target_sr=target_sr) |
| prepped.append(wav) |
| feats = fe_audio(prepped, sampling_rate=target_sr, return_tensors="pt", |
| return_attention_mask=True, padding="max_length", truncation=True) |
| mel, am = feats["input_features"], feats.get("attention_mask") |
| if am is not None: |
| tmax = int(am.sum(dim=1).max().item()) |
| mel, am = mel[:, :, :tmax], am[:, :tmax] |
| fmask = (am.bool() if am is not None |
| else torch.ones(mel.shape[0], mel.shape[2], dtype=torch.bool)) |
| frames, frame_mask = self._rt["tower"](mel.to(self._device), |
| fmask.to(self._device)) |
| audio_tok = self.resampler(frames, frame_mask) |
| ids = torch.tensor([[cfg.audio_pad_id] * cfg.n_query + [cfg.eos_id]] * mel.shape[0], |
| device=self._device) |
| attention_mask = torch.ones_like(ids) |
| full = self._rt["full"] |
| embeds = full.get_input_embeddings()(ids).clone() |
| embeds[ids == cfg.audio_pad_id] = ( |
| audio_tok.reshape(-1, audio_tok.size(-1)).to(embeds.dtype)) |
| with self._rt["gate"]: |
| out = full.language_model(inputs_embeds=embeds, attention_mask=attention_mask) |
| hidden = out.last_hidden_state if hasattr(out, "last_hidden_state") else out[0] |
| pooled = last_token_pool(hidden, attention_mask) |
| return mrl_truncate_normalize(pooled.float(), dim or cfg.mrl_default).cpu() |
|
|
| |
| @staticmethod |
| def center(embs: torch.Tensor) -> torch.Tensor: |
| """Per-modality mean-centering + renormalization (cross-modal ranking readout).""" |
| c = embs - embs.mean(dim=0, keepdim=True) |
| return F.normalize(c, dim=-1) |
|
|
| def forward(self, *args, **kwargs): |
| raise NotImplementedError( |
| "fusion-embedding is an embedding model: use embed_text(str), " |
| "embed_audio(path_or_array, sr=...), embed_image(path_or_PIL), and " |
| "center(embs) for cross-modal ranking.") |
|
|