Text Generation
Transformers
Safetensors
maccy
custom_code
mixture-of-experts
kimi-delta-attention
multi-head-latent-attention
Instructions to use bgub/maccy-106m-base with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use bgub/maccy-106m-base with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="bgub/maccy-106m-base", trust_remote_code=True)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("bgub/maccy-106m-base", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use bgub/maccy-106m-base with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "bgub/maccy-106m-base" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "bgub/maccy-106m-base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/bgub/maccy-106m-base
- SGLang
How to use bgub/maccy-106m-base 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 "bgub/maccy-106m-base" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "bgub/maccy-106m-base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'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 "bgub/maccy-106m-base" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "bgub/maccy-106m-base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use bgub/maccy-106m-base with Docker Model Runner:
docker model run hf.co/bgub/maccy-106m-base
| """Portable Transformers implementation of the Maccy architecture.""" | |
| from collections.abc import Sequence | |
| from typing import Any, cast | |
| import torch | |
| from torch import Tensor, nn | |
| from torch.nn import functional as F | |
| from transformers import PreTrainedModel | |
| from transformers.generation.utils import GenerationMixin | |
| from transformers.modeling_outputs import CausalLMOutputWithPast | |
| from .configuration_maccy import MaccyConfig | |
| _NORM_EPSILON = 1e-6 | |
| _MINIMUM_RETENTION = 0.125 | |
| class RMSNorm(nn.Module): | |
| """Normalize vector magnitude without subtracting its mean.""" | |
| def __init__(self, width: int) -> None: | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(width)) | |
| def forward(self, inputs: Tensor) -> Tensor: | |
| inverse_rms = torch.rsqrt( | |
| inputs.float().square().mean(dim=-1, keepdim=True) + _NORM_EPSILON | |
| ) | |
| return inputs * inverse_rms.to(inputs.dtype) * self.weight.to(inputs.dtype) | |
| class RotaryEmbedding(nn.Module): | |
| """Apply rotary position embeddings over the penultimate dimension.""" | |
| def __init__(self, width: int, maximum_length: int) -> None: | |
| super().__init__() | |
| self.width = width | |
| self.maximum_length = maximum_length | |
| def forward(self, inputs: Tensor) -> Tensor: | |
| sequence_length = inputs.shape[-2] | |
| if sequence_length > self.maximum_length: | |
| raise ValueError("sequence length exceeds the rotary embedding limit") | |
| pair_indices = torch.arange(0, self.width, 2, dtype=torch.float32, device=inputs.device) | |
| inverse_frequencies = 1.0 / (10_000.0 ** (pair_indices / self.width)) | |
| positions = torch.arange(sequence_length, dtype=torch.float32, device=inputs.device) | |
| angles = torch.outer(positions, inverse_frequencies) | |
| cosines = angles.cos().to(inputs.dtype) | |
| sines = angles.sin().to(inputs.dtype) | |
| even, odd = inputs[..., 0::2], inputs[..., 1::2] | |
| return torch.stack( | |
| (even * cosines - odd * sines, even * sines + odd * cosines), dim=-1 | |
| ).flatten(start_dim=-2) | |
| def recurrent_kda( | |
| queries: Tensor, | |
| keys: Tensor, | |
| values: Tensor, | |
| retention: Tensor, | |
| update_rate: Tensor, | |
| ) -> Tensor: | |
| """Evaluate the delta-rule memory recurrence in float32.""" | |
| output_dtype = values.dtype | |
| queries = queries.float() * (keys.shape[-1] ** -0.5) | |
| keys = keys.float() | |
| values = values.float() | |
| retention = retention.float() | |
| update_rate = update_rate.float() | |
| batch_size, _, n_heads, key_dim = keys.shape | |
| state = keys.new_zeros(batch_size, n_heads, key_dim, values.shape[-1]) | |
| outputs = [] | |
| for token_index in range(keys.shape[1]): | |
| query = queries[:, token_index] | |
| key = keys[:, token_index] | |
| value = values[:, token_index] | |
| state = state * retention[:, token_index].unsqueeze(-1) | |
| prediction = torch.einsum("bhkv,bhk->bhv", state, key) | |
| error = value - prediction | |
| beta = update_rate[:, token_index, :, None, None] | |
| state = state + beta * key.unsqueeze(-1) * error.unsqueeze(-2) | |
| outputs.append(torch.einsum("bhkv,bhk->bhv", state, query)) | |
| return torch.stack(outputs, dim=1).to(output_dtype) | |
| class CausalDepthwiseConvolution(nn.Module): | |
| """Mix a four-token local history independently within each channel.""" | |
| def __init__(self, d_model: int) -> None: | |
| super().__init__() | |
| self.width = 4 | |
| self.convolution = nn.Conv1d( | |
| d_model, d_model, kernel_size=self.width, groups=d_model, bias=False, padding=3 | |
| ) | |
| def forward(self, inputs: Tensor) -> Tensor: | |
| sequence_length = inputs.shape[1] | |
| convolved = self.convolution(inputs.transpose(1, 2))[..., :sequence_length] | |
| return F.silu(convolved.transpose(1, 2)) | |
| class KimiDeltaAttention(nn.Module): | |
| """Kimi Delta Attention with a portable recurrent implementation.""" | |
| def __init__(self, d_model: int, n_heads: int) -> None: | |
| super().__init__() | |
| self.n_heads = n_heads | |
| self.head_dim = d_model // n_heads | |
| self.qkv_projection = nn.Linear(d_model, 3 * d_model, bias=False) | |
| self.query_convolution = CausalDepthwiseConvolution(d_model) | |
| self.key_convolution = CausalDepthwiseConvolution(d_model) | |
| self.value_convolution = CausalDepthwiseConvolution(d_model) | |
| self.control_down = nn.Linear(d_model, 2 * self.head_dim, bias=False) | |
| self.update_projection = nn.Linear(d_model, n_heads, bias=False) | |
| self.retention_up = nn.Linear(self.head_dim, d_model, bias=False) | |
| self.retention_bias = nn.Parameter(torch.zeros(d_model)) | |
| self.log_decay_scale = nn.Parameter(torch.zeros(n_heads)) | |
| self.output_gate_up = nn.Linear(self.head_dim, d_model, bias=True) | |
| self.output_norm_weight = nn.Parameter(torch.ones(self.head_dim)) | |
| self.output = nn.Linear(d_model, d_model, bias=False) | |
| def _split_heads(self, inputs: Tensor) -> Tensor: | |
| return inputs.view(inputs.shape[0], inputs.shape[1], self.n_heads, self.head_dim) | |
| def forward(self, inputs: Tensor) -> Tensor: | |
| queries, keys, values = self.qkv_projection(inputs).chunk(3, dim=-1) | |
| queries = F.normalize( | |
| self._split_heads(self.query_convolution(queries)), dim=-1, eps=_NORM_EPSILON | |
| ) | |
| keys = F.normalize(self._split_heads(self.key_convolution(keys)), dim=-1, eps=_NORM_EPSILON) | |
| values = self._split_heads(self.value_convolution(values)) | |
| retention_latent, gate_latent = self.control_down(inputs).chunk(2, dim=-1) | |
| retention_logits = self.retention_up(retention_latent) + self.retention_bias | |
| retention_logits = self._split_heads(retention_logits).float() | |
| decay_scale = self.log_decay_scale.exp().view(1, 1, self.n_heads, 1) | |
| retention = (-decay_scale * F.softplus(retention_logits)).exp() | |
| retention = retention.clamp_min(_MINIMUM_RETENTION) | |
| update_rate = self.update_projection(inputs).float().sigmoid() | |
| output_gate = self._split_heads(self.output_gate_up(gate_latent)).float().sigmoid() | |
| mixed = recurrent_kda(queries, keys, values, retention, update_rate).float() | |
| inverse_rms = torch.rsqrt(mixed.square().mean(dim=-1, keepdim=True) + _NORM_EPSILON) | |
| mixed = mixed * inverse_rms * self.output_norm_weight.float() | |
| mixed = mixed * output_gate | |
| mixed = mixed.to(self.output.weight.dtype) | |
| return self.output(mixed.flatten(start_dim=-2)) | |
| class MultiHeadLatentAttention(nn.Module): | |
| """Causal attention through compressed query and key-value latents.""" | |
| def __init__(self, config: MaccyConfig) -> None: | |
| super().__init__() | |
| mla = config.mla | |
| self.n_heads = config.n_heads | |
| self.query_rank = mla["query_rank"] | |
| self.kv_rank = mla["kv_rank"] | |
| self.content_head_dim = mla["content_head_dim"] | |
| self.rope_head_dim = mla["rope_head_dim"] | |
| self.value_head_dim = mla["value_head_dim"] | |
| query_head_dim = self.content_head_dim + self.rope_head_dim | |
| self.input_down = nn.Linear( | |
| config.d_model, | |
| self.query_rank + self.kv_rank + self.rope_head_dim, | |
| bias=config.bias, | |
| ) | |
| self.query_norm = RMSNorm(self.query_rank) | |
| self.query_up = nn.Linear(self.query_rank, self.n_heads * query_head_dim, bias=config.bias) | |
| self.kv_norm = RMSNorm(self.kv_rank) | |
| self.kv_up = nn.Linear( | |
| self.kv_rank, | |
| self.n_heads * (self.content_head_dim + self.value_head_dim), | |
| bias=config.bias, | |
| ) | |
| self.rotary_embedding = RotaryEmbedding(self.rope_head_dim, config.context_length) | |
| self.gate = ( | |
| nn.Linear(config.d_model, self.n_heads * self.value_head_dim, bias=True) | |
| if mla["gated"] | |
| else None | |
| ) | |
| self.output = nn.Linear( | |
| self.n_heads * self.value_head_dim, config.d_model, bias=config.bias | |
| ) | |
| def forward(self, inputs: Tensor) -> Tensor: | |
| batch_size, sequence_length, _ = inputs.shape | |
| compressed_queries, compressed_kv, rotary_keys = self.input_down(inputs).split( | |
| (self.query_rank, self.kv_rank, self.rope_head_dim), dim=-1 | |
| ) | |
| expanded_queries = self.query_up(self.query_norm(compressed_queries)).view( | |
| batch_size, | |
| sequence_length, | |
| self.n_heads, | |
| self.content_head_dim + self.rope_head_dim, | |
| ) | |
| expanded_kv = self.kv_up(self.kv_norm(compressed_kv)).view( | |
| batch_size, | |
| sequence_length, | |
| self.n_heads, | |
| self.content_head_dim + self.value_head_dim, | |
| ) | |
| content_queries, rotary_queries = expanded_queries.split( | |
| (self.content_head_dim, self.rope_head_dim), dim=-1 | |
| ) | |
| content_keys, values = expanded_kv.split( | |
| (self.content_head_dim, self.value_head_dim), dim=-1 | |
| ) | |
| rotary_queries = self.rotary_embedding(rotary_queries.transpose(1, 2)) | |
| rotary_keys = self.rotary_embedding(rotary_keys.unsqueeze(1)).expand( | |
| -1, self.n_heads, -1, -1 | |
| ) | |
| queries = torch.cat((content_queries.transpose(1, 2), rotary_queries), dim=-1) | |
| keys = torch.cat((content_keys.transpose(1, 2), rotary_keys), dim=-1) | |
| values = values.transpose(1, 2) | |
| mixed = F.scaled_dot_product_attention(queries, keys, values, is_causal=True) | |
| mixed = mixed.transpose(1, 2) | |
| if self.gate is not None: | |
| gate = self.gate(inputs).view( | |
| batch_size, sequence_length, self.n_heads, self.value_head_dim | |
| ) | |
| mixed = mixed * gate.sigmoid() | |
| return self.output(mixed.flatten(start_dim=-2)) | |
| class PackedSwiGLUExperts(nn.Module): | |
| """Store equal-shaped experts in two packed parameter tensors.""" | |
| def __init__(self, n_experts: int, d_model: int, hidden_dim: int, *, bias: bool) -> None: | |
| super().__init__() | |
| self.input_weight = nn.Parameter(torch.empty(n_experts, d_model, 2 * hidden_dim)) | |
| self.output_weight = nn.Parameter(torch.empty(n_experts, hidden_dim, d_model)) | |
| if bias: | |
| self.input_bias = nn.Parameter(torch.zeros(n_experts, 2 * hidden_dim)) | |
| self.output_bias = nn.Parameter(torch.zeros(n_experts, d_model)) | |
| else: | |
| self.register_parameter("input_bias", None) | |
| self.register_parameter("output_bias", None) | |
| def forward_expert(self, expert_index: int, inputs: Tensor) -> Tensor: | |
| projected = inputs @ self.input_weight[expert_index] | |
| if self.input_bias is not None: | |
| projected = projected + self.input_bias[expert_index] | |
| gate, values = projected.chunk(2, dim=-1) | |
| updates = (F.silu(gate) * values) @ self.output_weight[expert_index] | |
| if self.output_bias is not None: | |
| updates = updates + self.output_bias[expert_index] | |
| return updates | |
| class SparseMoE(nn.Module): | |
| """Route each token to a weighted top-k subset of SwiGLU experts.""" | |
| def __init__(self, config: MaccyConfig) -> None: | |
| super().__init__() | |
| moe = config.moe | |
| self.n_experts = moe["n_experts"] | |
| self.experts_per_token = moe["experts_per_token"] | |
| self.router = nn.Linear(config.d_model, self.n_experts, bias=False) | |
| hidden_dim = round(moe["expert_expansion"] * config.d_model) | |
| self.experts = PackedSwiGLUExperts( | |
| self.n_experts, config.d_model, hidden_dim, bias=config.bias | |
| ) | |
| def forward(self, inputs: Tensor) -> Tensor: | |
| input_shape = inputs.shape | |
| flat_inputs = inputs.flatten(0, -2) | |
| probabilities = self.router(flat_inputs).float().softmax(dim=-1) | |
| weights, expert_indices = probabilities.topk(self.experts_per_token, dim=-1) | |
| weights = (weights / weights.sum(dim=-1, keepdim=True)).to(inputs.dtype) | |
| updates = torch.zeros_like(flat_inputs) | |
| for expert_index in range(self.n_experts): | |
| token_indices, choice_indices = torch.where(expert_indices == expert_index) | |
| if token_indices.numel() == 0: | |
| continue | |
| expert_updates = self.experts.forward_expert( | |
| expert_index, flat_inputs.index_select(0, token_indices) | |
| ) | |
| expert_weights = weights[token_indices, choice_indices].unsqueeze(-1) | |
| updates = updates.index_add(0, token_indices, expert_updates * expert_weights) | |
| return updates.view(input_shape) | |
| class TransformerBlock(nn.Module): | |
| """Apply one pre-normalized sequence mixer and sparse channel mixer.""" | |
| def __init__(self, config: MaccyConfig, mixer_kind: str) -> None: | |
| super().__init__() | |
| self.attention_norm = RMSNorm(config.d_model) | |
| self.mixer = ( | |
| KimiDeltaAttention(config.d_model, config.n_heads) | |
| if mixer_kind == "kda" | |
| else MultiHeadLatentAttention(config) | |
| ) | |
| self.feed_forward_norm = RMSNorm(config.d_model) | |
| self.feed_forward = SparseMoE(config) | |
| def forward(self, inputs: Tensor) -> Tensor: | |
| inputs = inputs + self.mixer(self.attention_norm(inputs)) | |
| return inputs + self.feed_forward(self.feed_forward_norm(inputs)) | |
| class MaccyPreTrainedModel(PreTrainedModel): | |
| """Shared Transformers metadata for Maccy models.""" | |
| config_class = MaccyConfig | |
| base_model_prefix = "" | |
| _no_split_modules = ["TransformerBlock"] | |
| _supports_sdpa = True | |
| def _init_weights(self, module: nn.Module) -> None: | |
| if isinstance(module, (nn.Linear, nn.Embedding, nn.Conv1d)): | |
| nn.init.normal_(module.weight, mean=0.0, std=0.02) | |
| if isinstance(module, nn.Linear) and module.bias is not None: | |
| nn.init.zeros_(module.bias) | |
| class MaccyForCausalLM(MaccyPreTrainedModel, GenerationMixin): | |
| """Maccy decoder with a tied next-token language-modeling head.""" | |
| _tied_weights_keys = {"lm_head.weight": "token_embedding.weight"} | |
| def __init__(self, config: MaccyConfig) -> None: | |
| super().__init__(config) | |
| self.token_embedding = nn.Embedding(config.vocab_size, config.d_model) | |
| mixers = tuple(part.strip() for part in config.mixer_pattern.split(",")) | |
| repeated_mixers = mixers * (config.n_layers // len(mixers)) | |
| self.blocks = nn.ModuleList( | |
| TransformerBlock(config, mixer_kind) for mixer_kind in repeated_mixers | |
| ) | |
| self.output_norm = RMSNorm(config.d_model) | |
| self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=config.bias) | |
| self.post_init() | |
| def get_input_embeddings(self) -> nn.Embedding: | |
| return self.token_embedding | |
| def set_input_embeddings(self, value: nn.Module) -> None: | |
| if not isinstance(value, nn.Embedding): | |
| raise TypeError("input embeddings must be an nn.Embedding") | |
| self.token_embedding = value | |
| def get_output_embeddings(self) -> nn.Linear: | |
| return self.lm_head | |
| def set_output_embeddings(self, new_embeddings: nn.Module) -> None: | |
| if not isinstance(new_embeddings, nn.Linear): | |
| raise TypeError("output embeddings must be an nn.Linear") | |
| self.lm_head = new_embeddings | |
| def forward( | |
| self, | |
| input_ids: Tensor | None = None, | |
| attention_mask: Tensor | None = None, | |
| inputs_embeds: Tensor | None = None, | |
| labels: Tensor | None = None, | |
| use_cache: bool | None = None, | |
| logits_to_keep: int | Tensor = 0, | |
| return_dict: bool | None = None, | |
| **_: Any, | |
| ) -> CausalLMOutputWithPast | tuple[Tensor, ...]: | |
| del attention_mask, use_cache | |
| if (input_ids is None) == (inputs_embeds is None): | |
| raise ValueError("pass exactly one of input_ids or inputs_embeds") | |
| hidden_states = self.token_embedding(input_ids) if inputs_embeds is None else inputs_embeds | |
| if hidden_states.shape[1] > self.config.context_length: | |
| raise ValueError(f"Maccy's context length is {self.config.context_length} tokens") | |
| for block in self.blocks: | |
| hidden_states = block(hidden_states) | |
| hidden_states = self.output_norm(hidden_states) | |
| indices = ( | |
| slice(None) | |
| if labels is not None or (isinstance(logits_to_keep, int) and logits_to_keep == 0) | |
| else slice(-logits_to_keep, None) | |
| if isinstance(logits_to_keep, int) | |
| else logits_to_keep | |
| ) | |
| logits = self.lm_head(hidden_states[:, indices, :]) | |
| loss = None | |
| if labels is not None: | |
| shift_logits = logits[:, :-1].contiguous().float() | |
| shift_labels = labels[:, 1:].contiguous() | |
| loss = F.cross_entropy( | |
| shift_logits.view(-1, self.config.vocab_size), | |
| shift_labels.view(-1), | |
| ignore_index=-100, | |
| ) | |
| output = CausalLMOutputWithPast( | |
| loss=cast(torch.FloatTensor | None, loss), | |
| logits=logits, | |
| past_key_values=None, | |
| ) | |
| if return_dict is False: | |
| values: Sequence[Tensor | None] = (loss, logits) if loss is not None else (logits,) | |
| return tuple(value for value in values if value is not None) | |
| return output | |