Feature Extraction
Transformers
Safetensors
English
slip
text-generation
time-series
sensor
multimodal
contrastive-learning
llama
custom_code
Instructions to use leochen085/SLIP-Llama with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use leochen085/SLIP-Llama with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="leochen085/SLIP-Llama", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("leochen085/SLIP-Llama", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download multimodal_gemma.py from leochen085/SLIP-Llama: direct link, hf CLI and curl.
- Browser
- Download file 6.36 kB
-
https://huggingface.co/leochen085/SLIP-Llama/resolve/main/multimodal_gemma.py
- Command line
-
hf download hf://leochen085/SLIP-Llama/multimodal_gemma.py
-
curl -L -o multimodal_gemma.py https://huggingface.co/leochen085/SLIP-Llama/resolve/main/multimodal_gemma.py
6.36 kB
| """Gemma3 decoder with SLIP cross-attention blocks at layers >= split_layer. | |
| Refactored from HealthSensorSLM-Bench/model_factory/multimodal_gemma.py. The | |
| key change is how sensor context reaches the cross-attention blocks: instead | |
| of caching ``self.vis_x`` as module state (which is unsafe when the same | |
| decoder is called multiple times per step with different micro-batch shapes, | |
| as CODI does), each call threads ``vis_x`` through a ``contextvars.ContextVar`` | |
| scoped by a context manager. Wrapped decoder layers read the ContextVar on | |
| every forward — so the context is always fresh and batch-size-correct. | |
| The ``.to(torch.bfloat16)`` hard-cast from the original is also removed; dtype | |
| is taken from the surrounding model so autocast / fp32 runs work unchanged. | |
| """ | |
| import os | |
| from contextlib import contextmanager | |
| from contextvars import ContextVar | |
| from typing import Optional | |
| import torch | |
| import torch.nn as nn | |
| from transformers import AutoConfig, AutoModelForCausalLM | |
| from .ts_transformer import CrossAttention | |
| _SLIP_VIS_X: "ContextVar[Optional[torch.Tensor]]" = ContextVar("slip_vis_x", default=None) | |
| def slip_vis_x_ctx(vis_x: Optional[torch.Tensor]): | |
| """Bind ``vis_x`` as the cross-attention context for the enclosed forward. | |
| Safe to nest; the outer binding is restored on exit. Reset on exceptions. | |
| """ | |
| token = _SLIP_VIS_X.set(vis_x) | |
| try: | |
| yield | |
| finally: | |
| _SLIP_VIS_X.reset(token) | |
| class Residual(nn.Module): | |
| def __init__(self, fn): | |
| super().__init__() | |
| self.fn = fn | |
| def forward(self, x, *args, **kwargs): | |
| return self.fn(x, *args, **kwargs) + x | |
| class Gemma3MultimodalLayer(nn.Module): | |
| """Wraps a Gemma3 decoder layer with a post-hoc cross-attention block. | |
| Reads the current sensor context from the module-level ``_SLIP_VIS_X`` | |
| ContextVar rather than from ``self``, so the context stays accurate across | |
| consecutive forwards with changing batch shapes. | |
| """ | |
| _xattn_debug_calls: int = 0 | |
| _XATTN_DEBUG_MAX_CALLS: int = 60 | |
| def __init__(self, original_layer, cross_attn_block): | |
| super().__init__() | |
| self.original_layer = original_layer | |
| self.cross_attn_block = cross_attn_block | |
| def __getattr__(self, name): | |
| try: | |
| return super().__getattr__(name) | |
| except AttributeError: | |
| try: | |
| original_layer = super().__getattr__("original_layer") | |
| except AttributeError: | |
| raise AttributeError(name) | |
| return getattr(original_layer, name) | |
| def forward(self, hidden_states, **kwargs): | |
| vis_x = _SLIP_VIS_X.get() | |
| assert vis_x is not None, ( | |
| "SLIP cross-attention invoked without a sensor context. " | |
| "Wrap the model forward in `slip_vis_x_ctx(sensor_hidden)` first." | |
| ) | |
| outputs = self.original_layer(hidden_states, **kwargs) | |
| if isinstance(outputs, tuple): | |
| hidden_states = outputs[0] | |
| else: | |
| hidden_states = outputs | |
| vis_x_cast = vis_x.to(dtype=hidden_states.dtype, device=hidden_states.device) | |
| cls = Gemma3MultimodalLayer | |
| _debug = ( | |
| os.environ.get("TLR_TS_DEBUG") == "1" | |
| and cls._xattn_debug_calls < cls._XATTN_DEBUG_MAX_CALLS | |
| ) | |
| if _debug: | |
| pre = hidden_states | |
| post = self.cross_attn_block(pre, context=vis_x_cast) | |
| pre_f = pre.detach().float() | |
| post_f = post.detach().float() | |
| base = pre_f.norm().clamp_min(1e-6) | |
| delta = (post_f - pre_f).norm() | |
| print( | |
| f"[XATTN] call={cls._xattn_debug_calls} " | |
| f"vis_x_norm={vis_x_cast.detach().float().norm().item():.3f} " | |
| f"pre_norm={pre_f.norm().item():.3f} " | |
| f"post_norm={post_f.norm().item():.3f} " | |
| f"delta_over_pre={(delta / base).item():.4e}", | |
| flush=True, | |
| ) | |
| cls._xattn_debug_calls += 1 | |
| hidden_states = post | |
| else: | |
| hidden_states = self.cross_attn_block(hidden_states, context=vis_x_cast) | |
| if isinstance(outputs, tuple): | |
| return (hidden_states,) + outputs[1:] | |
| return hidden_states | |
| class Gemma3MultimodalModel(nn.Module): | |
| """Standard HF Gemma3 with SLIP cross-attention inserted at/after ``split_layer``.""" | |
| def __init__( | |
| self, | |
| model_id: str = "google/gemma-3-270m", | |
| post_train: bool = False, | |
| split_layer: int = 12, | |
| num_heads: Optional[int] = None, | |
| dtype: Optional[torch.dtype] = None, | |
| ): | |
| super().__init__() | |
| load_kwargs = dict(attn_implementation="sdpa", trust_remote_code=True) | |
| if dtype is not None: | |
| load_kwargs["dtype"] = dtype | |
| # post_train=False is the right default when loading SLIP via | |
| # SlipForCausalLM.from_pretrained(...): the SLIP safetensors contains the | |
| # full post-trained Gemma weights already, so downloading the vanilla | |
| # google/gemma-3-270m checkpoint just to overwrite it is wasted work (and | |
| # produces a misleading "lm_head.weight newly initialized" warning — | |
| # Gemma-3 ties lm_head to embed_tokens, so it's never in the checkpoint). | |
| # Flip to True only when you want a randomly-initialised cross-attention | |
| # head on top of actually-pretrained Gemma weights, not overwritten by an | |
| # outer load. | |
| if post_train: | |
| self.model = AutoModelForCausalLM.from_pretrained(model_id, **load_kwargs) | |
| else: | |
| config = AutoConfig.from_pretrained(model_id, trust_remote_code=True) | |
| self.model = AutoModelForCausalLM.from_config(config, **load_kwargs) | |
| self.split_layer = split_layer | |
| hidden_size = self.model.config.hidden_size | |
| self.hidden_size = hidden_size | |
| if num_heads is None: | |
| num_heads = self.model.config.num_attention_heads | |
| for i in range(split_layer, len(self.model.model.layers)): | |
| cross_attn = CrossAttention( | |
| dim=hidden_size, context_dim=hidden_size, num_heads=num_heads, dropout_rate=0.1 | |
| ) | |
| original_layer = self.model.model.layers[i] | |
| self.model.model.layers[i] = Gemma3MultimodalLayer(original_layer, Residual(cross_attn)) | |