Text Generation
Transformers
Safetensors
English
microloop_diffusion
causal-lm
base-model
small-language-model
custom_code
muon
hummingbird-v1
conversational
Instructions to use juinron/Hummingbird-V1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use juinron/Hummingbird-V1 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="juinron/Hummingbird-V1", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("juinron/Hummingbird-V1", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use juinron/Hummingbird-V1 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "juinron/Hummingbird-V1" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "juinron/Hummingbird-V1", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/juinron/Hummingbird-V1
- SGLang
How to use juinron/Hummingbird-V1 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 "juinron/Hummingbird-V1" \ --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": "juinron/Hummingbird-V1", "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 "juinron/Hummingbird-V1" \ --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": "juinron/Hummingbird-V1", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use juinron/Hummingbird-V1 with Docker Model Runner:
docker model run hf.co/juinron/Hummingbird-V1
| """Small, HF-compatible causal bootstrap model used by the M0 gate. | |
| The diffusion objective and sampler are deliberately separate modules. This model | |
| provides the shared transformer backbone and a causal forward path so that the | |
| project can validate shape correctness, parameter accounting, and reproducibility | |
| before any expensive data work begins. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Optional | |
| import torch | |
| from torch import Tensor, nn | |
| from torch.nn import functional as F | |
| from .configuration_microloop import MicroLoopConfig | |
| try: | |
| from transformers import PreTrainedModel | |
| from transformers.generation import GenerationMixin | |
| from transformers.utils import ModelOutput | |
| except ImportError: # pragma: no cover - only used in a minimal environment. | |
| class GenerationMixin: # type: ignore[no-redef] | |
| pass | |
| class ModelOutput: # type: ignore[no-redef] | |
| pass | |
| class PreTrainedModel(nn.Module): # type: ignore[no-redef] | |
| config_class = MicroLoopConfig | |
| base_model_prefix = "microloop" | |
| def __init__(self, config: MicroLoopConfig) -> None: | |
| super().__init__() | |
| self.config = config | |
| class MicroLoopCausalLMOutput(ModelOutput): | |
| """Minimal output object with both attribute and mapping-style access.""" | |
| logits: Tensor | |
| loss: Optional[Tensor] = None | |
| hidden_states: Optional[Tensor] = None | |
| loop_applications: Optional[int] = None | |
| def __getitem__(self, key: str): | |
| return getattr(self, key) | |
| class RMSNorm(nn.Module): | |
| def __init__(self, hidden_size: int, eps: float) -> None: | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(hidden_size)) | |
| self.eps = eps | |
| def forward(self, hidden_states: Tensor) -> Tensor: | |
| # Explicit computation rather than the fused ``F.rms_norm`` kernel: | |
| # the fused kernel selects implementations based on process-level state | |
| # and produces context-dependent numerics inside the training process | |
| # (the 2026-08-05 provenance incident). This explicit path is | |
| # deterministic everywhere; the small speed cost is acceptable here. | |
| variance = hidden_states.to(torch.float32).pow(2).mean(-1, keepdim=True) | |
| return (hidden_states / torch.sqrt(variance + self.eps)) * self.weight | |
| def _rotate_half(x: Tensor) -> Tensor: | |
| x_even = x[..., ::2] | |
| x_odd = x[..., 1::2] | |
| return torch.stack((-x_odd, x_even), dim=-1).flatten(-2) | |
| def _rope_tables(max_position: int, head_dim: int, theta: float) -> tuple[Tensor, Tensor]: | |
| """Build interleaved rotary tables once, in float32 for stable reuse.""" | |
| inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, dtype=torch.float32) / head_dim)) | |
| positions = torch.arange(max_position, device=inv_freq.device, dtype=torch.float32) | |
| angles = positions.unsqueeze(-1) * inv_freq | |
| angles = torch.stack((angles, angles), dim=-1).flatten(-2) | |
| return angles.cos(), angles.sin() | |
| def _apply_rope(q: Tensor, k: Tensor, cos: Tensor, sin: Tensor) -> tuple[Tensor, Tensor]: | |
| """Apply cached interleaved rotary embeddings to query and key tensors.""" | |
| return q * cos + _rotate_half(q) * sin, k * cos + _rotate_half(k) * sin | |
| class GroupedQueryAttention(nn.Module): | |
| """Grouped-query self-attention with explicit Q/K/V projections. | |
| Attention uses an explicit scaled-dot-product implementation (matmul + | |
| softmax) rather than ``F.scaled_dot_product_attention`` because the fused | |
| kernels select implementations based on process-level state and produce | |
| deterministic but context-dependent results: evaluations inside the | |
| training process then disagree with evaluations of the same saved | |
| checkpoint in a fresh process (the 2026-08-05 provenance incident). The | |
| explicit math path is deterministic everywhere at the cost of a small | |
| amount of speed, which is acceptable at this model size. | |
| """ | |
| def __init__(self, config: MicroLoopConfig) -> None: | |
| super().__init__() | |
| self.num_heads = config.num_attention_heads | |
| self.num_key_value_heads = config.num_key_value_heads | |
| self.head_dim = config.head_dimension | |
| self.num_groups = self.num_heads // self.num_key_value_heads | |
| self.q_proj = nn.Linear(config.hidden_size, self.num_heads * self.head_dim, bias=False) | |
| self.k_proj = nn.Linear( | |
| config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False | |
| ) | |
| self.v_proj = nn.Linear( | |
| config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False | |
| ) | |
| self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False) | |
| self.output_gate = ( | |
| nn.Linear(config.hidden_size, self.num_heads, bias=False) | |
| if config.attention_output_gate | |
| else None | |
| ) | |
| self.rope_theta = config.rope_theta | |
| self.attention_implementation = config.attention_implementation | |
| if config.qk_norm == "per_head": | |
| # One affine scale is shared by all Q heads and one by all K heads; | |
| # normalization itself is applied independently over each head. | |
| self.q_norm: RMSNorm | None = RMSNorm(self.head_dim, config.rms_norm_eps) | |
| self.k_norm: RMSNorm | None = RMSNorm(self.head_dim, config.rms_norm_eps) | |
| else: | |
| self.q_norm = None | |
| self.k_norm = None | |
| def forward( | |
| self, | |
| hidden_states: Tensor, | |
| attention_mask: Tensor | None = None, | |
| position_ids: Tensor | None = None, | |
| rope_embeddings: tuple[Tensor, Tensor] | None = None, | |
| ) -> Tensor: | |
| batch, sequence, _ = hidden_states.shape | |
| q = self.q_proj(hidden_states).view(batch, sequence, self.num_heads, self.head_dim) | |
| k = self.k_proj(hidden_states).view( | |
| batch, sequence, self.num_key_value_heads, self.head_dim | |
| ) | |
| v = self.v_proj(hidden_states).view( | |
| batch, sequence, self.num_key_value_heads, self.head_dim | |
| ) | |
| q = q.transpose(1, 2) | |
| k = k.transpose(1, 2) | |
| v = v.transpose(1, 2) | |
| if position_ids is None: | |
| position_ids = torch.arange(sequence, device=hidden_states.device).expand(batch, -1) | |
| if position_ids.shape != (batch, sequence): | |
| raise ValueError( | |
| f"position_ids must have shape [batch, sequence], got {tuple(position_ids.shape)}" | |
| ) | |
| if rope_embeddings is None: | |
| table_cos, table_sin = _rope_tables(sequence, self.head_dim, self.rope_theta) | |
| cos = table_cos[position_ids].unsqueeze(1).to(q.dtype) | |
| sin = table_sin[position_ids].unsqueeze(1).to(q.dtype) | |
| else: | |
| cos, sin = rope_embeddings | |
| q, k = _apply_rope(q, k, cos, sin) | |
| if self.q_norm is not None: | |
| q = self.q_norm(q) | |
| assert self.k_norm is not None | |
| k = self.k_norm(k) | |
| if self.num_heads % self.num_key_value_heads != 0: | |
| raise ValueError( | |
| f"num_heads {self.num_heads} must divide num_key_value_heads " | |
| f"{self.num_key_value_heads}" | |
| ) | |
| if attention_mask is None: | |
| visible: Tensor | None = None | |
| is_causal = True | |
| else: | |
| # A 2-D mask uses the conventional HF meaning: one means visible. | |
| if attention_mask.shape == (batch, sequence): | |
| causal = torch.tril( | |
| torch.ones(sequence, sequence, device=q.device, dtype=torch.bool) | |
| ) | |
| visible = causal.unsqueeze(0).unsqueeze(0) & attention_mask.bool().unsqueeze( | |
| 1 | |
| ).unsqueeze(2) | |
| is_causal = False | |
| elif attention_mask.shape == (batch, sequence, sequence): | |
| visible = attention_mask.bool().unsqueeze(1) | |
| is_causal = False | |
| else: | |
| raise ValueError( | |
| "attention_mask must have shape [batch, sequence] or " | |
| "[batch, sequence, sequence], " | |
| f"got {tuple(attention_mask.shape)}" | |
| ) | |
| # GQA: repeat the KV heads so every query head has its own K/V. | |
| repeat = self.num_heads // self.num_key_value_heads | |
| if repeat > 1: | |
| k = k.repeat_interleave(repeat, dim=1) | |
| v = v.repeat_interleave(repeat, dim=1) | |
| if self.attention_implementation == "sdpa": | |
| attended = F.scaled_dot_product_attention( | |
| q, k, v, attn_mask=visible, dropout_p=0.0, is_causal=is_causal | |
| ) | |
| else: | |
| scores = torch.matmul(q, k.transpose(-2, -1)) / float(self.head_dim) ** 0.5 | |
| if is_causal: | |
| seq_ids = torch.arange(sequence, device=scores.device) | |
| visible = seq_ids.unsqueeze(0) <= seq_ids.unsqueeze(1) | |
| visible = visible.expand(batch, self.num_heads, sequence, sequence) | |
| if visible is not None: | |
| scores = scores.masked_fill(~visible, float("-inf")) | |
| probs = torch.softmax(scores, dim=-1) | |
| attended = torch.matmul(probs, v) | |
| if self.output_gate is not None: | |
| gate = F.silu(self.output_gate(hidden_states)).transpose(1, 2).unsqueeze(-1) | |
| attended = attended * gate | |
| attended = attended.transpose(1, 2).contiguous().view(batch, sequence, -1) | |
| return self.o_proj(attended) | |
| class MicroLoopBlock(nn.Module): | |
| """Pre-norm transformer block with SwiGLU feed-forward network.""" | |
| def __init__(self, config: MicroLoopConfig) -> None: | |
| super().__init__() | |
| self.attn_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.attn = GroupedQueryAttention(config) | |
| self.ffn_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.ffn_gate = nn.Linear(config.hidden_size, config.intermediate_size, bias=False) | |
| self.ffn_up = nn.Linear(config.hidden_size, config.intermediate_size, bias=False) | |
| self.ffn_down = nn.Linear(config.intermediate_size, config.hidden_size, bias=False) | |
| self.use_attn_residuals = config.attn_res_block_size is not None | |
| if self.use_attn_residuals: | |
| self.attn_res_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.ffn_res_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.attn_res_proj = nn.Linear(config.hidden_size, 1, bias=False) | |
| self.ffn_res_proj = nn.Linear(config.hidden_size, 1, bias=False) | |
| clamp = dict(config.swiglu_clamp) | |
| self.swiglu_clamp_enabled = bool(clamp.get("enabled", False)) | |
| self.swiglu_linear_min = float(clamp.get("linear_min", -10.0)) | |
| self.swiglu_linear_max = float(clamp.get("linear_max", 10.0)) | |
| self.swiglu_gate_max = float(clamp.get("gate_max", 10.0)) | |
| def forward( | |
| self, | |
| hidden_states: Tensor, | |
| attention_mask: Tensor | None = None, | |
| position_ids: Tensor | None = None, | |
| rope_embeddings: tuple[Tensor, Tensor] | None = None, | |
| block_residuals: list[Tensor] | None = None, | |
| ) -> Tensor: | |
| if self.use_attn_residuals and block_residuals: | |
| residual_stack = torch.stack(block_residuals, dim=-2) | |
| scores = torch.cat( | |
| [self.attn_res_proj(self.attn_res_norm(state)) for state in block_residuals], dim=-1 | |
| ) | |
| hidden_states = hidden_states + ( | |
| torch.softmax(scores, dim=-1).unsqueeze(-1) * residual_stack | |
| ).sum(dim=-2) | |
| hidden_states = hidden_states + self.attn( | |
| self.attn_norm(hidden_states), | |
| attention_mask, | |
| position_ids, | |
| rope_embeddings, | |
| ) | |
| if self.use_attn_residuals and block_residuals: | |
| residual_stack = torch.stack(block_residuals, dim=-2) | |
| scores = torch.cat( | |
| [self.ffn_res_proj(self.ffn_res_norm(state)) for state in block_residuals], dim=-1 | |
| ) | |
| hidden_states = hidden_states + ( | |
| torch.softmax(scores, dim=-1).unsqueeze(-1) * residual_stack | |
| ).sum(dim=-2) | |
| ffn_input = self.ffn_norm(hidden_states) | |
| gate_linear = self.ffn_gate(ffn_input) | |
| up_linear = self.ffn_up(ffn_input) | |
| if self.swiglu_clamp_enabled: | |
| gate_linear = gate_linear.clamp(self.swiglu_linear_min, self.swiglu_linear_max) | |
| up_linear = up_linear.clamp(self.swiglu_linear_min, self.swiglu_linear_max) | |
| gate = F.silu(gate_linear) | |
| if self.swiglu_clamp_enabled: | |
| gate = gate.clamp(max=self.swiglu_gate_max) | |
| ffn_output = self.ffn_down(gate * up_linear) | |
| return hidden_states + ffn_output | |
| class MicroLoopPreTrainedModel(PreTrainedModel): | |
| config_class = MicroLoopConfig | |
| base_model_prefix = "microloop" | |
| class MicroLoopForDiffusionLM(MicroLoopPreTrainedModel, GenerationMixin): | |
| """Backbone plus tied output head for causal bootstrap and diffusion training.""" | |
| _tied_weights_keys = {"lm_head.weight": "embed_tokens.weight"} | |
| def __init__(self, config: MicroLoopConfig) -> None: | |
| config.validate() | |
| super().__init__(config) | |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) | |
| self.layers = nn.ModuleList( | |
| [MicroLoopBlock(config) for _ in range(config.num_hidden_layers)] | |
| ) | |
| self.norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) | |
| self.mtp_proj = ( | |
| nn.Linear(config.hidden_size, config.hidden_size, bias=False) | |
| if config.mtp_enabled | |
| else None | |
| ) | |
| rope_cos, rope_sin = _rope_tables( | |
| config.max_position_embeddings, config.head_dimension, config.rope_theta | |
| ) | |
| # PERSISTENT buffers on purpose: transformers 5.x ``from_pretrained`` | |
| # re-initializes non-persistent buffers that are missing from the | |
| # checkpoint (``_initialize_missing_keys`` -> ``initialize_weights``), | |
| # overwriting the rotary tables with garbage. That made every fresh- | |
| # process evaluation of saved checkpoints compute with corrupted rope | |
| # tables (the historical "external collapse" at ~0.03 accuracy was this | |
| # artifact). Persisting the tables makes the saved checkpoint carry | |
| # exactly the tables used in training. | |
| self.register_buffer("rope_cos", rope_cos, persistent=True) | |
| self.register_buffer("rope_sin", rope_sin, persistent=True) | |
| if config.tie_word_embeddings: | |
| self.lm_head.weight = self.embed_tokens.weight | |
| # Canonical HF pattern: post_init() installs all_tied_weights_keys and | |
| # dispatches _initialize_weights per module. | |
| self.post_init() | |
| def _reset_rope_buffers(self) -> None: | |
| """Recompute the rotary tables from the config (they are deterministic).""" | |
| rope_cos, rope_sin = _rope_tables( | |
| self.config.max_position_embeddings, | |
| self.config.head_dimension, | |
| self.config.rope_theta, | |
| ) | |
| self.rope_cos.copy_(rope_cos) | |
| self.rope_sin.copy_(rope_sin) | |
| def _initialize_weights(self, module: nn.Module, is_custom_code: bool = False) -> None: | |
| # The caller controls the RNG through seed_everything; this method performs | |
| # no hidden reseeding and is therefore reproducible by construction. | |
| if getattr(module, "_is_hf_initialized", False): | |
| return | |
| if module is self: | |
| # The main module owns the rotary tables. transformers 5.x | |
| # re-initializes buffers that are missing from a loaded checkpoint | |
| # ("_initialize_missing_keys"), which zeroes/garbles the tables; | |
| # restore the deterministic canonical tables here instead. | |
| self._reset_rope_buffers() | |
| if isinstance(module, (nn.Linear, nn.Embedding)): | |
| nn.init.normal_(module.weight, mean=0.0, std=0.02) | |
| if getattr(module, "bias", None) is not None: | |
| nn.init.zeros_(module.bias) | |
| elif isinstance(module, RMSNorm): | |
| nn.init.ones_(module.weight) | |
| def get_input_embeddings(self) -> nn.Embedding: | |
| return self.embed_tokens | |
| def get_output_embeddings(self) -> nn.Linear: | |
| return self.lm_head | |
| def set_input_embeddings(self, value: nn.Embedding) -> None: | |
| self.embed_tokens = value | |
| if self.config.tie_word_embeddings: | |
| self.lm_head.weight = self.embed_tokens.weight | |
| def forward( | |
| self, | |
| input_ids: Tensor | None = None, | |
| inputs_embeds: Tensor | None = None, | |
| attention_mask: Tensor | None = None, | |
| document_ids: Tensor | None = None, | |
| position_ids: Tensor | None = None, | |
| labels: Tensor | None = None, | |
| logit_mask: Tensor | None = None, | |
| mtp_loss_weight: float = 0.0, | |
| loop_count: int = 1, | |
| output_hidden_states: bool = False, | |
| **_: object, | |
| ) -> MicroLoopCausalLMOutput: | |
| if (input_ids is None) == (inputs_embeds is None): | |
| raise ValueError("exactly one of input_ids or inputs_embeds must be provided") | |
| if loop_count < 1: | |
| raise ValueError("loop_count must be at least one") | |
| if input_ids is not None: | |
| if input_ids.dim() != 2: | |
| raise ValueError( | |
| f"input_ids must have shape [batch, sequence], got {input_ids.dim()}-D" | |
| ) | |
| batch, sequence = input_ids.shape | |
| else: | |
| if inputs_embeds is None or inputs_embeds.dim() != 3: | |
| raise ValueError( | |
| "inputs_embeds must have shape [batch, sequence, hidden], got " | |
| f"{None if inputs_embeds is None else inputs_embeds.dim()}-D" | |
| ) | |
| batch, sequence, _ = inputs_embeds.shape | |
| device = (input_ids if input_ids is not None else inputs_embeds).device | |
| if document_ids is not None: | |
| if document_ids.shape != (batch, sequence): | |
| raise ValueError("document_ids must have the same shape as the input") | |
| if attention_mask is not None and attention_mask.dim() != 2: | |
| raise ValueError( | |
| "document_ids cannot be combined with a precomputed attention mask" | |
| ) | |
| causal = torch.tril(torch.ones(sequence, sequence, dtype=torch.bool, device=device)) | |
| valid = ( | |
| attention_mask.bool() | |
| if attention_mask is not None | |
| else torch.ones((batch, sequence), dtype=torch.bool, device=device) | |
| ) | |
| attention_mask = ( | |
| causal.unsqueeze(0) | |
| & document_ids.unsqueeze(2).eq(document_ids.unsqueeze(1)) | |
| & valid.unsqueeze(1) | |
| & valid.unsqueeze(2) | |
| ) | |
| if position_ids is None: | |
| position_ids = torch.arange(sequence, device=device).expand(batch, -1) | |
| rope_dtype = ( | |
| torch.get_autocast_dtype("cuda") | |
| if device.type == "cuda" and torch.is_autocast_enabled("cuda") | |
| else self.embed_tokens.weight.dtype | |
| ) | |
| rope_embeddings = ( | |
| self.rope_cos[position_ids].unsqueeze(1).to(rope_dtype), | |
| self.rope_sin[position_ids].unsqueeze(1).to(rope_dtype), | |
| ) | |
| hidden_states = self.embed_tokens(input_ids) if input_ids is not None else inputs_embeds | |
| loop_layers = set(self.config.looping.get("layers", [4, 5, 6])) | |
| block_residuals: list[Tensor] = [] | |
| block_size = self.config.attn_res_block_size | |
| total_applications = 0 | |
| for layer_number, layer in enumerate(self.layers, start=1): | |
| if block_size is not None and (layer_number - 1) % block_size == 0: | |
| block_residuals.append(hidden_states) | |
| prior_block_residuals = block_residuals[:-1] if block_size is not None else None | |
| repetitions = loop_count if layer_number in loop_layers else 1 | |
| for _ in range(repetitions): | |
| hidden_states = layer( | |
| hidden_states, | |
| attention_mask=attention_mask, | |
| position_ids=position_ids, | |
| rope_embeddings=rope_embeddings, | |
| block_residuals=prior_block_residuals, | |
| ) | |
| total_applications += 1 | |
| hidden_states = self.norm(hidden_states) | |
| if logit_mask is not None: | |
| # Diffusion training: only the masked positions carry loss, so the | |
| # output head runs on those rows instead of the full sequence. | |
| if labels is not None: | |
| raise ValueError("logit_mask cannot be combined with labels") | |
| if logit_mask.shape != (batch, sequence): | |
| raise ValueError("logit_mask must have the same shape as the input") | |
| flat = hidden_states.reshape(-1, self.config.hidden_size) | |
| logits = self.lm_head(flat[logit_mask.reshape(-1)]) | |
| else: | |
| logits = self.lm_head(hidden_states) | |
| loss = None | |
| if labels is not None: | |
| if labels.shape != (batch, sequence): | |
| raise ValueError("labels must have the same shape as the input") | |
| if labels.size(1) < 2: | |
| raise ValueError("causal training requires sequences with at least two tokens") | |
| # Causal next-token prediction: position t predicts the label at t + 1. | |
| loss = F.cross_entropy( | |
| logits[:, :-1, :].reshape(-1, logits.size(-1)), | |
| labels[:, 1:].reshape(-1), | |
| ignore_index=-100, | |
| ) | |
| if mtp_loss_weight: | |
| if self.mtp_proj is None: | |
| raise ValueError("mtp_loss_weight requires mtp_enabled=true") | |
| if mtp_loss_weight < 0: | |
| raise ValueError("mtp_loss_weight must be non-negative") | |
| mtp_logits = self.lm_head(self.mtp_proj(hidden_states[:, :-2, :])) | |
| mtp_loss = F.cross_entropy( | |
| mtp_logits.reshape(-1, mtp_logits.size(-1)), | |
| labels[:, 2:].reshape(-1), | |
| ignore_index=-100, | |
| ) | |
| loss = loss + float(mtp_loss_weight) * mtp_loss | |
| return MicroLoopCausalLMOutput( | |
| logits=logits, | |
| loss=loss, | |
| hidden_states=hidden_states if output_hidden_states else None, | |
| loop_applications=total_applications, | |
| ) | |
| def generate_greedy( | |
| self, input_ids: Tensor, max_new_tokens: int, eos_token_id: int | None = None | |
| ) -> Tensor: | |
| """Small causal smoke decoder; diffusion sampling belongs in ``sampler.py``.""" | |
| return self.generate_causal( | |
| input_ids, max_new_tokens=max_new_tokens, eos_token_id=eos_token_id, do_sample=False | |
| ) | |
| def generate_causal( | |
| self, | |
| input_ids: Tensor, | |
| *, | |
| max_new_tokens: int, | |
| eos_token_id: int | None = None, | |
| do_sample: bool = False, | |
| temperature: float = 1.0, | |
| top_k: int | None = None, | |
| ) -> Tensor: | |
| """Generate a batched causal continuation for M2 validation and serving. | |
| This deliberately recomputes the context on every step. KV caching is a | |
| later optimization; keeping this reference path simple makes M2 output | |
| semantics straightforward to test. | |
| """ | |
| if max_new_tokens < 0: | |
| raise ValueError("max_new_tokens must be non-negative") | |
| if temperature <= 0: | |
| raise ValueError("temperature must be positive") | |
| if top_k is not None and top_k <= 0: | |
| raise ValueError("top_k must be positive when supplied") | |
| generated = input_ids | |
| finished = torch.zeros(input_ids.size(0), dtype=torch.bool, device=input_ids.device) | |
| for _ in range(max_new_tokens): | |
| next_logits = self(generated).logits[:, -1, :] | |
| if do_sample: | |
| next_logits = next_logits / temperature | |
| if top_k is not None and top_k < next_logits.size(-1): | |
| threshold = torch.topk(next_logits, top_k, dim=-1).values[:, -1:] | |
| next_logits = next_logits.masked_fill(next_logits < threshold, float("-inf")) | |
| next_token = torch.multinomial(torch.softmax(next_logits, dim=-1), 1) | |
| else: | |
| next_token = next_logits.argmax(dim=-1, keepdim=True) | |
| if eos_token_id is not None: | |
| next_token = torch.where( | |
| finished.unsqueeze(1), | |
| torch.full_like(next_token, eos_token_id), | |
| next_token, | |
| ) | |
| finished |= next_token.squeeze(1).eq(eos_token_id) | |
| generated = torch.cat((generated, next_token), dim=1) | |
| if eos_token_id is not None and bool(finished.all()): | |
| break | |
| return generated | |