Text Generation
Transformers
Safetensors
qwen3_5_text
tinycenn
cenn
language-modeling
research
conversational
Instructions to use vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32") model = AutoModelForCausalLM.from_pretrained("vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32
- SGLang
How to use vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32 with Docker Model Runner:
docker model run hf.co/vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32
Download src/tinycenn_lm/smollm2_amcenn_v2.py from vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32: direct link, hf CLI and curl.
- Browser
- Download file 14 kB
-
https://huggingface.co/vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32/resolve/a38f16360c3cb10c703d5ad743022f5421d31a1e/src/tinycenn_lm/smollm2_amcenn_v2.py
- Command line
-
hf download hf://vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32@a38f16360c3cb10c703d5ad743022f5421d31a1e/src/tinycenn_lm/smollm2_amcenn_v2.py
-
curl -L -o smollm2_amcenn_v2.py https://huggingface.co/vtava/Qwen3.5-0.8B-PDelta3-CLVR-Local32/resolve/a38f16360c3cb10c703d5ad743022f5421d31a1e/src/tinycenn_lm/smollm2_amcenn_v2.py
14 kB
| from __future__ import annotations | |
| import json | |
| import math | |
| from dataclasses import asdict, dataclass | |
| from pathlib import Path | |
| from typing import Iterable | |
| import torch | |
| from torch import Tensor, nn | |
| from .smollm2_amcenn import DEFAULT_SMOLLM2, ShardedTop2LlamaMLP, SmolAMCeNNConfig | |
| class SmolAMCeNNV2Config: | |
| feature_dim: int = 128 | |
| num_shards: int = 8 | |
| top_k: int = 2 | |
| feature_seed: int = 2026 | |
| eps: float = 1e-6 | |
| antithetic_features: bool = True | |
| learnable_feature_correction: bool = True | |
| def validate(self, model_config) -> None: | |
| if self.feature_dim < 8: | |
| raise ValueError("feature_dim must be >= 8") | |
| if self.antithetic_features and self.feature_dim % 2: | |
| raise ValueError("antithetic feature_dim must be even") | |
| if self.num_shards < 2: | |
| raise ValueError("num_shards must be >= 2") | |
| if not 1 <= self.top_k <= self.num_shards: | |
| raise ValueError("top_k must be in [1, num_shards]") | |
| if int(model_config.intermediate_size) % self.num_shards: | |
| raise ValueError("intermediate_size must divide evenly by num_shards") | |
| def to_dict(self) -> dict: | |
| return asdict(self) | |
| def from_dict(cls, data: dict) -> "SmolAMCeNNV2Config": | |
| return cls(**dict(data)) | |
| class AdaptivePositiveSoftmaxFeatures(nn.Module): | |
| """Positive softmax-kernel features with a zero-init learnable correction. | |
| The base projection is sampled from N(0,I). In v2 we optionally use | |
| antithetic pairs (omega, -omega) to reduce estimator variance while keeping | |
| Gaussian marginals. delta_projection starts at zero, so training begins from | |
| the mathematical random-feature estimator and can adapt the finite feature | |
| basis to each pretrained layer's actual Q/K distribution. | |
| """ | |
| def __init__( | |
| self, | |
| head_dim: int, | |
| feature_dim: int, | |
| seed: int, | |
| *, | |
| antithetic: bool = True, | |
| learnable_correction: bool = True, | |
| ) -> None: | |
| super().__init__() | |
| g = torch.Generator(device="cpu").manual_seed(seed) | |
| if antithetic: | |
| half = torch.randn(feature_dim // 2, head_dim, generator=g) | |
| projection = torch.cat([half, -half], dim=0) | |
| else: | |
| projection = torch.randn(feature_dim, head_dim, generator=g) | |
| self.register_buffer("base_projection", projection, persistent=True) | |
| if learnable_correction: | |
| self.delta_projection = nn.Parameter(torch.zeros_like(projection)) | |
| else: | |
| self.register_parameter("delta_projection", None) | |
| self.head_dim = int(head_dim) | |
| self.feature_dim = int(feature_dim) | |
| self.scale = head_dim ** -0.25 | |
| self.log_norm = 0.5 * math.log(float(feature_dim)) | |
| def projection(self) -> Tensor: | |
| if self.delta_projection is None: | |
| return self.base_projection | |
| return self.base_projection + self.delta_projection | |
| def forward(self, x: Tensor) -> Tensor: | |
| work = x.float() * self.scale | |
| projected = torch.einsum("...d,fd->...f", work, self.projection.float()) | |
| norm = 0.5 * work.square().sum(dim=-1, keepdim=True) | |
| log_phi = (projected - norm - self.log_norm).clamp(min=-20.0, max=20.0) | |
| return torch.exp(log_phi) | |
| class AMCeNNAttentionV2(nn.Module): | |
| """Causal finite-state approximation of softmax attention. | |
| S_t = S_{t-1} + phi(k_t) v_t^T | |
| z_t = z_{t-1} + phi(k_t) | |
| a_t = phi(q_t)^T S_t / (phi(q_t)^T z_t + eps) | |
| Unlike v1, the feature map is larger by default (m=128), variance reduced | |
| with antithetic features, and the finite basis can adapt through a zero-init | |
| delta projection. No T x T attention matrix is constructed. | |
| """ | |
| def __init__(self, original_attn: nn.Module, model_config, config: SmolAMCeNNV2Config, layer_idx: int) -> None: | |
| super().__init__() | |
| self.hidden_size = int(model_config.hidden_size) | |
| self.num_heads = int(model_config.num_attention_heads) | |
| self.num_key_value_heads = int(model_config.num_key_value_heads) | |
| self.head_dim = self.hidden_size // self.num_heads | |
| self.num_key_value_groups = self.num_heads // self.num_key_value_heads | |
| self.eps = float(config.eps) | |
| self.layer_idx = int(layer_idx) | |
| self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False) | |
| self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False) | |
| self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False) | |
| self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False) | |
| with torch.no_grad(): | |
| self.q_proj.weight.copy_(original_attn.q_proj.weight) | |
| self.k_proj.weight.copy_(original_attn.k_proj.weight) | |
| self.v_proj.weight.copy_(original_attn.v_proj.weight) | |
| self.o_proj.weight.copy_(original_attn.o_proj.weight) | |
| self.features = AdaptivePositiveSoftmaxFeatures( | |
| self.head_dim, | |
| config.feature_dim, | |
| seed=config.feature_seed + self.layer_idx, | |
| antithetic=config.antithetic_features, | |
| learnable_correction=config.learnable_feature_correction, | |
| ) | |
| self.last_state_norm = torch.tensor(0.0) | |
| def _apply_rope(self, q: Tensor, k: Tensor, position_embeddings) -> tuple[Tensor, Tensor]: | |
| if position_embeddings is None: | |
| return q, k | |
| try: | |
| from transformers.models.llama.modeling_llama import apply_rotary_pos_emb | |
| cos, sin = position_embeddings | |
| return apply_rotary_pos_emb(q, k, cos, sin) | |
| except Exception: | |
| return q, k | |
| def forward( | |
| self, | |
| hidden_states: Tensor, | |
| attention_mask=None, | |
| position_ids=None, | |
| past_key_values=None, | |
| use_cache: bool = False, | |
| cache_position=None, | |
| position_embeddings=None, | |
| **kwargs, | |
| ) -> tuple[Tensor, None]: | |
| if use_cache: | |
| raise RuntimeError("AM-CeNN v2 currently requires use_cache=False") | |
| bsz, seq_len, _ = hidden_states.shape | |
| q = self.q_proj(hidden_states).view(bsz, seq_len, self.num_heads, self.head_dim).transpose(1, 2) | |
| k = self.k_proj(hidden_states).view( | |
| bsz, seq_len, self.num_key_value_heads, self.head_dim | |
| ).transpose(1, 2) | |
| v = self.v_proj(hidden_states).view( | |
| bsz, seq_len, self.num_key_value_heads, self.head_dim | |
| ).transpose(1, 2) | |
| q, k = self._apply_rope(q, k, position_embeddings) | |
| phi_q = self.features(q).transpose(1, 2) | |
| phi_k = self.features(k).transpose(1, 2) | |
| values = v.transpose(1, 2).float() | |
| kv_write = torch.einsum("btkf,btkd->btkfd", phi_k, values) | |
| state_s = kv_write.cumsum(dim=1) | |
| state_z = phi_k.cumsum(dim=1) | |
| state_s = state_s.repeat_interleave(self.num_key_value_groups, dim=2) | |
| state_z = state_z.repeat_interleave(self.num_key_value_groups, dim=2) | |
| numerator = torch.einsum("bthf,bthfd->bthd", phi_q, state_s) | |
| denominator = torch.einsum("bthf,bthf->bth", phi_q, state_z).unsqueeze(-1) | |
| out = numerator / denominator.clamp_min(self.eps) | |
| out = out.to(dtype=hidden_states.dtype).reshape(bsz, seq_len, self.hidden_size) | |
| out = self.o_proj(out) | |
| self.last_state_norm = state_s[:, -1].detach().float().norm().cpu() | |
| return out, None | |
| def _moe_config(config: SmolAMCeNNV2Config) -> SmolAMCeNNConfig: | |
| return SmolAMCeNNConfig( | |
| feature_dim=max(8, config.feature_dim), | |
| num_shards=config.num_shards, | |
| top_k=config.top_k, | |
| feature_seed=config.feature_seed, | |
| eps=config.eps, | |
| ) | |
| def convert_all_ffns_to_sharded_top2(model: nn.Module, config: SmolAMCeNNV2Config) -> nn.Module: | |
| config.validate(model.config) | |
| moe_cfg = _moe_config(config) | |
| for layer in model.model.layers: | |
| if not isinstance(layer.mlp, ShardedTop2LlamaMLP): | |
| old = layer.mlp | |
| new = ShardedTop2LlamaMLP(old, model.config, moe_cfg) | |
| new.to(device=old.gate_proj.weight.device, dtype=old.gate_proj.weight.dtype) | |
| layer.mlp = new | |
| return model | |
| def replace_attention_layers( | |
| model: nn.Module, | |
| config: SmolAMCeNNV2Config, | |
| layer_indices: Iterable[int], | |
| ) -> nn.Module: | |
| config.validate(model.config) | |
| for idx in layer_indices: | |
| layer = model.model.layers[int(idx)] | |
| if isinstance(layer.self_attn, AMCeNNAttentionV2): | |
| continue | |
| old = layer.self_attn | |
| new = AMCeNNAttentionV2(old, model.config, config, int(idx)) | |
| new.to(device=old.q_proj.weight.device, dtype=old.q_proj.weight.dtype) | |
| layer.self_attn = new | |
| model.config.use_cache = False | |
| if hasattr(model, "generation_config"): | |
| model.generation_config.use_cache = False | |
| return model | |
| def replace_all_smollm2_attention(model: nn.Module, config: SmolAMCeNNV2Config) -> nn.Module: | |
| return replace_attention_layers(model, config, range(int(model.config.num_hidden_layers))) | |
| def freeze_for_group_calibration(model: nn.Module, layer_indices: Iterable[int]) -> list[nn.Parameter]: | |
| selected = {int(i) for i in layer_indices} | |
| for p in model.parameters(): | |
| p.requires_grad = False | |
| trainable: list[nn.Parameter] = [] | |
| for module in model.modules(): | |
| if isinstance(module, AMCeNNAttentionV2) and module.layer_idx in selected: | |
| for p in module.parameters(): | |
| p.requires_grad = True | |
| trainable.append(p) | |
| return trainable | |
| def freeze_for_global_training(model: nn.Module, *, train_router: bool = True) -> list[nn.Parameter]: | |
| for p in model.parameters(): | |
| p.requires_grad = False | |
| trainable: list[nn.Parameter] = [] | |
| for module in model.modules(): | |
| if isinstance(module, AMCeNNAttentionV2): | |
| for p in module.parameters(): | |
| p.requires_grad = True | |
| trainable.append(p) | |
| elif isinstance(module, ShardedTop2LlamaMLP) and train_router: | |
| module.router.weight.requires_grad = True | |
| module.route_mix.requires_grad = True | |
| trainable.extend([module.router.weight, module.route_mix]) | |
| return trainable | |
| def v2_parameter_summary(model: nn.Module) -> dict[str, int | float]: | |
| total = sum(p.numel() for p in model.parameters()) | |
| trainable = sum(p.numel() for p in model.parameters() if p.requires_grad) | |
| am = sum(p.numel() for m in model.modules() if isinstance(m, AMCeNNAttentionV2) for p in m.parameters()) | |
| feature_delta = sum( | |
| m.features.delta_projection.numel() | |
| for m in model.modules() | |
| if isinstance(m, AMCeNNAttentionV2) and m.features.delta_projection is not None | |
| ) | |
| routers = sum( | |
| m.router.weight.numel() + m.route_mix.numel() | |
| for m in model.modules() | |
| if isinstance(m, ShardedTop2LlamaMLP) | |
| ) | |
| return { | |
| "total": total, | |
| "trainable": trainable, | |
| "amcenn_attention": am, | |
| "feature_delta": feature_delta, | |
| "router_and_mix": routers, | |
| "trainable_percent": 100.0 * trainable / max(total, 1), | |
| } | |
| def _v2_state(model: nn.Module) -> dict[str, Tensor]: | |
| state: dict[str, Tensor] = {} | |
| for name, tensor in model.state_dict().items(): | |
| if ".self_attn." in name or ".mlp." in name: | |
| state[name] = tensor.detach().cpu() | |
| if not state: | |
| raise RuntimeError("no SmolLM2 AM-CeNN v2 state found") | |
| return state | |
| def save_smollm2_amcenn_v2( | |
| model: nn.Module, | |
| output_dir: str | Path, | |
| *, | |
| config: SmolAMCeNNV2Config, | |
| base_model: str = DEFAULT_SMOLLM2, | |
| extra_metadata: dict | None = None, | |
| ) -> Path: | |
| output_dir = Path(output_dir) | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| torch.save(_v2_state(model), output_dir / "smollm2_amcenn_v2.pt") | |
| meta = { | |
| "format_version": 2, | |
| "architecture": "smollm2-amcenn-top2-v2", | |
| "base_model": base_model, | |
| "amcenn_v2": config.to_dict(), | |
| } | |
| if extra_metadata: | |
| meta["training"] = extra_metadata | |
| (output_dir / "smollm2_amcenn_v2_config.json").write_text( | |
| json.dumps(meta, indent=2), encoding="utf-8" | |
| ) | |
| return output_dir | |
| def load_smollm2_amcenn_v2_weights(model: nn.Module, student_dir: str | Path) -> nn.Module: | |
| state = torch.load(Path(student_dir) / "smollm2_amcenn_v2.pt", map_location="cpu", weights_only=True) | |
| incompatible = model.load_state_dict(state, strict=False) | |
| expected = set(_v2_state(model)) | |
| missing = [k for k in incompatible.missing_keys if k in expected] | |
| unexpected = [k for k in incompatible.unexpected_keys if k not in expected] | |
| if missing: | |
| raise RuntimeError(f"missing AM-CeNN v2 keys: {missing[:8]}") | |
| if unexpected: | |
| raise RuntimeError(f"unexpected AM-CeNN v2 keys: {unexpected[:8]}") | |
| return model | |
| def build_smollm2_amcenn_v2(student_dir: str | Path, *, device=None, dtype=None): | |
| from transformers import AutoModelForCausalLM | |
| student_dir = Path(student_dir) | |
| meta = json.loads((student_dir / "smollm2_amcenn_v2_config.json").read_text()) | |
| if meta.get("architecture") != "smollm2-amcenn-top2-v2": | |
| raise ValueError("checkpoint is not SmolLM2 AM-CeNN Top-2 v2") | |
| kwargs = {} | |
| if dtype is not None: | |
| kwargs["dtype"] = dtype | |
| model = AutoModelForCausalLM.from_pretrained(meta["base_model"], **kwargs) | |
| config = SmolAMCeNNV2Config.from_dict(meta["amcenn_v2"]) | |
| convert_all_ffns_to_sharded_top2(model, config) | |
| replace_all_smollm2_attention(model, config) | |
| load_smollm2_amcenn_v2_weights(model, student_dir) | |
| if device is not None: | |
| model.to(device) | |
| if dtype is not None: | |
| model.to(dtype=dtype) | |
| model.config.use_cache = False | |
| if hasattr(model, "generation_config"): | |
| model.generation_config.use_cache = False | |
| return model | |