""" AHA-Qwen3: All-or-Here Attention applied to Qwen3. Minimal modification to standard Qwen3 attention: adds a binary gate that dynamically toggles between full attention and local attention. The persisted ``aha_router_granularity`` config selects either one gate per (token, KV-head) or one gate per token shared by every head in the layer. Based on: transformers.models.qwen3.modeling_qwen3 (Qwen3Attention) Changes: 1. q_proj outputs either one extra gate value (``token``) or ``num_key_value_heads`` values (``token_kv_head``). The shared-token value is broadcast to all KV/query heads before attention mixing. 2. Gate: sigmoid → hard threshold → STE for training 3. Double-mix: global attention + local window attention, blended by gate (broadcast across the GQA group) 4. Auxiliary loss: λ * mean(gate_soft) to encourage sparsity """ import atexit import json import os from dataclasses import dataclass from pathlib import Path from typing import Callable, Optional, Union import torch import torch.nn as nn from safetensors.torch import load_file from transformers import Qwen3Config, Qwen3ForCausalLM from transformers.modeling_layers import GradientCheckpointingLayer from transformers.models.qwen3.modeling_qwen3 import ( Qwen3Attention, Qwen3DecoderLayer, Qwen3Model, Qwen3RMSNorm, apply_rotary_pos_emb, ) from transformers.cache_utils import Cache, DynamicCache from transformers.masking_utils import create_causal_mask, create_sliding_window_causal_mask from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast from transformers.processing_utils import Unpack from transformers.utils.generic import TransformersKwargs # AHA constants AHA_WINDOW_SIZE = 1024 AHA_LAMBDA = 3e-4 AHA_DISTILL_WEIGHT = 0.0 AHA_CE_WEIGHT = 1.0 # Gate-density ceiling (hinge target). The aux loss only fires when # ``mean(gate_soft) > AHA_GATE_TARGET``, so sparsity cannot drop # below ``1 - AHA_GATE_TARGET`` in steady state regardless of how # large ``aha_lambda`` is. Setting this to 1.0 recovers the legacy # behaviour (unconditional downward pressure). AHA_GATE_TARGET = 1.0 # DuoAttention baseline knobs. When ``aha_mode == "duo"`` the per-(token, # kv_head) gate is replaced by a per-(layer, kv_head) learnable scalar # (``duo_alpha``, 224 scalars total for Qwen3-0.6B), and the "local" # branch becomes a streaming mask with ``duo_sink_size`` attention sinks # plus a recent window of ``duo_recent_size``. This is a strict reduction # of the dynamic AHA gate and reproduces the DuoAttention paper's head # classifier (Xiao et al. 2024) on the Qwen3 backbone. AHA_MODE = "dynamic" DUO_SINK_SIZE = 64 DUO_RECENT_SIZE = 256 DUO_ALPHA_INIT = 1.0 # Local-branch mask kind for the dual-branch attention (global vs local). # - "sliding_window": last ``aha_window_size`` tokens, no attention sinks. # Historical default for ``aha_mode="dynamic"`` (Bv3 was trained with this). # - "sink_recent": first ``duo_sink_size`` tokens + last ``duo_recent_size`` # tokens, matching the DuoAttention paper. Historical hardcoded default for # ``aha_mode="duo"`` (still forced for duo regardless of this field). # § 9.4.13.3 found that hot-starting a dynamic-mode ckpt from a duo ckpt with # sliding_window local loses ~20pp LongBench retention vs the duo source, # almost entirely from passage_retrieval_en (sinks are critical for long # passage retrieval). Setting this to "sink_recent" for hot-started dynamic # ckpts closes that architectural gap. AHA_LOCAL_KIND = "sliding_window" AHA_ROUTER_GRANULARITY = "token_kv_head" def aha_router_output_size(config: Qwen3Config) -> int: """Return the number of native dynamic-router logits per token.""" granularity = getattr(config, "aha_router_granularity", AHA_ROUTER_GRANULARITY) if granularity == "token": return 1 if granularity == "token_kv_head": return int(config.num_key_value_heads) raise ValueError(f"Unsupported aha_router_granularity={granularity!r}") class _AHAInferenceSparsityTracker: """Accumulate exact inference hard-route counts without changing outputs.""" def __init__(self) -> None: # Keep counters as device scalars until process exit. Calling .item() # once per layer and decode token serialized CUDA and made the # diagnostic tracker materially slow generation. self.sparse_decisions: dict[int, torch.Tensor] = {} self.total_decisions: dict[int, int] = {} self.phase_sparse_decisions: dict[str, torch.Tensor] = {} self.phase_total_decisions: dict[str, int] = {} raw_thresholds = os.environ.get("AHA_COUNTERFACTUAL_THRESHOLDS", "") self.counterfactual_thresholds = tuple( sorted({float(value) for value in raw_thresholds.split(",") if value.strip()}) ) if any(value < 0.0 or value > 1.0 for value in self.counterfactual_thresholds): raise ValueError("AHA_COUNTERFACTUAL_THRESHOLDS values must be in [0, 1]") self.counterfactual_sparse: dict[float, torch.Tensor] = {} self.router_granularity: Optional[str] = None self.native_router_decisions = 0 atexit.register(self.write_stats) def update( self, gate_hard: torch.Tensor, gate_soft: torch.Tensor, layer_idx: int, phase: str, router_granularity: str, native_router_width: int, ) -> None: if self.router_granularity is None: self.router_granularity = router_granularity elif self.router_granularity != router_granularity: raise RuntimeError( "AHA sparsity tracker received mixed router granularities: " f"{self.router_granularity!r} and {router_granularity!r}" ) sparse = torch.count_nonzero(gate_hard == 0).to(torch.int64) total = int(gate_hard.numel()) effective_width = int(gate_hard.shape[-1]) self.native_router_decisions += total // effective_width * native_router_width if layer_idx not in self.sparse_decisions: self.sparse_decisions[layer_idx] = sparse else: self.sparse_decisions[layer_idx].add_(sparse) self.total_decisions[layer_idx] = self.total_decisions.get(layer_idx, 0) + total if phase not in self.phase_sparse_decisions: self.phase_sparse_decisions[phase] = sparse.clone() else: self.phase_sparse_decisions[phase].add_(sparse) self.phase_total_decisions[phase] = self.phase_total_decisions.get(phase, 0) + total if self.counterfactual_thresholds: flat = gate_soft.detach().reshape(-1, 1) thresholds = flat.new_tensor(self.counterfactual_thresholds).reshape(1, -1) counts = (flat <= thresholds).sum(dim=0, dtype=torch.int64) for threshold, count in zip(self.counterfactual_thresholds, counts.unbind()): if threshold not in self.counterfactual_sparse: self.counterfactual_sparse[threshold] = count else: self.counterfactual_sparse[threshold].add_(count) def write_stats(self) -> None: output = os.environ.get("AHA_SPARSITY_STATS_PATH") if not output: return sparse_by_layer = { layer_idx: int(value.item()) for layer_idx, value in self.sparse_decisions.items() } sparse = sum(sparse_by_layer.values()) total = sum(self.total_decisions.values()) per_layer = {} for layer_idx in sorted(self.total_decisions): layer_sparse = sparse_by_layer.get(layer_idx, 0) layer_total = self.total_decisions[layer_idx] per_layer[str(layer_idx)] = { "sparse_decisions": layer_sparse, "total_decisions": layer_total, "sparsity": layer_sparse / layer_total if layer_total else None, } payload = { "definition": ( "hard AHA gate zeros / all token x KV-head x layer decisions, " "token-weighted over prefill and decode" ), "router_granularity": self.router_granularity, "native_router_decisions": self.native_router_decisions, "effective_router_decisions": total, "native_router_note": ( "Native decisions count learned gate logits before broadcast; effective " "decisions always use token x KV-head x layer for matched sparsity." ), "sparse_decisions": sparse, "total_decisions": total, "sparsity": sparse / total if total else None, "full_attention_usage": 1.0 - sparse / total if total else None, "per_layer": per_layer, "by_phase": { phase: { "sparse_decisions": int(self.phase_sparse_decisions[phase].item()), "total_decisions": phase_total, "sparsity": int(self.phase_sparse_decisions[phase].item()) / phase_total if phase_total else None, "full_attention_usage": 1.0 - int(self.phase_sparse_decisions[phase].item()) / phase_total if phase_total else None, } for phase, phase_total in sorted(self.phase_total_decisions.items()) }, } if self.counterfactual_thresholds: payload["counterfactual_sparsity_by_threshold"] = { f"{threshold:.6g}": { "sparse_decisions": int(self.counterfactual_sparse[threshold].item()), "total_decisions": total, "sparsity": int(self.counterfactual_sparse[threshold].item()) / total if total else None, } for threshold in self.counterfactual_thresholds } payload["counterfactual_note"] = ( "Routing counts only; benchmark quality must be measured in a separate " "run with AHA_GATE_HARD_THRESHOLD set to the selected threshold." ) path = Path(output) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload, indent=2) + "\n") _AHA_INFERENCE_SPARSITY_TRACKER: Optional[_AHAInferenceSparsityTracker] = None def _track_aha_inference_sparsity( gate_hard: torch.Tensor, gate_soft: torch.Tensor, layer_idx: int, phase: str, router_granularity: str, native_router_width: int, ) -> None: global _AHA_INFERENCE_SPARSITY_TRACKER if not os.environ.get("AHA_SPARSITY_STATS_PATH"): return if _AHA_INFERENCE_SPARSITY_TRACKER is None: _AHA_INFERENCE_SPARSITY_TRACKER = _AHAInferenceSparsityTracker() _AHA_INFERENCE_SPARSITY_TRACKER.update( gate_hard.detach(), gate_soft.detach(), layer_idx, phase, router_granularity, native_router_width, ) def _get_optional_nonnegative_int_env(name: str) -> Optional[int]: raw = os.environ.get(name, "").strip() if not raw: return None value = int(raw) if value < 0: raise ValueError(f"{name} must be non-negative, got {value}") return value def _apply_low_alpha_force_full_heads_from_env(model: nn.Module) -> None: """Auto-protect the weakest static-full heads in duo_dynamic mode. ``AHA_FORCE_LOW_ALPHA_FULL_HEADS=K`` selects the K smallest ``full_attention_heads`` values among heads that static Duo still kept full (alpha > 0.5). These are the fragile boundary full heads: raising the dynamic threshold can close them, but static Duo's alpha says they should not be fully discarded. The selected heads are OR-ed with any manually supplied ``AHA_FORCE_FULL_HEADS`` mask. """ k = _get_optional_nonnegative_int_env("AHA_FORCE_LOW_ALPHA_FULL_HEADS") if not k: return if getattr(model.config, "aha_mode", "dynamic") != "duo_dynamic": return candidates: list[tuple[float, int, int]] = [] with torch.no_grad(): for layer_idx, layer in enumerate(model.model.layers): attn = layer.self_attn alpha = getattr(attn, "full_attention_heads", None) if alpha is None: continue for head_idx, value in enumerate(alpha.detach().float().cpu().tolist()): if value > 0.5: candidates.append((float(value), layer_idx, head_idx)) selected = sorted(candidates)[: min(k, len(candidates))] if not selected: return selected_by_layer: dict[int, list[int]] = {} for _, layer_idx, head_idx in selected: selected_by_layer.setdefault(layer_idx, []).append(head_idx) with torch.no_grad(): for layer_idx, heads in selected_by_layer.items(): mask = model.model.layers[layer_idx].self_attn._aha_force_full_heads_mask for head_idx in heads: mask[head_idx] = True model.config.aha_force_low_alpha_full_heads = int(k) formatted = ",".join(f"{layer}:{head}" for _, layer, head in selected) print( f"[AHA] AHA_FORCE_LOW_ALPHA_FULL_HEADS={k} selected {len(selected)} " f"duo_dynamic heads: {formatted}", flush=True, ) class AHAQwen3Config(Qwen3Config): """Qwen3Config with AHA-specific parameters.""" model_type = "aha_qwen3" def __init__( self, aha_window_size=AHA_WINDOW_SIZE, aha_lambda=AHA_LAMBDA, aha_distill_weight=AHA_DISTILL_WEIGHT, aha_ce_weight=AHA_CE_WEIGHT, aha_gate_target=AHA_GATE_TARGET, aha_reg_weight=-1.0, aha_force_gate_value=None, aha_mode=AHA_MODE, duo_sink_size=DUO_SINK_SIZE, duo_recent_size=DUO_RECENT_SIZE, duo_alpha_init=DUO_ALPHA_INIT, aha_local_kind=AHA_LOCAL_KIND, aha_router_granularity=AHA_ROUTER_GRANULARITY, **kwargs, ): super().__init__(**kwargs) self.aha_window_size = aha_window_size self.aha_lambda = aha_lambda # Per-layer attention distillation weight. When > 0 and model is # training, each layer contributes ``mean(((1-g)·(global-local))**2)`` # to the total loss. This is a dense per-(token, head) signal that # pushes the gate toward 1 where SWA is insufficient to approximate # full attention. Cost: ~0 extra forward compute (global/local are # already computed for the mix). self.aha_distill_weight = aha_distill_weight # Language-modeling CE weight for the training objective. Default 1.0 # keeps the standard SFT behaviour. Set to 0.0 to drop CE entirely # and train the gate purely from ``aux + distill`` (useful when the # backbone is frozen and we only want to shape the gate). Evaluation # always reports unweighted ``ce_loss`` regardless of this setting. self.aha_ce_weight = aha_ce_weight # Direct Duo-style sparsity regularizer. When >= 0, this replaces the # legacy hinge term and uses ``aha_reg_weight * mean(gate_soft)``. # Default -1 keeps older checkpoints on their original hinge objective. self.aha_reg_weight = float(aha_reg_weight) # Optional override used by Duo-style dynamic distillation. When set # to 1.0, dynamic mode behaves as a full-attention teacher for one # forward; None keeps the learned gate. self.aha_force_gate_value = aha_force_gate_value # Target ceiling on mean(gate_soft). Aux loss is hinge-shaped: # ``λ · max(0, mean(gate_soft) - τ)``. Setting τ < 1.0 guarantees # the gate cannot drop below ``1 - τ`` local-attention fraction # in steady state -- the failure mode we observed with the legacy # unconditional aux, where ``λ = 1.0`` drove mean(gate_hard) to # 0.005 (99.5% local) and killed long-context accuracy # (gsm8k 74% -> 19%). Default 1.0 is a no-op for backward # compatibility with checkpoints trained on the old loss. self.aha_gate_target = aha_gate_target # DuoAttention-style baseline. ``dynamic`` keeps the original per- # (token, kv_head) MLP gate. ``duo`` replaces it with a per-(layer, # kv_head) learnable scalar and swaps the local branch's mask for a # streaming (sink + recent) pattern. ``duo_dynamic`` keeps the Duo # static mask for KV-capacity planning, but applies a dynamic AHA gate # only on the static-full heads: effective_full = duo_full AND dyn_gate. # Sink / recent sizes are fixed at train time; set ``duo_sink_size=0`` # for the sink-ablation run. if aha_mode not in ("dynamic", "duo", "duo_dynamic"): raise ValueError( f"aha_mode must be 'dynamic', 'duo', or 'duo_dynamic', got {aha_mode!r}" ) self.aha_mode = aha_mode if aha_router_granularity not in ("token", "token_kv_head"): raise ValueError( "aha_router_granularity must be 'token' or 'token_kv_head', " f"got {aha_router_granularity!r}" ) # Persist this even for static Duo checkpoints. Dynamic AHA uses it to # choose the native q_proj gate-row count; old configs omit the field # and therefore retain the historical token_kv_head behavior. self.aha_router_granularity = aha_router_granularity self.duo_sink_size = int(duo_sink_size) self.duo_recent_size = int(duo_recent_size) self.duo_alpha_init = float(duo_alpha_init) if aha_local_kind not in ("sliding_window", "sink_recent"): raise ValueError( f"aha_local_kind must be 'sliding_window' or 'sink_recent', " f"got {aha_local_kind!r}" ) # When aha_mode="duo" the local branch is always sink+recent regardless # of this field (forced to the historical DuoAttention behaviour). The # field only controls the dynamic mode's local mask. self.aha_local_kind = aha_local_kind def _build_streaming_causal_mask( seq_len_q: int, seq_len_kv: int, sink_size: int, recent_size: int, device: torch.device, dtype: torch.dtype, kv_offset: int = 0, ) -> torch.Tensor: """Construct a streaming attention mask (sink + recent, causal). Returns an additive mask of shape ``[1, 1, Q, K]`` where disallowed positions are ``finfo(dtype).min`` and allowed positions are ``0``. A query at position ``q`` (absolute in the sequence, with ``kv_offset`` tokens already in the KV cache) attends to a key at position ``k`` iff ``k <= q_abs`` AND (``k < sink_size`` OR ``q_abs - k < recent_size``). """ q_abs = torch.arange(seq_len_q, device=device).unsqueeze(-1) + kv_offset # [Q, 1] k_abs = torch.arange(seq_len_kv, device=device).unsqueeze(0) # [1, K] causal = k_abs <= q_abs sink = k_abs < sink_size if sink_size > 0 else torch.zeros_like(causal) recent = (q_abs - k_abs) < recent_size allowed = causal & (sink | recent) mask = torch.zeros(seq_len_q, seq_len_kv, dtype=dtype, device=device) mask.masked_fill_(~allowed, torch.finfo(dtype).min) return mask.unsqueeze(0).unsqueeze(0) class AHAQwen3Attention(nn.Module): """Qwen3 attention with AHA gate for per-head local/global routing.""" def __init__(self, config: AHAQwen3Config, layer_idx: int): super().__init__() self.config = config self.layer_idx = layer_idx self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads) self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads self.scaling = self.head_dim ** -0.5 self.attention_dropout = config.attention_dropout self.is_causal = True self.aha_window_size = config.aha_window_size # q_proj: extra router outputs for either one shared per-token gate or # one per-(token, KV-head) gate. # DUO mode uses a per-(layer, kv_head) static scalar instead, so # q_proj keeps its vanilla Qwen3 shape and is binary-compatible # with the base model checkpoint. self._has_dyn_gate_logits = getattr(config, "aha_mode", "dynamic") in ( "dynamic", "duo_dynamic", ) self.aha_router_outputs = aha_router_output_size(config) q_out_dim = config.num_attention_heads * self.head_dim if self._has_dyn_gate_logits: q_out_dim += self.aha_router_outputs self.q_proj = nn.Linear( config.hidden_size, q_out_dim, bias=config.attention_bias, ) self.k_proj = nn.Linear( config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias ) self.v_proj = nn.Linear( config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias ) self.o_proj = nn.Linear( config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias ) # Qwen3 applies q_norm/k_norm per head_dim (not full projection) self.q_norm = Qwen3RMSNorm(self.head_dim, eps=config.rms_norm_eps) self.k_norm = Qwen3RMSNorm(self.head_dim, eps=config.rms_norm_eps) # DuoAttention baseline: per-(layer, kv_head) learnable scalar. # Populated in ``duo`` and ``duo_dynamic``. In ``duo_dynamic`` this # scalar is the frozen Duo capacity mask; the dynamic gate is only # allowed to close heads where this scalar says "full". Registered # as ``full_attention_heads`` to match the name used in the # official DuoAttention repo (``duo_attn.patch.llama``), so # per-head statistics (e.g. retrieval heads vs streaming heads) # can be read back with the same tooling. if getattr(config, "aha_mode", "dynamic") in ("duo", "duo_dynamic"): init = float(getattr(config, "duo_alpha_init", 1.0)) self.full_attention_heads = nn.Parameter( torch.full((config.num_key_value_heads,), init, dtype=torch.float32) ) else: self.full_attention_heads = None force_full_mask = torch.zeros(config.num_key_value_heads, dtype=torch.bool) for raw in os.environ.get("AHA_FORCE_FULL_HEADS", "").split(","): raw = raw.strip() if not raw: continue layer_s, head_s = raw.split(":", 1) if int(layer_s) == int(layer_idx): force_full_mask[int(head_s)] = True self.register_buffer( "_aha_force_full_heads_mask", force_full_mask, persistent=False, ) def forward( self, hidden_states: torch.Tensor, position_embeddings: tuple[torch.Tensor, torch.Tensor], attention_mask: Optional[dict], # dict with "global" and "local" keys past_key_values: Optional[Cache] = None, cache_position: Optional[torch.LongTensor] = None, **kwargs, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, Optional[torch.Tensor]]: input_shape = hidden_states.shape[:-1] hidden_shape = (*input_shape, -1, self.head_dim) # === Q projection + (optional) gate extraction === q_proj_out = self.q_proj(hidden_states) if self._has_dyn_gate_logits: query_states, gate = torch.split( q_proj_out, [ self.config.num_attention_heads * self.head_dim, self.aha_router_outputs, ], dim=-1, ) else: query_states = q_proj_out # DUO mode: use a zero placeholder so downstream dtype/device # checks stay unchanged. The actual gate comes from the # per-kv-head scalar ``full_attention_heads`` below. gate = torch.zeros( *input_shape, self.config.num_key_value_heads, dtype=query_states.dtype, device=query_states.device, ) # Qwen3-style: norm then reshape then transpose query_states = self.q_norm(query_states.view(hidden_shape)).transpose(1, 2) key_states = self.k_norm(self.k_proj(hidden_states).view(hidden_shape)).transpose(1, 2) value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2) cos, sin = position_embeddings query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) if past_key_values is not None: cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position} key_states, value_states = past_key_values.update( key_states, value_states, self.layer_idx, cache_kwargs ) # === Gate computation === # gate shape: (B, Q, H_kv) — per-(token, kv_head) in dynamic mode, # or per-kv_head broadcast to (B, Q, H_kv) in DUO mode. # At inference, threshold is read from env var AHA_GATE_HARD_THRESHOLD # so users can trade sparsity for quality without re-training. # Default 0.5 keeps training-time behaviour. Lowering to e.g. 0.35 # lets more (token, kv_head) pairs route through global attention, # which recovers long-CoT quality at the cost of sparsity. aha_mode = getattr(self.config, "aha_mode", "dynamic") if aha_mode == "duo": # DuoAttention: alpha is per-(layer, kv_head) learnable scalar. # gate_soft = clamp(alpha, 0, 1); gate_hard = (alpha > 0.5). # Both broadcast to (B, Q, H_kv) for a uniform downstream API # (so the distill / aux / blend paths do not need to branch). qhead_alpha = getattr(self, "_aha_qhead_full_attention_heads", None) if qhead_alpha is not None: # Eval-only oracle diagnostic: route at Q-head granularity # instead of KV-head/GQA-group granularity. This does not # represent a KV-capacity-saving deployment point; it isolates # whether GQA grouping itself is the quality bottleneck. alpha = qhead_alpha.to(gate.dtype).clamp(0.0, 1.0) gate_soft = alpha.view(1, 1, -1).expand(*input_shape, -1) import os as _os _thr = float(_os.environ.get("AHA_GATE_HARD_THRESHOLD", "0.5")) gate_hard = (alpha > _thr).to(gate.dtype).view(1, 1, -1).expand(*input_shape, -1) gate_ste = gate_hard else: alpha = self.full_attention_heads.to(gate.dtype).clamp(0.0, 1.0) gate_soft = alpha.view(1, 1, -1).expand(*input_shape, -1) if self.training: gate_hard = (alpha > 0.5).to(gate.dtype).view(1, 1, -1).expand(*input_shape, -1) # Straight-through estimator through the clamped alpha so # L1 / distill gradients flow into the scalar. gate_ste = gate_soft else: import os as _os _thr = float(_os.environ.get("AHA_GATE_HARD_THRESHOLD", "0.5")) gate_hard = (alpha > _thr).to(gate.dtype).view(1, 1, -1).expand(*input_shape, -1) gate_ste = gate_hard elif aha_mode == "duo_dynamic": # Duo + AHA-on-full-heads: # duo_full_mask = 1 for static Duo full/retrieval heads # dyn_gate = per-token AHA decision inside those heads # effective_full = duo_full_mask AND dyn_gate # # Duo streaming heads are locked to local/streaming forever. This # preserves Duo's KV-capacity saving: only static-full heads need a # full KV cache. ``aha_force_gate_value=1`` therefore reproduces # the static Duo deployment point exactly, not all-full attention. alpha = self.full_attention_heads.to(gate.dtype).clamp(0.0, 1.0) duo_full = (alpha > 0.5).to(gate.dtype).view(1, 1, -1) dyn_soft = torch.sigmoid(gate) force_gate_value = getattr(self.config, "aha_force_gate_value", None) if force_gate_value is not None: dyn_soft = torch.full_like(dyn_soft, float(force_gate_value)) gate_soft = dyn_soft * duo_full gate_hard = gate_soft gate_ste = gate_soft elif self.training: train_thr = float(os.environ.get("AHA_TRAIN_GATE_HARD_THRESHOLD", "0.5")) gate_soft = dyn_soft * duo_full dyn_hard = (dyn_soft > train_thr).to(gate.dtype) gate_hard = dyn_hard * duo_full gate_ste = gate_hard + (gate_soft - gate_soft.detach()) else: import os as _os _thr = float(_os.environ.get("AHA_GATE_HARD_THRESHOLD", "0.5")) gate_soft = dyn_soft * duo_full dyn_hard = (dyn_soft > _thr).to(gate.dtype) gate_hard = dyn_hard * duo_full gate_ste = gate_hard force_full = self._aha_force_full_heads_mask.to(gate.dtype).view(1, 1, -1) if bool(force_full.any().item()): force_full = force_full * duo_full gate_hard = torch.maximum(gate_hard, force_full) gate_ste = torch.maximum(gate_ste, force_full) else: gate_soft = torch.sigmoid(gate) force_gate_value = getattr(self.config, "aha_force_gate_value", None) if force_gate_value is not None: force = float(force_gate_value) gate_soft = torch.full_like(gate_soft, force) gate_hard = torch.full_like(gate_soft, force) gate_ste = gate_soft elif self.training: train_thr = float(os.environ.get("AHA_TRAIN_GATE_HARD_THRESHOLD", "0.5")) gate_hard = (gate_soft > train_thr).to(gate.dtype) gate_ste = gate_hard + (gate_soft - gate_soft.detach()) else: import os as _os _thr = float(_os.environ.get("AHA_GATE_HARD_THRESHOLD", "0.5")) gate_hard = (gate_soft > _thr).to(gate.dtype) gate_ste = gate_hard # Normalize the downstream contract to effective # token x KV-head decisions. In shared-token mode there is only one # learned logit per token, but regularization, sparsity accounting, and # attention mixing all see the same KV-head denominator as the # head-granular arm. if gate_soft.shape[-1] == 1 and self.config.num_key_value_heads != 1: effective_shape = (*gate_soft.shape[:-1], self.config.num_key_value_heads) gate_soft = gate_soft.expand(effective_shape) gate_hard = gate_hard.expand(effective_shape) gate_ste = gate_ste.expand(effective_shape) if ( aha_mode in ("duo", "duo_dynamic") and not self.training and os.environ.get("AHA_DUO_PREFILL_FULL", "0") == "1" and query_states.shape[-2] == key_states.shape[-2] ): # Diagnostic parity with the official DuoAttention HF eval path: # initial prefill is full attention; streaming heads are only # localized during subsequent decode steps. gate_hard = torch.ones_like(gate_hard) gate_ste = torch.ones_like(gate_ste) if not self.training: phase = ( "decode" if hidden_states.shape[1] == 1 and key_states.shape[-2] > 1 else "prefill" ) _track_aha_inference_sparsity( gate_hard, gate_soft, self.layer_idx, phase, getattr( self.config, "aha_router_granularity", AHA_ROUTER_GRANULARITY, ), self.aha_router_outputs if self._has_dyn_gate_logits else self.config.num_key_value_heads, ) # === Attention: double_mix (global + local, blended by gate) === from transformers.models.qwen3.modeling_qwen3 import eager_attention_forward attention_interface: Callable = eager_attention_forward if self.config._attn_implementation != "eager": attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation] # Global attention (full context) global_attn_output, _ = attention_interface( self, query_states, key_states, value_states, attention_mask["global"], dropout=0.0 if not self.training else self.attention_dropout, scaling=self.scaling, **kwargs, ) if getattr(self.config, "_aha_teacher_full_fastpath", False): attn_output = global_attn_output.reshape(*input_shape, -1).contiguous() attn_output = self.o_proj(attn_output) return attn_output, gate_soft, gate_hard, None # Local / streaming branch. The local mask was pre-built by the # model's ``forward`` either as sliding-window (legacy dynamic mode # default) or sink+recent (duo mode, and dynamic mode when # ``aha_local_kind="sink_recent"``). Only pass ``sliding_window`` to # the SDPA backend when the mask is the sliding-window kind, otherwise # the backend will double-mask and wipe out the sink positions. local_attn_kwargs = dict( dropout=0.0 if not self.training else self.attention_dropout, scaling=self.scaling, ) local_kind = "sink_recent" if aha_mode in ("duo", "duo_dynamic") else getattr( self.config, "aha_local_kind", "sliding_window" ) if local_kind == "sliding_window": local_attn_kwargs["sliding_window"] = self.aha_window_size local_attn_output, _ = attention_interface( self, query_states, key_states, value_states, attention_mask["local"], **local_attn_kwargs, **kwargs, ) # Blend: normal gate_ste is (B, Q, H_kv); broadcast to (B, Q, H, 1) # by repeating each kv_head decision. The optional qhead oracle above # already emits (B, Q, H), so it bypasses this broadcast. if gate_ste.shape[-1] == self.config.num_attention_heads: g_bqh = gate_ste else: g_bqh = gate_ste.repeat_interleave(self.num_key_value_groups, dim=-1) attn_output = ( global_attn_output * g_bqh.unsqueeze(-1) + local_attn_output * (1 - g_bqh.unsqueeze(-1)) ) # Per-layer attention distillation signal. Returns a scalar so the # outer model can aggregate across layers. When gate → 1 the term # vanishes; when gate → 0 on positions where global/local disagree # strongly, the gradient pushes gate back toward 1. # # ``diff`` is ``detach``-ed so distill only trains the gate; it does # not reshape attention outputs through the backbone. This also # prevents the squared-difference graph from retaining activations # for all 28 layers' global/local branches, which was the source of # OOM when gradient checkpointing is disabled on the custom forward. distill_per_layer: Optional[torch.Tensor] = None if self.training and getattr(self.config, "aha_distill_weight", 0.0) > 0.0: with torch.no_grad(): diff_sq = (global_attn_output - local_attn_output).float() ** 2 one_minus_g_sq = ((1.0 - g_bqh.float()) ** 2).unsqueeze(-1) distill_per_layer = (one_minus_g_sq * diff_sq).mean() attn_output = attn_output.reshape(*input_shape, -1).contiguous() attn_output = self.o_proj(attn_output) return attn_output, gate_soft, gate_hard, distill_per_layer class AHAQwen3DecoderLayer(GradientCheckpointingLayer): """Decoder layer with gradient-checkpointing support. Inheriting from ``GradientCheckpointingLayer`` lets HuggingFace Trainer's ``gradient_checkpointing_enable`` take effect on this layer without any explicit ``_gradient_checkpointing_func`` call in the outer forward. """ def __init__(self, config: AHAQwen3Config, layer_idx: int): super().__init__() self.hidden_size = config.hidden_size self.self_attn = AHAQwen3Attention(config=config, layer_idx=layer_idx) from transformers.models.qwen3.modeling_qwen3 import Qwen3MLP self.mlp = Qwen3MLP(config) self.input_layernorm = Qwen3RMSNorm(config.hidden_size, eps=config.rms_norm_eps) self.post_attention_layernorm = Qwen3RMSNorm(config.hidden_size, eps=config.rms_norm_eps) def forward( self, hidden_states: torch.Tensor, attention_mask: Optional[dict] = None, position_ids: Optional[torch.LongTensor] = None, past_key_values: Optional[Cache] = None, use_cache: Optional[bool] = False, cache_position: Optional[torch.LongTensor] = None, position_embeddings: Optional[tuple[torch.Tensor, torch.Tensor]] = None, **kwargs, ): residual = hidden_states hidden_states = self.input_layernorm(hidden_states) hidden_states, gate_soft, gate_hard, distill_per_layer = self.self_attn( hidden_states=hidden_states, attention_mask=attention_mask, position_ids=position_ids, past_key_values=past_key_values, use_cache=use_cache, cache_position=cache_position, position_embeddings=position_embeddings, **kwargs, ) hidden_states = residual + hidden_states residual = hidden_states hidden_states = self.post_attention_layernorm(hidden_states) hidden_states = self.mlp(hidden_states) hidden_states = residual + hidden_states return hidden_states, gate_soft, gate_hard, distill_per_layer @dataclass class AHACausalLMOutputWithPast(CausalLMOutputWithPast): """Causal LM output with AHA loss breakdown and gate density stats (training / eval with labels).""" ce_loss: Optional[torch.FloatTensor] = None gate_aux_loss: Optional[torch.FloatTensor] = None distill_loss: Optional[torch.FloatTensor] = None gate_soft_mean: Optional[torch.FloatTensor] = None gate_hard_mean: Optional[torch.FloatTensor] = None class AHAModelOutputWithPast(BaseModelOutputWithPast): def __init__(self, last_hidden_state, past_key_values=None, hidden_states=None, attentions=None, all_gate_soft=None, all_gate_hard=None, all_distill_per_layer=None): super().__init__(last_hidden_state=last_hidden_state, past_key_values=past_key_values, hidden_states=hidden_states, attentions=attentions) self.all_gate_soft = all_gate_soft self.all_gate_hard = all_gate_hard # Tuple of per-layer distill scalars, or empty tuple when distillation is off. self.all_distill_per_layer = all_distill_per_layer if all_distill_per_layer is not None else () class AHAQwen3Model(Qwen3Model): config_class = AHAQwen3Config def __init__(self, config: AHAQwen3Config): from transformers import PreTrainedModel PreTrainedModel.__init__(self, config) self.padding_idx = config.pad_token_id self.vocab_size = config.vocab_size self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx) self.layers = nn.ModuleList( [AHAQwen3DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)] ) self.norm = Qwen3RMSNorm(config.hidden_size, eps=config.rms_norm_eps) from transformers.models.qwen3.modeling_qwen3 import Qwen3RotaryEmbedding self.rotary_emb = Qwen3RotaryEmbedding(config=config) self.gradient_checkpointing = False self.post_init() def forward( self, input_ids: Optional[torch.LongTensor] = None, attention_mask: Optional[torch.Tensor] = None, position_ids: Optional[torch.LongTensor] = None, past_key_values: Optional[Cache] = None, inputs_embeds: Optional[torch.FloatTensor] = None, use_cache: Optional[bool] = None, cache_position: Optional[torch.LongTensor] = None, **kwargs, ) -> AHAModelOutputWithPast: if (input_ids is None) ^ (inputs_embeds is not None): raise ValueError("You must specify exactly one of input_ids or inputs_embeds") if inputs_embeds is None: inputs_embeds = self.embed_tokens(input_ids) if use_cache and past_key_values is None: past_key_values = DynamicCache(config=self.config) if cache_position is None: past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0 cache_position = torch.arange( past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device ) if position_ids is None: position_ids = cache_position.unsqueeze(0) # Build two masks: global (full causal) and local (sliding window # for dynamic mode, streaming sink+recent for DUO mode). orig_sliding_window = getattr(self.config, "sliding_window", None) self.config.sliding_window = self.config.aha_window_size mask_kwargs = { "config": self.config, "input_embeds": inputs_embeds, "attention_mask": attention_mask, "cache_position": cache_position, "past_key_values": past_key_values, "position_ids": position_ids, } global_mask = create_causal_mask(**mask_kwargs) aha_mode = getattr(self.config, "aha_mode", "dynamic") # duo / duo_dynamic force sink_recent for backward compat. dynamic mode # reads the local kind from config (default sliding_window for legacy # Bv3 ckpts; hot-started ckpts from duo set sink_recent — see # §9.4.13.3). local_kind = "sink_recent" if aha_mode in ("duo", "duo_dynamic") else getattr( self.config, "aha_local_kind", "sliding_window" ) if local_kind == "sink_recent": seq_len_q = inputs_embeds.shape[1] past_seen = past_key_values.get_seq_length() if past_key_values is not None else 0 seq_len_kv = seq_len_q + past_seen local_mask = _build_streaming_causal_mask( seq_len_q=seq_len_q, seq_len_kv=seq_len_kv, sink_size=int(getattr(self.config, "duo_sink_size", 64)), recent_size=int(getattr(self.config, "duo_recent_size", 256)), device=inputs_embeds.device, dtype=inputs_embeds.dtype, kv_offset=past_seen, ) else: local_mask = create_sliding_window_causal_mask(**mask_kwargs) causal_mask_dict = {"global": global_mask, "local": local_mask} self.config.sliding_window = orig_sliding_window hidden_states = inputs_embeds position_embeddings = self.rotary_emb(hidden_states, position_ids) all_gate_soft = () all_gate_hard = () all_distill_per_layer: tuple[torch.Tensor, ...] = () for decoder_layer in self.layers: hidden_states, gate_soft, gate_hard, distill_per_layer = decoder_layer( hidden_states, attention_mask=causal_mask_dict, position_ids=position_ids, past_key_values=past_key_values, use_cache=use_cache, cache_position=cache_position, position_embeddings=position_embeddings, **kwargs, ) all_gate_soft += (gate_soft,) all_gate_hard += (gate_hard,) if distill_per_layer is not None: all_distill_per_layer += (distill_per_layer,) hidden_states = self.norm(hidden_states) return AHAModelOutputWithPast( last_hidden_state=hidden_states, past_key_values=past_key_values if use_cache else None, all_gate_soft=all_gate_soft, all_gate_hard=all_gate_hard, all_distill_per_layer=all_distill_per_layer, ) class AHAQwen3ForCausalLM(Qwen3ForCausalLM): config_class = AHAQwen3Config _tied_weights_keys = ["lm_head.weight"] def __init__(self, config: AHAQwen3Config): # Skip Qwen3ForCausalLM.__init__ which creates Qwen3Model # Go to PreTrainedModel.__init__ directly from transformers import PreTrainedModel PreTrainedModel.__init__(self, config) self.model = AHAQwen3Model(config) self.vocab_size = config.vocab_size self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) self.post_init() def forward( self, input_ids: Optional[torch.LongTensor] = None, attention_mask: Optional[torch.Tensor] = None, position_ids: Optional[torch.LongTensor] = None, past_key_values: Optional[Cache] = None, inputs_embeds: Optional[torch.FloatTensor] = None, labels: Optional[torch.LongTensor] = None, use_cache: Optional[bool] = None, cache_position: Optional[torch.LongTensor] = None, logits_to_keep: Union[int, torch.Tensor] = 0, **kwargs, ) -> AHACausalLMOutputWithPast: outputs = self.model( input_ids=input_ids, attention_mask=attention_mask, position_ids=position_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, use_cache=use_cache, cache_position=cache_position, **kwargs, ) hidden_states = outputs.last_hidden_state slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep logits = self.lm_head(hidden_states[:, slice_indices, :]) loss = None ce_loss = None gate_aux_loss = None distill_loss = None gate_soft_mean = None gate_hard_mean = None if labels is not None: from torch.nn import CrossEntropyLoss shift_logits = logits[..., :-1, :].contiguous() shift_labels = labels[..., 1:].contiguous() loss_fct = CrossEntropyLoss() ce_loss = loss_fct(shift_logits.view(-1, self.vocab_size), shift_labels.view(-1)) all_gate_soft = outputs.all_gate_soft all_gate_hard = outputs.all_gate_hard if labels is not None and len(all_gate_soft) > 0 and len(all_gate_hard) > 0: gate_soft_stack = torch.stack( [g.to(hidden_states.device) for g in all_gate_soft], dim=-1 ).float() gate_hard_stack = torch.stack( [g.to(hidden_states.device).float() for g in all_gate_hard], dim=-1 ) shift_gate_soft = gate_soft_stack[:, :-1, :].contiguous() shift_gate_hard = gate_hard_stack[:, :-1, :].contiguous() # Match the CE/PPL population exactly. The old unmasked mean # counted padded sequence positions as real gate decisions, which # made the training sparsity curve disagree with inference-time # sparsity on variable-length batches. valid_gate = shift_labels.ne(-100) if attention_mask is not None: valid_gate = valid_gate & attention_mask[:, 1:].to(torch.bool) if bool(valid_gate.any()): valid_gate = valid_gate[..., None, None].to(shift_gate_soft.dtype) normalizer = ( valid_gate.sum() * shift_gate_soft.shape[-2] * shift_gate_soft.shape[-1] ) gate_soft_mean = (shift_gate_soft * valid_gate).sum() / normalizer gate_hard_mean = (shift_gate_hard * valid_gate).sum() / normalizer else: gate_soft_mean = shift_gate_soft.mean() gate_hard_mean = shift_gate_hard.mean() # Per-layer distillation loss: mean over layers of # ``mean(((1-g)·(global-local))**2)``. Populated only when training # with ``config.aha_distill_weight > 0`` (see AHAQwen3Attention). all_distill = outputs.all_distill_per_layer if self.training and len(all_distill) > 0: distill_loss = torch.stack(list(all_distill)).mean() # === Loss assembly === # Eval path: standard LM loss for compatibility with HF metrics. # Train path: weighted sum of (ce, gate_aux, distill) so callers can # zero any term by setting its weight to 0 (e.g. ``aha_ce_weight=0`` # to train the gate purely from ``aux + distill``). if not self.training: loss = ce_loss else: ce_weight = float(getattr(self.config, "aha_ce_weight", 1.0)) distill_weight = float(getattr(self.config, "aha_distill_weight", 0.0)) terms: list[torch.Tensor] = [] if ce_loss is not None and ce_weight > 0.0: terms.append(ce_weight * ce_loss) if gate_soft_mean is not None: reg_weight = float(getattr(self.config, "aha_reg_weight", -1.0)) if reg_weight >= 0.0: # Duo-style direct sparsity term: # alpha/gate near 1 means full/global attention, so penalize # mean gate usage directly. gate_aux_loss = reg_weight * gate_soft_mean else: # Legacy hinge aux: penalise only the excess above the target # ceiling. Below ``τ`` the gradient is zero, so CE and # distill freely shape where the gate opens without an # unbounded downward push from aux. See AHAQwen3Config # for the rationale and the collapse that motivated it. gate_target = float(getattr(self.config, "aha_gate_target", 1.0)) excess = torch.clamp(gate_soft_mean - gate_target, min=0.0) gate_aux_loss = self.config.aha_lambda * excess terms.append(gate_aux_loss) if distill_loss is not None and distill_weight > 0.0: terms.append(distill_weight * distill_loss) if terms: loss = terms[0] for t in terms[1:]: loss = loss + t return AHACausalLMOutputWithPast( loss=loss, logits=logits, past_key_values=outputs.past_key_values, ce_loss=ce_loss, gate_aux_loss=gate_aux_loss, distill_loss=distill_loss, gate_soft_mean=gate_soft_mean, gate_hard_mean=gate_hard_mean, ) @classmethod def from_pretrained_qwen3(cls, qwen3_path: str, aha_window_size: int = AHA_WINDOW_SIZE, aha_lambda: float = AHA_LAMBDA, aha_distill_weight: float = AHA_DISTILL_WEIGHT, aha_ce_weight: float = AHA_CE_WEIGHT, aha_gate_target: float = AHA_GATE_TARGET, aha_reg_weight: float = -1.0, aha_force_gate_value=None, aha_mode: str = AHA_MODE, duo_sink_size: int = DUO_SINK_SIZE, duo_recent_size: int = DUO_RECENT_SIZE, duo_alpha_init: float = DUO_ALPHA_INIT, aha_local_kind: str = AHA_LOCAL_KIND, aha_router_granularity: str = AHA_ROUTER_GRANULARITY, **kwargs): """Load a pretrained Qwen3 model and convert to AHA-Qwen3. Initializes the gate weights in q_proj randomly (the extra num_heads outputs). All other weights are copied from the pretrained model. """ # Load original Qwen3 original = Qwen3ForCausalLM.from_pretrained(qwen3_path, **kwargs) original_config = original.config # Create AHA config config_dict = original_config.to_dict() config_dict["aha_window_size"] = aha_window_size config_dict["aha_lambda"] = aha_lambda config_dict["aha_distill_weight"] = aha_distill_weight config_dict["aha_ce_weight"] = aha_ce_weight config_dict["aha_gate_target"] = aha_gate_target config_dict["aha_reg_weight"] = aha_reg_weight config_dict["aha_force_gate_value"] = aha_force_gate_value config_dict["aha_mode"] = aha_mode config_dict["duo_sink_size"] = duo_sink_size config_dict["duo_recent_size"] = duo_recent_size config_dict["duo_alpha_init"] = duo_alpha_init config_dict["aha_local_kind"] = aha_local_kind config_dict["aha_router_granularity"] = aha_router_granularity config_dict["model_type"] = "aha_qwen3" aha_config = AHAQwen3Config(**config_dict) aha_config._attn_implementation = kwargs.get("attn_implementation", "sdpa") # Create AHA model aha_model = cls(aha_config) # Copy weights # embed_tokens, lm_head, norm aha_model.model.embed_tokens.load_state_dict(original.model.embed_tokens.state_dict()) aha_model.lm_head.load_state_dict(original.lm_head.state_dict()) aha_model.model.norm.load_state_dict(original.model.norm.state_dict()) aha_model.model.rotary_emb.load_state_dict(original.model.rotary_emb.state_dict()) # Copy per-layer weights num_heads = original_config.num_attention_heads head_dim = aha_config.head_dim for i in range(original_config.num_hidden_layers): orig_layer = original.model.layers[i] aha_layer = aha_model.model.layers[i] # MLP + norms: direct copy aha_layer.mlp.load_state_dict(orig_layer.mlp.state_dict()) aha_layer.input_layernorm.load_state_dict(orig_layer.input_layernorm.state_dict()) aha_layer.post_attention_layernorm.load_state_dict(orig_layer.post_attention_layernorm.state_dict()) # Attention: k_proj, v_proj, o_proj, q_norm, k_norm — direct copy aha_attn = aha_layer.self_attn orig_attn = orig_layer.self_attn aha_attn.k_proj.load_state_dict(orig_attn.k_proj.state_dict()) aha_attn.v_proj.load_state_dict(orig_attn.v_proj.state_dict()) aha_attn.o_proj.load_state_dict(orig_attn.o_proj.state_dict()) aha_attn.q_norm.load_state_dict(orig_attn.q_norm.state_dict()) aha_attn.k_norm.load_state_dict(orig_attn.k_norm.state_dict()) # q_proj: # - dynamic / duo_dynamic mode: copy original Q rows, init extra # gate rows random (~0.5). # - duo mode: q_proj is identical shape to base Qwen3 (no gate # rows), so direct load_state_dict works. orig_q_weight = orig_attn.q_proj.weight.data # (num_heads*head_dim, hidden_size) aha_q_weight = aha_attn.q_proj.weight.data if aha_attn._has_dyn_gate_logits: aha_q_weight[:num_heads * head_dim, :] = orig_q_weight nn.init.normal_(aha_q_weight[num_heads * head_dim:, :], mean=0.0, std=0.01) if orig_attn.q_proj.bias is not None and aha_attn.q_proj.bias is not None: aha_attn.q_proj.bias.data[:num_heads * head_dim] = orig_attn.q_proj.bias.data nn.init.zeros_(aha_attn.q_proj.bias.data[num_heads * head_dim:]) else: aha_attn.q_proj.load_state_dict(orig_attn.q_proj.state_dict()) del original return aha_model @classmethod def from_pretrained_aha(cls, checkpoint_path: str, **kwargs): """Load an AHA-Qwen3 checkpoint saved by `trainer.save_model`. Supports both single-file (``model.safetensors``) and multi-shard (``model.safetensors.index.json`` + ``model-*-of-*.safetensors``) layouts. The latter is automatically used by HF when a checkpoint exceeds the single-file size threshold (e.g. Qwen3-1.7B+). """ config = AHAQwen3Config.from_pretrained(checkpoint_path) config._attn_implementation = kwargs.get( "attn_implementation", getattr(config, "_attn_implementation", "sdpa"), ) model = cls(config) single_path = os.path.join(checkpoint_path, "model.safetensors") index_path = os.path.join(checkpoint_path, "model.safetensors.index.json") if os.path.exists(single_path): state_dict = load_file(single_path) elif os.path.exists(index_path): with open(index_path) as f: shard_index = json.load(f) shard_files = sorted(set(shard_index["weight_map"].values())) state_dict = {} for shard in shard_files: state_dict.update(load_file(os.path.join(checkpoint_path, shard))) else: raise FileNotFoundError( f"No safetensors file or index found in {checkpoint_path}. " f"Expected either model.safetensors or model.safetensors.index.json." ) missing_keys, unexpected_keys = model.load_state_dict(state_dict, strict=False) allowed_missing = {"lm_head.weight"} if getattr(config, "tie_word_embeddings", False) else set() extra_missing = set(missing_keys) - allowed_missing if extra_missing or unexpected_keys: raise RuntimeError( f"Unexpected checkpoint mismatch. missing={sorted(extra_missing)}, " f"unexpected={sorted(unexpected_keys)}" ) if "lm_head.weight" in missing_keys: model.tie_weights() _apply_low_alpha_force_full_heads_from_env(model) torch_dtype = kwargs.get("torch_dtype") if torch_dtype is not None: model = model.to(dtype=torch_dtype) return model