| |
| """ |
| Onyx 7B Dense Model, Marvin Tutt, Caia Tech |
| """ |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from dataclasses import dataclass |
| from typing import Optional, Tuple, Dict, Any, List |
| import math |
| import warnings |
|
|
| |
| try: |
| from flash_attn import flash_attn_func |
| FLASH_AVAILABLE = True |
| except ImportError: |
| FLASH_AVAILABLE = False |
| warnings.warn("FlashAttention not available, using PyTorch SDPA", stacklevel=2) |
|
|
|
|
| @dataclass |
| class OnyxConfig: |
| """Configuration for Onyx 7B Dense Model""" |
| |
| vocab_size: int = 128256 |
| d_model: int = 4096 |
| n_layers: int = 32 |
| n_heads: int = 32 |
| n_kv_heads: int = 8 |
| d_ff: int = 11008 |
| max_seq_len: int = 16384 |
| |
| eos_token_id: int = 2 |
| |
| |
| rope_theta: float = 500000.0 |
| rope_scaling: Optional[Dict] = None |
| |
| |
| use_swiglu: bool = True |
| use_rms_norm: bool = True |
| norm_eps: float = 1e-5 |
| use_qk_norm: bool = False |
| |
| |
| use_flash_attn: bool = True |
| use_cuda_graphs: bool = False |
| use_torch_compile: bool = True |
| |
| |
| dropout: float = 0.0 |
| attention_dropout: float = 0.0 |
| gradient_checkpointing: bool = False |
| |
| |
| tie_embeddings: bool = True |
| |
| def __post_init__(self): |
| if self.n_heads % self.n_kv_heads != 0: |
| raise ValueError(f"n_heads ({self.n_heads}) must be divisible by n_kv_heads ({self.n_kv_heads})") |
| if self.d_model % self.n_heads != 0: |
| raise ValueError(f"d_model ({self.d_model}) must be divisible by n_heads ({self.n_heads})") |
| |
| head_dim = self.d_model // self.n_heads |
| if head_dim % 2 != 0: |
| raise ValueError(f"head_dim ({head_dim}) must be even for RoPE") |
|
|
|
|
| class RMSNorm(nn.Module): |
| """RMSNorm normalization layer""" |
| |
| def __init__(self, dim: int, eps: float = 1e-6): |
| super().__init__() |
| self.eps = eps |
| self.weight = nn.Parameter(torch.ones(dim)) |
| |
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| variance = x.pow(2).mean(-1, keepdim=True) |
| x = x * torch.rsqrt(variance + self.eps) |
| return x * self.weight |
|
|
|
|
| class RoPE(nn.Module): |
| """Rotary Position Embeddings with correct even/odd implementation""" |
| |
| def __init__(self, config: OnyxConfig): |
| super().__init__() |
| self.max_seq_len = config.max_seq_len |
| self.d_model = config.d_model |
| self.n_heads = config.n_heads |
| self.theta = config.rope_theta |
| self.head_dim = self.d_model // self.n_heads |
| |
| |
| self.register_buffer( |
| "freqs", |
| self._compute_freqs(), |
| persistent=False |
| ) |
| |
| def _compute_freqs(self) -> torch.Tensor: |
| |
| freqs = 1.0 / (self.theta ** (torch.arange(0, self.head_dim, 2).float() / self.head_dim)) |
| return freqs |
| |
| def forward( |
| self, |
| q: torch.Tensor, |
| k: torch.Tensor, |
| seq_len: int, |
| offset: int = 0 |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| |
| t = torch.arange(offset, offset + seq_len, device=q.device, dtype=self.freqs.dtype) |
| freqs = torch.outer(t, self.freqs) |
| |
| |
| cos = torch.cos(freqs) |
| sin = torch.sin(freqs) |
| |
| |
| q_embed = self._apply_rotary(q, cos, sin) |
| k_embed = self._apply_rotary(k, cos, sin) |
| |
| return q_embed, k_embed |
| |
| def _apply_rotary( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor |
| ) -> torch.Tensor: |
| """Apply rotary embeddings with correct even/odd pairing""" |
| |
| d = x.size(-1) |
| assert d % 2 == 0, "head_dim must be even for RoPE" |
| |
| |
| cos = cos.to(x.dtype).unsqueeze(0).unsqueeze(2) |
| sin = sin.to(x.dtype).unsqueeze(0).unsqueeze(2) |
| |
| |
| x_even = x[..., ::2] |
| x_odd = x[..., 1::2] |
| |
| |
| x_rotated_even = x_even * cos - x_odd * sin |
| x_rotated_odd = x_even * sin + x_odd * cos |
| |
| |
| x_out = torch.empty_like(x) |
| x_out[..., ::2] = x_rotated_even |
| x_out[..., 1::2] = x_rotated_odd |
| |
| return x_out |
|
|
|
|
| class OptimizedAttention(nn.Module): |
| """Multi-head attention with GQA and proper KV cache""" |
| |
| def __init__(self, config: OnyxConfig): |
| super().__init__() |
| self.config = config |
| self.d_model = config.d_model |
| self.n_heads = config.n_heads |
| self.n_kv_heads = config.n_kv_heads |
| self.head_dim = self.d_model // self.n_heads |
| self.n_rep = self.n_heads // self.n_kv_heads |
| |
| |
| self.q_proj = nn.Linear(self.d_model, self.n_heads * self.head_dim, bias=False) |
| self.k_proj = nn.Linear(self.d_model, self.n_kv_heads * self.head_dim, bias=False) |
| self.v_proj = nn.Linear(self.d_model, self.n_kv_heads * self.head_dim, bias=False) |
| self.o_proj = nn.Linear(self.d_model, self.d_model, bias=False) |
| |
| |
| self.rope = RoPE(config) |
| |
| |
| self.dropout = nn.Dropout(config.attention_dropout) |
| |
| def forward( |
| self, |
| x: torch.Tensor, |
| use_cache: bool = False, |
| past_kv: Optional[Tuple[torch.Tensor, torch.Tensor]] = None |
| ) -> Tuple[torch.Tensor, Optional[Tuple[torch.Tensor, torch.Tensor]]]: |
| batch_size, seq_len, _ = x.shape |
| |
| |
| q = self.q_proj(x).view(batch_size, seq_len, self.n_heads, self.head_dim) |
| k = self.k_proj(x).view(batch_size, seq_len, self.n_kv_heads, self.head_dim) |
| v = self.v_proj(x).view(batch_size, seq_len, self.n_kv_heads, self.head_dim) |
| |
| |
| offset = 0 |
| if past_kv is not None: |
| offset = past_kv[0].shape[2] |
| q, k = self.rope(q, k, seq_len, offset) |
| |
| |
| q_bshd = q |
| k_base = k |
| v_base = v |
| |
| |
| if past_kv is not None and use_cache: |
| past_k, past_v = past_kv |
| |
| k_cat = torch.cat([past_k, k_base.transpose(1,2)], dim=2) |
| v_cat = torch.cat([past_v, v_base.transpose(1,2)], dim=2) |
| k_base = k_cat.transpose(1,2) |
| v_base = v_cat.transpose(1,2) |
| |
| |
| if self.n_rep > 1: |
| k_rep = k_base.repeat_interleave(self.n_rep, dim=2) |
| v_rep = v_base.repeat_interleave(self.n_rep, dim=2) |
| else: |
| k_rep, v_rep = k_base, v_base |
| |
| |
| use_fa = ( |
| FLASH_AVAILABLE and self.config.use_flash_attn and not use_cache |
| and q_bshd.is_cuda and q_bshd.dtype in (torch.float16, torch.bfloat16) |
| ) |
| |
| if use_fa: |
| |
| q_bshd = q_bshd.contiguous() |
| k_rep = k_rep.contiguous() |
| v_rep = v_rep.contiguous() |
| |
| |
| attn_output = flash_attn_func( |
| q_bshd, k_rep, v_rep, |
| dropout_p=self.dropout.p if self.training else 0.0, |
| causal=True |
| ) |
| |
| attn_output = attn_output.transpose(1, 2) |
| else: |
| |
| q = q_bshd.transpose(1, 2) |
| k = k_rep.transpose(1, 2) |
| v = v_rep.transpose(1, 2) |
| attn_output = F.scaled_dot_product_attention( |
| q, k, v, |
| attn_mask=None, |
| dropout_p=self.dropout.p if self.training else 0.0, |
| is_causal=True |
| ) |
| |
| |
| attn_output = attn_output.transpose(1, 2).contiguous() |
| attn_output = attn_output.view(batch_size, seq_len, self.d_model) |
| output = self.o_proj(attn_output) |
| |
| |
| if use_cache: |
| new_kv = (k_base.transpose(1,2).contiguous(), v_base.transpose(1,2).contiguous()) |
| else: |
| new_kv = None |
| |
| return output, new_kv |
|
|
|
|
| class OptimizedFFN(nn.Module): |
| """Feed-forward network with SwiGLU""" |
| |
| def __init__(self, config: OnyxConfig): |
| super().__init__() |
| self.config = config |
| |
| if config.use_swiglu: |
| |
| self.w1 = nn.Linear(config.d_model, config.d_ff, bias=False) |
| self.w2 = nn.Linear(config.d_ff, config.d_model, bias=False) |
| self.w3 = nn.Linear(config.d_model, config.d_ff, bias=False) |
| else: |
| |
| self.up = nn.Linear(config.d_model, config.d_ff, bias=False) |
| self.down = nn.Linear(config.d_ff, config.d_model, bias=False) |
| |
| self.dropout = nn.Dropout(config.dropout) |
| |
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| if self.config.use_swiglu: |
| |
| gate = F.silu(self.w1(x)) |
| up = self.w3(x) |
| x = gate * up |
| x = self.w2(x) |
| else: |
| |
| x = self.up(x) |
| x = F.gelu(x) |
| x = self.down(x) |
| |
| return self.dropout(x) |
|
|
|
|
| class TransformerBlock(nn.Module): |
| """Single transformer block""" |
| |
| def __init__(self, config: OnyxConfig, layer_idx: int): |
| super().__init__() |
| self.config = config |
| self.layer_idx = layer_idx |
| |
| |
| self.norm1 = RMSNorm(config.d_model, config.norm_eps) |
| self.norm2 = RMSNorm(config.d_model, config.norm_eps) |
| |
| |
| self.attention = OptimizedAttention(config) |
| self.ffn = OptimizedFFN(config) |
| |
| def forward( |
| self, |
| x: torch.Tensor, |
| use_cache: bool = False, |
| past_kv: Optional[Tuple[torch.Tensor, torch.Tensor]] = None |
| ) -> Tuple[torch.Tensor, Optional[Tuple[torch.Tensor, torch.Tensor]]]: |
| |
| |
| residual = x |
| x = self.norm1(x) |
| attn_out, new_kv = self.attention(x, use_cache, past_kv) |
| x = residual + attn_out |
| |
| |
| residual = x |
| x = self.norm2(x) |
| x = self.ffn(x) |
| x = residual + x |
| |
| return x, new_kv |
|
|
|
|
| class Onyx7B(nn.Module): |
| """Onyx 7B Dense Model - Production Ready""" |
| |
| def __init__(self, config: OnyxConfig): |
| super().__init__() |
| self.config = config |
| |
| |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.d_model) |
| |
| |
| self.layers = nn.ModuleList([ |
| TransformerBlock(config, i) for i in range(config.n_layers) |
| ]) |
| |
| |
| self.norm = RMSNorm(config.d_model, config.norm_eps) |
| |
| |
| if config.tie_embeddings: |
| self.lm_head = None |
| else: |
| self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False) |
| |
| |
| self.apply(self._init_weights) |
| |
| def _init_weights(self, module): |
| """Initialize weights with scaled normal distribution""" |
| if isinstance(module, nn.Linear): |
| torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) |
| if module.bias is not None: |
| torch.nn.init.zeros_(module.bias) |
| elif isinstance(module, nn.Embedding): |
| torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) |
| |
| def forward( |
| self, |
| input_ids: torch.Tensor, |
| labels: Optional[torch.Tensor] = None, |
| use_cache: bool = False, |
| past_key_values: Optional[List[Tuple[torch.Tensor, torch.Tensor]]] = None, |
| return_dict: bool = True |
| ) -> Dict[str, Any]: |
| """ |
| Forward pass with KV cache support |
| |
| Args: |
| input_ids: Input token IDs (batch_size, seq_len) |
| labels: Target labels for training |
| use_cache: Whether to use/return KV cache |
| past_key_values: Past KV cache from previous forward pass |
| return_dict: Return dictionary or tuple |
| |
| Returns: |
| Dictionary with 'logits', optionally 'loss' and 'past_key_values' |
| """ |
| |
| if hasattr(torch._dynamo, 'is_compiling') and torch._dynamo.is_compiling(): |
| |
| use_cache = False |
| past_key_values = None |
| |
| batch_size, seq_len = input_ids.shape |
| |
| |
| hidden_states = self.embed_tokens(input_ids) |
| |
| |
| new_past_key_values = [] if use_cache else None |
| |
| for i, layer in enumerate(self.layers): |
| past_kv = past_key_values[i] if past_key_values else None |
| |
| if self.config.gradient_checkpointing and self.training: |
| |
| def cf(h): |
| y, _ = layer(h, use_cache=False, past_kv=None) |
| return y |
| hidden_states = torch.utils.checkpoint.checkpoint(cf, hidden_states, use_reentrant=False) |
| new_kv = None |
| else: |
| hidden_states, new_kv = layer(hidden_states, use_cache, past_kv) |
| |
| if use_cache: |
| new_past_key_values.append(new_kv) |
| |
| |
| hidden_states = self.norm(hidden_states) |
| |
| |
| if self.lm_head is None: |
| logits = F.linear(hidden_states, self.embed_tokens.weight) |
| else: |
| logits = self.lm_head(hidden_states) |
| |
| |
| loss = None |
| if labels is not None: |
| |
| shift_logits = logits[..., :-1, :].contiguous() |
| shift_labels = labels[..., 1:].contiguous() |
| |
| |
| loss = F.cross_entropy( |
| shift_logits.float().view(-1, shift_logits.size(-1)), |
| shift_labels.view(-1), |
| ignore_index=-100 |
| ) |
| |
| if return_dict: |
| return { |
| "logits": logits, |
| "loss": loss, |
| "past_key_values": new_past_key_values, |
| "hidden_states": hidden_states |
| } |
| |
| return (logits, new_past_key_values) if use_cache else logits |
| |
| def generate( |
| self, |
| input_ids: torch.Tensor, |
| max_length: int = 100, |
| temperature: float = 0.7, |
| top_p: float = 0.9, |
| top_k: Optional[int] = None, |
| use_cache: bool = True |
| ) -> torch.Tensor: |
| """ |
| Generate text using the model with KV cache |
| |
| Args: |
| input_ids: Starting token IDs (batch_size, seq_len) |
| max_length: Maximum generation length |
| temperature: Sampling temperature |
| top_p: Nucleus sampling threshold |
| use_cache: Use KV cache for efficiency |
| |
| Returns: |
| Generated token IDs |
| """ |
| self.eval() |
| |
| |
| past_key_values = None |
| generated_tokens = input_ids |
| temperature = max(1e-6, float(temperature)) |
| |
| |
| if generated_tokens.shape[1] >= max_length: |
| return generated_tokens[:, :max_length] |
| |
| with torch.inference_mode(): |
| for _ in range(max_length - input_ids.shape[1]): |
| |
| if past_key_values is not None: |
| input_tokens = generated_tokens[:, -1:] |
| else: |
| input_tokens = generated_tokens |
| |
| |
| outputs = self.forward( |
| input_tokens, |
| use_cache=use_cache, |
| past_key_values=past_key_values |
| ) |
| |
| logits = outputs["logits"] |
| past_key_values = outputs.get("past_key_values", None) |
| |
| |
| next_token_logits = logits[:, -1, :] / temperature |
| |
| |
| if top_k is not None and top_k > 0: |
| top_k_val = min(top_k, next_token_logits.size(-1)) |
| kth = torch.topk(next_token_logits, top_k_val, dim=-1).values[..., -1, None] |
| next_token_logits = torch.where( |
| next_token_logits < kth, |
| torch.full_like(next_token_logits, float("-inf")), |
| next_token_logits |
| ) |
| |
| |
| if top_p < 1.0: |
| sorted_logits, sorted_indices = torch.sort(next_token_logits, descending=True) |
| probs = F.softmax(sorted_logits, dim=-1) |
| cumulative_probs = torch.cumsum(probs, dim=-1) |
| |
| sorted_indices_to_remove = cumulative_probs > top_p |
| sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone() |
| sorted_indices_to_remove[..., 0] = 0 |
| |
| |
| base_mask = torch.zeros_like(sorted_indices_to_remove, dtype=torch.bool) |
| indices_to_remove = base_mask.scatter(1, sorted_indices, sorted_indices_to_remove) |
| next_token_logits = next_token_logits.masked_fill(indices_to_remove, float('-inf')) |
| |
| |
| probs = F.softmax(next_token_logits, dim=-1) |
| next_token = torch.multinomial(probs, num_samples=1) |
| |
| |
| generated_tokens = torch.cat([generated_tokens, next_token], dim=-1) |
| |
| |
| if (next_token == self.config.eos_token_id).any(): |
| break |
| |
| return generated_tokens |
| |
| def get_num_params(self) -> int: |
| """Get total number of parameters""" |
| return sum(p.numel() for p in self.parameters()) |
| |
| def get_param_groups(self) -> Dict[str, List[nn.Parameter]]: |
| """Get parameter groups for optimizer with correct weight decay""" |
| decay_params = [] |
| no_decay_params = [] |
| |
| for name, param in self.named_parameters(): |
| if not param.requires_grad: |
| continue |
| |
| |
| if any(k in name for k in ["norm", "bias", "embed_tokens"]): |
| no_decay_params.append(param) |
| else: |
| decay_params.append(param) |
| |
| return { |
| "decay": decay_params, |
| "no_decay": no_decay_params |
| } |
|
|
|
|
| def create_onyx_7b( |
| device: str = "cuda", |
| dtype: torch.dtype = torch.float16, |
| compile_model: bool = True |
| ) -> Onyx7B: |
| """ |
| Create and initialize Onyx 7B model |
| |
| Args: |
| device: Device to place model on |
| dtype: Data type for model weights (prefer bfloat16 on Ampere+) |
| compile_model: Whether to compile with torch.compile |
| |
| Returns: |
| Initialized Onyx 7B model |
| """ |
| |
| if device == "cuda": |
| torch.backends.cuda.matmul.allow_tf32 = True |
| torch.set_float32_matmul_precision("high") |
| |
| |
| config = OnyxConfig( |
| vocab_size=128256, |
| d_model=4096, |
| n_layers=32, |
| n_heads=32, |
| n_kv_heads=8, |
| d_ff=11008, |
| max_seq_len=16384, |
| rope_theta=500000.0, |
| use_swiglu=True, |
| use_rms_norm=True, |
| use_flash_attn=FLASH_AVAILABLE, |
| use_cuda_graphs=False, |
| use_torch_compile=compile_model |
| ) |
| |
| |
| model = Onyx7B(config) |
| |
| |
| model = model.to(device=device, dtype=dtype) |
| |
| |
| if compile_model and config.use_torch_compile: |
| model.forward = torch.compile( |
| model.forward, |
| mode="max-autotune", |
| dynamic=True |
| ) |
| |
| |
| num_params = model.get_num_params() |
| print(f"✅ Created Onyx 7B Dense Model") |
| print(f" Parameters: {num_params:,} ({num_params/1e9:.2f}B)") |
| print(f" Architecture: Dense transformer") |
| print(f" Optimizations: RoPE, SDPA, KV cache (4x savings)") |
| print(f" Device: {device}") |
| print(f" Dtype: {dtype}") |
| |
| return model |
|
|
|
|
| if __name__ == "__main__": |
| |
| print("Testing Onyx 7B Production Model") |
| print("=" * 60) |
| |
| |
| torch.manual_seed(0) |
| |
| |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| if device == "cuda": |
| torch.cuda.manual_seed_all(0) |
| print(f"CUDA Available: Yes (GPU: {torch.cuda.get_device_name()})") |
| print(f"CUDA Capability: {torch.cuda.get_device_capability()}") |
| else: |
| print("CUDA Available: No (using CPU)") |
| |
| dtype = torch.bfloat16 if (device == "cuda" and torch.cuda.get_device_capability()[0] >= 8) else torch.float16 |
| |
| model = create_onyx_7b(device=device, dtype=dtype, compile_model=False) |
| |
| |
| print("\nTesting forward pass...") |
| input_ids = torch.randint(0, 128256, (1, 32), device=device) |
| |
| with torch.inference_mode(): |
| outputs = model(input_ids, use_cache=True) |
| print(f"✅ Forward pass successful!") |
| print(f" Output shape: {outputs['logits'].shape}") |
| print(f" KV cache: {len(outputs['past_key_values'])} layers") |
| |
| |
| print("\nTesting generation with KV cache...") |
| import time |
| |
| |
| start = time.time() |
| generated = model.generate(input_ids, max_length=64, use_cache=False) |
| time_no_cache = time.time() - start |
| |
| |
| start = time.time() |
| generated = model.generate(input_ids, max_length=64, use_cache=True) |
| time_with_cache = time.time() - start |
| |
| print(f"✅ Generation successful!") |
| print(f" Without cache: {time_no_cache:.2f}s") |
| print(f" With cache: {time_with_cache:.2f}s") |
| if time_with_cache > 0: |
| print(f" Speedup: {time_no_cache/time_with_cache:.1f}x") |
| |
| |
| print("\nTesting top-k sampling...") |
| generated_topk = model.generate(input_ids, max_length=40, top_k=50, temperature=0.8) |
| print(f"✅ Top-k=50 generation: {generated_topk.shape}") |
| |
| |
| print("\nFA vs SDPA smoke test...") |
| model.eval() |
| x = torch.randint(0, 128256, (2, 16), device=device) |
| with torch.inference_mode(): |
| |
| model.config.use_flash_attn = False |
| y_sdpa = model(x, use_cache=False)["logits"] |
| |
| |
| model.config.use_flash_attn = True |
| try: |
| y_fa = model(x.to(dtype), use_cache=False)["logits"] |
| assert y_fa.shape == y_sdpa.shape |
| print(f"✅ FA/SDPA shapes match: {y_fa.shape}") |
| except Exception as e: |
| print(f"ℹ️ FA path skipped: {repr(e)}") |
| finally: |
| model.config.use_flash_attn = FLASH_AVAILABLE |
| |
| |
| print("\nKV cache head-count sanity...") |
| with torch.inference_mode(): |
| out = model(x[:, :8], use_cache=True) |
| pkv = out["past_key_values"] |
| k0, v0 = pkv[0] |
| |
| assert k0.shape[1] == model.config.n_kv_heads, f"Wrong KV heads: {k0.shape}" |
| print(f"✅ Compact KV cache shape: {k0.shape} (4x memory savings)") |
| |
| |
| print("\nKV cache growth during generation...") |
| generated_with_cache = model.generate(x[:1, :8], max_length=24, use_cache=True) |
| print(f" Generated sequence length: {generated_with_cache.shape[1]}") |
| print(f" Final KV cache per layer: (B={k0.shape[0]}, n_kv_heads={model.config.n_kv_heads}, S=varies, D={k0.shape[-1]})") |
| |
| |
| print("\nLong-context smoke test...") |
| L = min(4096, model.config.max_seq_len) |
| long_input = torch.randint(0, model.config.vocab_size, (1, L), device=device) |
| with torch.inference_mode(): |
| _ = model(long_input, use_cache=False) |
| print(f"✅ Processed {L} tokens successfully (RoPE/SDPA memory OK)") |
| |
| print("\n" + "=" * 60) |
| print("✅ All tests passed! Model is production-ready.") |
| print("\nKey features:") |
| print(" • FlashAttention v2 support with guards") |
| print(" • 4x KV cache memory savings (GQA)") |
| print(" • Fixed top-p sampling") |
| print(" • Gradient checkpointing (tensor-only)") |
| print(" • Float32 loss for stability") |
| print(" • PyTorch 2.8 optimized") |
|
|