SeaWolf-AI commited on
Commit
d9f7a55
·
verified ·
1 Parent(s): 613b24f

remove oheng/오행 references

Browse files
Files changed (1) hide show
  1. modeling_aether_v2_7way.py +867 -867
modeling_aether_v2_7way.py CHANGED
@@ -1,867 +1,867 @@
1
- # coding=utf-8
2
- # Copyright 2026 VIDRAFT (비드래프트). All rights reserved.
3
- #
4
- # AETHER-V2-7way: 7-aware attention + 7×7 Latin Square 49-layer MoE
5
- # Built upon HuggingFace Transformers conventions.
6
- #
7
- # Architecture:
8
- # - 49 layers organized as 7×7 Latin Square
9
- # - 7 distinct attention types (NSA, Differential, Full, Linear, Sliding, Compress, Hybrid)
10
- # - 25 experts per layer, top-7 active per token
11
- # - Each row of latin square = 1 cycle of 7 attention types
12
- # - Each column = different ordering (Latin square property)
13
- #
14
- # Layer index → (row, col) → attention type via LATIN_SQUARE_7x7
15
- #
16
- """PyTorch AETHER-V2-7way model."""
17
-
18
- from __future__ import annotations
19
- import math
20
- import warnings
21
- from typing import List, Optional, Tuple, Union
22
-
23
- import torch
24
- import torch.nn as nn
25
- import torch.nn.functional as F
26
- from torch.nn import CrossEntropyLoss
27
-
28
- from transformers.activations import ACT2FN
29
- from transformers.cache_utils import Cache, DynamicCache
30
- from transformers.modeling_outputs import (
31
- BaseModelOutputWithPast,
32
- CausalLMOutputWithPast,
33
- MoeCausalLMOutputWithPast,
34
- MoeModelOutputWithPast,
35
- )
36
- from transformers.modeling_utils import PreTrainedModel
37
- from transformers.generation import GenerationMixin
38
- from transformers.utils import logging
39
-
40
- from .configuration_aether_v2_7way import AETHERV27wayConfig
41
-
42
- # 7-aware attention modules (already authored, in v2_attentions/)
43
- from .nsa import NSAAttention
44
- from .differential import DifferentialAttention
45
-
46
- logger = logging.get_logger(__name__)
47
-
48
-
49
- # =============================================================================
50
- # 7×7 Latin Square — Layer → (attention_type, ffn_phase) 매핑
51
- # =============================================================================
52
- # Latin Square property: each row & column has each of {0..6} exactly once.
53
- # row = layer // 7 (0..6)
54
- # col = layer % 7 (0..6)
55
- # attention_type = LATIN_SQUARE_7x7[row][col]
56
- #
57
- # 5-element cyclic FFN phase (오행 상생 — Q2B OhengGate inspired):
58
- # ffn_phase = layer % 5
59
- #
60
- LATIN_SQUARE_7x7 = [
61
- [0, 1, 2, 3, 4, 5, 6], # row 0: identity
62
- [1, 2, 3, 4, 5, 6, 0], # row 1: shift +1
63
- [2, 3, 4, 5, 6, 0, 1], # row 2: shift +2
64
- [3, 4, 5, 6, 0, 1, 2], # row 3: shift +3
65
- [4, 5, 6, 0, 1, 2, 3], # row 4: shift +4
66
- [5, 6, 0, 1, 2, 3, 4], # row 5: shift +5
67
- [6, 0, 1, 2, 3, 4, 5], # row 6: shift +6
68
- ]
69
-
70
- # Attention type names (0..6)
71
- ATTN_TYPES = [
72
- "nsa", # 0: Native Sparse Attention (3-branch)
73
- "differential", # 1: Differential Attention (lambda-gated)
74
- "full", # 2: Full Attention (standard)
75
- "linear", # 3: Linear Attention (Mamba-style)
76
- "sliding", # 4: Sliding Window Attention
77
- "compress", # 5: Compress-only branch (NSA subset)
78
- "hybrid", # 6: NSA+Differential combined
79
- ]
80
-
81
-
82
- def get_attention_type(layer_idx: int) -> str:
83
- """Layer index → attention type via Latin Square."""
84
- row = layer_idx // 7
85
- col = layer_idx % 7
86
- type_idx = LATIN_SQUARE_7x7[row][col]
87
- return ATTN_TYPES[type_idx]
88
-
89
-
90
- def get_ffn_phase(layer_idx: int) -> int:
91
- """Layer index → 5-element cyclic phase (오행)."""
92
- return layer_idx % 5
93
-
94
-
95
- # =============================================================================
96
- # Rotary Position Embedding (RoPE)
97
- # =============================================================================
98
- class AETHERV27wayRotaryEmbedding(nn.Module):
99
- def __init__(self, dim: int, max_pos: int = 4096, base: float = 10000.0, device=None):
100
- super().__init__()
101
- self.dim = dim
102
- self.max_pos = max_pos
103
- self.base = base
104
- inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim))
105
- self.register_buffer("inv_freq", inv_freq, persistent=False)
106
- self._build_cos_sin_cache(max_pos, device or torch.device("cpu"))
107
-
108
- def _build_cos_sin_cache(self, seq_len: int, device, dtype=torch.float32):
109
- t = torch.arange(seq_len, device=device, dtype=torch.float32)
110
- freqs = torch.outer(t, self.inv_freq)
111
- emb = torch.cat([freqs, freqs], dim=-1)
112
- self.register_buffer("cos_cached", emb.cos().to(dtype), persistent=False)
113
- self.register_buffer("sin_cached", emb.sin().to(dtype), persistent=False)
114
-
115
- @torch.no_grad()
116
- def forward(self, x: torch.Tensor, position_ids: torch.Tensor):
117
- if position_ids.max() >= self.cos_cached.size(0):
118
- self._build_cos_sin_cache(int(position_ids.max() + 1), x.device, x.dtype)
119
- cos = self.cos_cached[position_ids].to(x.dtype)
120
- sin = self.sin_cached[position_ids].to(x.dtype)
121
- return cos, sin
122
-
123
-
124
- def rotate_half(x: torch.Tensor) -> torch.Tensor:
125
- x1, x2 = x.chunk(2, dim=-1)
126
- return torch.cat([-x2, x1], dim=-1)
127
-
128
-
129
- def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
130
- cos = cos.unsqueeze(unsqueeze_dim)
131
- sin = sin.unsqueeze(unsqueeze_dim)
132
- q_embed = (q * cos) + (rotate_half(q) * sin)
133
- k_embed = (k * cos) + (rotate_half(k) * sin)
134
- return q_embed, k_embed
135
-
136
-
137
- # =============================================================================
138
- # RMSNorm
139
- # =============================================================================
140
- class AETHERV27wayRMSNorm(nn.Module):
141
- def __init__(self, hidden_size: int, eps: float = 1e-6):
142
- super().__init__()
143
- self.weight = nn.Parameter(torch.ones(hidden_size))
144
- self.eps = eps
145
-
146
- def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
147
- in_dtype = hidden_states.dtype
148
- hidden_states = hidden_states.to(torch.float32)
149
- variance = hidden_states.pow(2).mean(-1, keepdim=True)
150
- hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
151
- return self.weight * hidden_states.to(in_dtype)
152
-
153
-
154
- # =============================================================================
155
- # Standard Multi-Head Attention (Full Attention type)
156
- # =============================================================================
157
- class FullAttention(nn.Module):
158
- """Standard multi-head attention with GQA support."""
159
-
160
- def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
161
- super().__init__()
162
- self.config = config
163
- self.layer_idx = layer_idx
164
- self.hidden_size = config.hidden_size
165
- self.num_heads = config.num_attention_heads
166
- self.num_kv_heads = getattr(config, "num_key_value_heads", config.num_attention_heads)
167
- self.head_dim = config.head_dim
168
- self.num_kv_groups = self.num_heads // self.num_kv_heads
169
-
170
- self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
171
- self.k_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False)
172
- self.v_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False)
173
- self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
174
-
175
- self.rotary = AETHERV27wayRotaryEmbedding(
176
- self.head_dim, config.max_position_embeddings, config.rope_theta,
177
- )
178
-
179
- def _repeat_kv(self, x: torch.Tensor) -> torch.Tensor:
180
- if self.num_kv_groups == 1:
181
- return x
182
- bsz, n_kv, seq, dim = x.shape
183
- return x[:, :, None, :, :].expand(bsz, n_kv, self.num_kv_groups, seq, dim).reshape(
184
- bsz, n_kv * self.num_kv_groups, seq, dim,
185
- )
186
-
187
- def forward(
188
- self,
189
- hidden_states: torch.Tensor,
190
- attention_mask: Optional[torch.Tensor] = None,
191
- position_ids: Optional[torch.LongTensor] = None,
192
- past_key_value: Optional[Cache] = None,
193
- use_cache: bool = False,
194
- **kwargs,
195
- ) -> Tuple[torch.Tensor, Optional[Cache]]:
196
- bsz, q_len, _ = hidden_states.size()
197
-
198
- q = self.q_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
199
- k = self.k_proj(hidden_states).view(bsz, q_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
200
- v = self.v_proj(hidden_states).view(bsz, q_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
201
-
202
- cos, sin = self.rotary(v, position_ids)
203
- q, k = apply_rotary_pos_emb(q, k, cos, sin)
204
-
205
- if past_key_value is not None:
206
- k, v = past_key_value.update(k, v, self.layer_idx)
207
-
208
- k = self._repeat_kv(k)
209
- v = self._repeat_kv(v)
210
-
211
- attn_out = F.scaled_dot_product_attention(
212
- q, k, v,
213
- attn_mask=(attention_mask.to(q.dtype) if attention_mask is not None else None),
214
- dropout_p=0.0 if not self.training else self.config.attention_dropout,
215
- is_causal=(attention_mask is None and q_len > 1),
216
- )
217
- attn_out = attn_out.transpose(1, 2).contiguous().view(bsz, q_len, -1)
218
- return self.o_proj(attn_out), past_key_value
219
-
220
-
221
- # =============================================================================
222
- # Linear Attention (Mamba-style, simplified)
223
- # =============================================================================
224
- class LinearAttention(nn.Module):
225
- """Linear attention (Mamba/RWKV-inspired) for long-context efficiency."""
226
-
227
- def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
228
- super().__init__()
229
- self.config = config
230
- self.layer_idx = layer_idx
231
- self.hidden_size = config.hidden_size
232
- self.num_heads = config.num_attention_heads
233
- self.head_dim = config.head_dim
234
- self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
235
- self.k_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
236
- self.v_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
237
- self.gate = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
238
- self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
239
- self.norm = AETHERV27wayRMSNorm(self.head_dim, eps=config.rms_norm_eps)
240
-
241
- def forward(
242
- self,
243
- hidden_states: torch.Tensor,
244
- attention_mask: Optional[torch.Tensor] = None,
245
- position_ids: Optional[torch.LongTensor] = None,
246
- past_key_value: Optional[Cache] = None,
247
- use_cache: bool = False,
248
- **kwargs,
249
- ) -> Tuple[torch.Tensor, Optional[Cache]]:
250
- bsz, q_len, _ = hidden_states.size()
251
- # causal mask handling: SDPA causal fallback (causal-safe)
252
- q = self.q_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
253
- k = self.k_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
254
- v = self.v_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
255
- g = self.gate(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).sigmoid()
256
-
257
- # AetherCache fix: KV cache + causal only when prefill (q_len>1). Training path unchanged.
258
- if past_key_value is not None:
259
- k, v = past_key_value.update(k, v, self.layer_idx)
260
- out = F.scaled_dot_product_attention(q, k, v, dropout_p=0.0, is_causal=(q_len > 1))
261
- out = out.transpose(1, 2).contiguous() # (bsz, q_len, num_heads, head_dim)
262
- out = out * g
263
- out = self.norm(out).reshape(bsz, q_len, -1)
264
- return self.o_proj(out), past_key_value
265
-
266
-
267
- # =============================================================================
268
- # Sliding Window Attention
269
- # =============================================================================
270
- class SlidingWindowAttention(FullAttention):
271
- """Standard MHA but limited to local window for efficiency."""
272
-
273
- def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
274
- super().__init__(config, layer_idx)
275
- self.window_size = getattr(config, "sliding_window_size", 512)
276
-
277
- def forward(self, hidden_states, attention_mask=None, position_ids=None,
278
- past_key_value=None, use_cache=False, **kwargs):
279
- bsz, q_len, _ = hidden_states.size()
280
- if attention_mask is None and q_len > self.window_size:
281
- mask = torch.ones(q_len, q_len, dtype=torch.bool, device=hidden_states.device)
282
- mask = torch.tril(mask) & torch.triu(mask, diagonal=-self.window_size)
283
- attention_mask = torch.where(mask, 0.0, float("-inf")).unsqueeze(0).unsqueeze(0)
284
- return super().forward(hidden_states, attention_mask, position_ids, past_key_value, use_cache, **kwargs)
285
-
286
-
287
- # =============================================================================
288
- # Compress Attention (NSA-subset, just compress branch)
289
- # =============================================================================
290
- class CompressAttention(nn.Module):
291
- """Compress branch: reduce KV cache via local average, then full attention on compressed."""
292
-
293
- def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
294
- super().__init__()
295
- self.config = config
296
- self.layer_idx = layer_idx
297
- self.compress_block = getattr(config, "compress_block_size", 16)
298
- self.full_attn = FullAttention(config, layer_idx)
299
-
300
- def forward(self, hidden_states, attention_mask=None, position_ids=None,
301
- past_key_value=None, use_cache=False, **kwargs):
302
- # per-token causal-safe block-mean
303
- # 임시 fallback: FullAttention causal (압축 효율 손실, 안전 우선)
304
- return self.full_attn(hidden_states, attention_mask, position_ids, past_key_value, use_cache)
305
-
306
-
307
- # =============================================================================
308
- # Hybrid Attention (NSA + Differential combined)
309
- # =============================================================================
310
- class HybridAttention(nn.Module):
311
- """Combine NSA + Differential outputs via learnable gate + final norm (stable).
312
-
313
- Fix v2 (2026-05-05): added post-merge GroupNorm + gate init=0 (sigmoid(0)=0.5 exact balance)
314
- + lightly scaled output to prevent 49-layer cumulative divergence.
315
- """
316
-
317
- def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
318
- super().__init__()
319
- self.nsa = NSAAttention(config, layer_idx)
320
- self.diff = DifferentialAttention(config, layer_idx)
321
- # AetherCache: nsa caches the layer input, diff caches KV -> they MUST NOT share a slot.
322
- # (layer_idx is kept for lambda_init math; only the cache slot is offset.)
323
- self.nsa.cache_idx = layer_idx
324
- self.diff.cache_idx = int(getattr(config, "num_hidden_layers", 49)) + layer_idx
325
- # Per-channel gate (richer than scalar), init to 0 → sigmoid(0)=0.5 exact balance
326
- self.gate = nn.Parameter(torch.zeros(config.hidden_size))
327
- # per-token RMSNorm (causal-safe)
328
- self.merge_norm = AETHERV27wayRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
329
-
330
- def forward(self, hidden_states, attention_mask=None, position_ids=None,
331
- past_key_value=None, use_cache=False, **kwargs):
332
- nsa_out, kv1 = self.nsa(hidden_states, attention_mask, position_ids, past_key_value, use_cache, **kwargs)
333
- diff_out, kv2 = self.diff(hidden_states, attention_mask, position_ids, past_key_value, use_cache, **kwargs)
334
- # Per-channel learnable mix: g (sigmoid) per channel
335
- g = torch.sigmoid(self.gate) # shape (hidden_size,)
336
- out = g * nsa_out + (1.0 - g) * diff_out
337
- # per-token RMSNorm (causal-safe)
338
- out = self.merge_norm(out)
339
- return out, kv1 if kv1 is not None else kv2
340
-
341
-
342
- # =============================================================================
343
- # 7-aware Attention Dispatcher
344
- # =============================================================================
345
- def build_attention(config: AETHERV27wayConfig, layer_idx: int) -> nn.Module:
346
- """Pick attention type based on Latin Square index."""
347
- attn_type = get_attention_type(layer_idx)
348
- if attn_type == "nsa":
349
- return NSAAttention(config, layer_idx)
350
- elif attn_type == "differential":
351
- return DifferentialAttention(config, layer_idx)
352
- elif attn_type == "full":
353
- return FullAttention(config, layer_idx)
354
- elif attn_type == "linear":
355
- return LinearAttention(config, layer_idx)
356
- elif attn_type == "sliding":
357
- return SlidingWindowAttention(config, layer_idx)
358
- elif attn_type == "compress":
359
- return CompressAttention(config, layer_idx)
360
- elif attn_type == "hybrid":
361
- return HybridAttention(config, layer_idx)
362
- raise ValueError(f"Unknown attention type: {attn_type}")
363
-
364
-
365
- # =============================================================================
366
- # MoE Block: 25 experts, top-7 active per token
367
- # =============================================================================
368
- class AETHERV27wayMLP(nn.Module):
369
- """Single expert MLP (SwiGLU)."""
370
-
371
- def __init__(self, config: AETHERV27wayConfig, intermediate_size: Optional[int] = None):
372
- super().__init__()
373
- self.hidden_size = config.hidden_size
374
- self.intermediate_size = intermediate_size or config.expert_intermediate_size
375
- self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
376
- self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
377
- self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
378
- self.act_fn = ACT2FN[config.hidden_act]
379
-
380
- def forward(self, x: torch.Tensor) -> torch.Tensor:
381
- return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
382
-
383
-
384
- class AETHERV27waySparseMoE(nn.Module):
385
- """25-expert MoE with top-7 active routing.
386
-
387
- Each layer has a 5-phase cyclic FFN bias to encode 오행 (Five Elements) cycle.
388
- """
389
-
390
- def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
391
- super().__init__()
392
- self.config = config
393
- self.layer_idx = layer_idx
394
- self.hidden_size = config.hidden_size
395
- self.num_experts = config.num_experts
396
- self.top_k = config.num_experts_per_tok
397
- self.ffn_phase = get_ffn_phase(layer_idx) # 0..4 (5-element cycle)
398
-
399
- # Router: hidden → num_experts logits
400
- self.gate = nn.Linear(self.hidden_size, self.num_experts, bias=False)
401
-
402
- # 25 experts (each is a SwiGLU MLP)
403
- self.experts = nn.ModuleList([
404
- AETHERV27wayMLP(config) for _ in range(self.num_experts)
405
- ])
406
-
407
- # 오행 cyclic phase bias (learnable, 5 phases)
408
- self.phase_bias = nn.Parameter(torch.zeros(5, self.num_experts))
409
-
410
- # Optional shared expert (always active, optional)
411
- self.use_shared_expert = getattr(config, "use_shared_expert", True)
412
- if self.use_shared_expert:
413
- self.shared_expert = AETHERV27wayMLP(
414
- config, intermediate_size=config.expert_intermediate_size,
415
- )
416
- self.shared_expert_gate = nn.Linear(self.hidden_size, 1, bias=False)
417
-
418
- def _stacked_experts(self):
419
- """Expert weights stacked into [E, ...] tensors so a decode step can run all top_k
420
- experts as three bmm calls instead of 3*top_k separate GEMMs. Built once, on first
421
- use, and only for inference: costs one extra copy of the expert weights in VRAM.
422
- """
423
- stk = getattr(self, "_stk", None)
424
- if stk is None:
425
- with torch.no_grad():
426
- stk = (
427
- torch.stack([e.gate_proj.weight for e in self.experts]), # [E, I, H]
428
- torch.stack([e.up_proj.weight for e in self.experts]), # [E, I, H]
429
- torch.stack([e.down_proj.weight for e in self.experts]), # [E, H, I]
430
- )
431
- self._stk = stk
432
- return stk
433
-
434
- def forward(self, hidden_states: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
435
- bsz, seq_len, dim = hidden_states.shape
436
- x = hidden_states.view(-1, dim) # (bsz*seq, dim)
437
-
438
- # Routing
439
- router_logits = self.gate(x) # (bsz*seq, num_experts)
440
- # Add 5-phase cyclic bias (오행)
441
- router_logits = router_logits + self.phase_bias[self.ffn_phase].unsqueeze(0)
442
-
443
- # Top-k selection
444
- routing_weights, selected_experts = torch.topk(router_logits, self.top_k, dim=-1)
445
- routing_weights = F.softmax(routing_weights, dim=-1)
446
-
447
- # Initialize output
448
- final_out = torch.zeros_like(x)
449
-
450
- # Per-expert dispatch. The old loop ran over every expert and called mask.any() to skip
451
- # the inactive ones -- but .any() and .nonzero() both sync the device, so a decoded token
452
- # paid num_experts x num_layers stalls just to decide what to skip. Both paths below keep
453
- # ascending-expert accumulation order, so results are unchanged.
454
- if x.shape[0] == 1 and not self.training:
455
- # Single-token decode. Each expert GEMM here is [1,H]x[H,I] -- far too small to keep
456
- # the GPU busy, so 3*top_k separate launches cost more than the math. Gather the
457
- # routed experts' weights with a device-side index (no host sync, static shape) and
458
- # run them as three bmm calls.
459
- wg, wu, wd = self._stacked_experts()
460
- idx = selected_experts[0] # [k], stays on device
461
- xe = x.unsqueeze(0).expand(idx.shape[0], 1, dim) # [k, 1, H]
462
- g = torch.bmm(xe, wg[idx].transpose(1, 2)) # [k, 1, I]
463
- u = torch.bmm(xe, wu[idx].transpose(1, 2)) # [k, 1, I]
464
- act = self.experts[0].act_fn(g) * u # [k, 1, I]
465
- o = torch.bmm(act, wd[idx].transpose(1, 2)) # [k, 1, H]
466
- w = routing_weights[0].view(-1, 1, 1).to(o.dtype)
467
- final_out = (o * w).sum(0) # [1, H]
468
- else:
469
- # unique() is sorted, so surviving experts keep ascending order; one sync per layer.
470
- for e in selected_experts.unique().tolist():
471
- mask = (selected_experts == e)
472
- token_idx, k_idx = mask.nonzero(as_tuple=True)
473
- expert_in = x[token_idx]
474
- expert_out = self.experts[e](expert_in)
475
- weight = routing_weights[token_idx, k_idx].unsqueeze(-1).to(expert_out.dtype)
476
- final_out.index_add_(0, token_idx, (expert_out * weight).to(final_out.dtype))
477
-
478
- # Shared expert
479
- if self.use_shared_expert:
480
- shared_out = self.shared_expert(x)
481
- shared_gate = torch.sigmoid(self.shared_expert_gate(x))
482
- final_out = final_out + (shared_out * shared_gate).to(final_out.dtype)
483
-
484
- final_out = final_out.view(bsz, seq_len, dim)
485
- return final_out, router_logits.view(bsz, seq_len, self.num_experts)
486
-
487
-
488
- # =============================================================================
489
- # Decoder Layer: Attention + MoE FFN with 7-aware + 5-phase logic
490
- # =============================================================================
491
- class AETHERV27wayDecoderLayer(nn.Module):
492
- def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
493
- super().__init__()
494
- self.config = config
495
- self.layer_idx = layer_idx
496
- self.hidden_size = config.hidden_size
497
- self.attn_type = get_attention_type(layer_idx)
498
- self.ffn_phase = get_ffn_phase(layer_idx)
499
-
500
- # 7-aware attention (1 of 7 types based on Latin square)
501
- self.self_attn = build_attention(config, layer_idx)
502
-
503
- # MoE FFN with 5-phase cyclic bias
504
- self.mlp = AETHERV27waySparseMoE(config, layer_idx)
505
-
506
- # Norms
507
- self.input_layernorm = AETHERV27wayRMSNorm(self.hidden_size, eps=config.rms_norm_eps)
508
- self.post_attention_layernorm = AETHERV27wayRMSNorm(self.hidden_size, eps=config.rms_norm_eps)
509
-
510
- def forward(
511
- self,
512
- hidden_states: torch.Tensor,
513
- attention_mask: Optional[torch.Tensor] = None,
514
- position_ids: Optional[torch.LongTensor] = None,
515
- past_key_value: Optional[Cache] = None,
516
- output_router_logits: bool = False,
517
- use_cache: bool = False,
518
- **kwargs,
519
- ) -> Tuple[torch.Tensor, Optional[Cache], Optional[torch.Tensor]]:
520
- # Self-attention with residual
521
- residual = hidden_states
522
- hidden_states = self.input_layernorm(hidden_states)
523
- hidden_states, kv = self.self_attn(
524
- hidden_states=hidden_states,
525
- attention_mask=attention_mask,
526
- position_ids=position_ids,
527
- past_key_value=past_key_value,
528
- use_cache=use_cache,
529
- **kwargs,
530
- )
531
- hidden_states = residual + hidden_states
532
-
533
- # MoE FFN with residual
534
- residual = hidden_states
535
- hidden_states = self.post_attention_layernorm(hidden_states)
536
- hidden_states, router_logits = self.mlp(hidden_states)
537
- hidden_states = residual + hidden_states
538
-
539
- outputs = (hidden_states, kv)
540
- if output_router_logits:
541
- outputs = outputs + (router_logits,)
542
- else:
543
- outputs = outputs + (None,)
544
- return outputs
545
-
546
-
547
- # =============================================================================
548
- # Pretrained base
549
- # =============================================================================
550
- class AETHERV27wayPreTrainedModel(PreTrainedModel):
551
- config_class = AETHERV27wayConfig
552
- base_model_prefix = "model"
553
- supports_gradient_checkpointing = True
554
- _no_split_modules = ["AETHERV27wayDecoderLayer"]
555
- _supports_cache_class = True
556
- _supports_static_cache = False
557
-
558
- def _init_weights(self, module):
559
- std = self.config.initializer_range
560
- if isinstance(module, nn.Linear):
561
- module.weight.data.normal_(mean=0.0, std=std)
562
- if module.bias is not None:
563
- module.bias.data.zero_()
564
- elif isinstance(module, nn.Embedding):
565
- module.weight.data.normal_(mean=0.0, std=std)
566
- if module.padding_idx is not None:
567
- module.weight.data[module.padding_idx].zero_()
568
- elif isinstance(module, AETHERV27wayRMSNorm):
569
- module.weight.data.fill_(1.0)
570
-
571
-
572
- # =============================================================================
573
- # Main Model
574
- # =============================================================================
575
- class AETHERV27wayModel(AETHERV27wayPreTrainedModel):
576
- """49-layer decoder-only model with 7-aware attention + MoE."""
577
-
578
- def __init__(self, config: AETHERV27wayConfig):
579
- super().__init__(config)
580
- self.padding_idx = config.pad_token_id
581
- self.vocab_size = config.vocab_size
582
-
583
- self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
584
- self.layers = nn.ModuleList([
585
- AETHERV27wayDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)
586
- ])
587
- self.norm = AETHERV27wayRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
588
- self.gradient_checkpointing = False
589
- self.post_init()
590
-
591
- def get_input_embeddings(self):
592
- return self.embed_tokens
593
-
594
- def set_input_embeddings(self, value):
595
- self.embed_tokens = value
596
-
597
- def forward(
598
- self,
599
- input_ids: Optional[torch.LongTensor] = None,
600
- attention_mask: Optional[torch.Tensor] = None,
601
- position_ids: Optional[torch.LongTensor] = None,
602
- past_key_values: Optional[Cache] = None,
603
- inputs_embeds: Optional[torch.FloatTensor] = None,
604
- use_cache: Optional[bool] = None,
605
- output_attentions: Optional[bool] = None,
606
- output_hidden_states: Optional[bool] = None,
607
- output_router_logits: Optional[bool] = None,
608
- return_dict: Optional[bool] = None,
609
- **kwargs,
610
- ) -> Union[Tuple, MoeModelOutputWithPast]:
611
- output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
612
- output_hidden_states = output_hidden_states if output_hidden_states is not None else False
613
- output_router_logits = output_router_logits if output_router_logits is not None else self.config.output_router_logits
614
- use_cache = use_cache if use_cache is not None else self.config.use_cache
615
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
616
-
617
- if input_ids is not None and inputs_embeds is not None:
618
- raise ValueError("Cannot specify both input_ids and inputs_embeds")
619
- if input_ids is not None:
620
- bsz, seq_len = input_ids.shape
621
- elif inputs_embeds is not None:
622
- bsz, seq_len, _ = inputs_embeds.shape
623
- else:
624
- raise ValueError("Either input_ids or inputs_embeds must be provided")
625
-
626
- if inputs_embeds is None:
627
- inputs_embeds = self.embed_tokens(input_ids)
628
-
629
- # TST superposition: bag s consecutive token-embeddings (FSDP-safe)
630
- _tst_bag = kwargs.get("tst_bag_size", 0)
631
- if _tst_bag and _tst_bag > 1:
632
- _b, _l, _d = inputs_embeds.shape
633
- inputs_embeds = inputs_embeds.view(_b, _l // _tst_bag, _tst_bag, _d).mean(dim=2)
634
- seq_len = inputs_embeds.shape[1]
635
-
636
- if use_cache and past_key_values is None:
637
- past_key_values = DynamicCache()
638
-
639
- past_seen = past_key_values.get_seq_length() if past_key_values is not None else 0
640
-
641
- if position_ids is None:
642
- position_ids = torch.arange(
643
- past_seen, past_seen + seq_len, device=inputs_embeds.device,
644
- ).unsqueeze(0)
645
-
646
- hidden_states = inputs_embeds
647
-
648
- all_hidden_states = () if output_hidden_states else None
649
- all_router_logits = () if output_router_logits else None
650
-
651
- for layer_idx, decoder_layer in enumerate(self.layers):
652
- if output_hidden_states:
653
- all_hidden_states += (hidden_states,)
654
-
655
- if self.gradient_checkpointing and self.training:
656
- layer_out = self._gradient_checkpointing_func(
657
- decoder_layer.__call__,
658
- hidden_states, attention_mask, position_ids,
659
- past_key_values, output_router_logits, use_cache,
660
- )
661
- else:
662
- layer_out = decoder_layer(
663
- hidden_states=hidden_states,
664
- attention_mask=attention_mask,
665
- position_ids=position_ids,
666
- past_key_value=past_key_values,
667
- output_router_logits=output_router_logits,
668
- use_cache=use_cache,
669
- )
670
-
671
- hidden_states = layer_out[0]
672
- if output_router_logits:
673
- all_router_logits += (layer_out[2],)
674
-
675
- hidden_states = self.norm(hidden_states)
676
- if output_hidden_states:
677
- all_hidden_states += (hidden_states,)
678
-
679
- if not return_dict:
680
- return tuple(v for v in [
681
- hidden_states, past_key_values, all_hidden_states, None, all_router_logits,
682
- ] if v is not None)
683
-
684
- return MoeModelOutputWithPast(
685
- last_hidden_state=hidden_states,
686
- past_key_values=past_key_values,
687
- hidden_states=all_hidden_states,
688
- attentions=None,
689
- router_logits=all_router_logits,
690
- )
691
-
692
-
693
- # =============================================================================
694
- # Causal LM Wrapper
695
- # =============================================================================
696
- class AETHERV27wayForCausalLM(AETHERV27wayPreTrainedModel, GenerationMixin):
697
- _tied_weights_keys = ["lm_head.weight"]
698
-
699
- def __init__(self, config: AETHERV27wayConfig):
700
- super().__init__(config)
701
- self.model = AETHERV27wayModel(config)
702
- self.vocab_size = config.vocab_size
703
- self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
704
- self.router_aux_loss_coef = getattr(config, "router_aux_loss_coef", 0.001)
705
- self.num_experts = config.num_experts
706
- self.num_experts_per_tok = config.num_experts_per_tok
707
- self.post_init()
708
-
709
- def get_input_embeddings(self):
710
- return self.model.embed_tokens
711
-
712
- def set_input_embeddings(self, value):
713
- self.model.embed_tokens = value
714
-
715
- def get_output_embeddings(self):
716
- return self.lm_head
717
-
718
- def set_output_embeddings(self, new_embeddings):
719
- self.lm_head = new_embeddings
720
-
721
- def get_decoder(self):
722
- return self.model
723
-
724
- def forward(
725
- self,
726
- input_ids: Optional[torch.LongTensor] = None,
727
- attention_mask: Optional[torch.Tensor] = None,
728
- position_ids: Optional[torch.LongTensor] = None,
729
- past_key_values: Optional[Cache] = None,
730
- inputs_embeds: Optional[torch.FloatTensor] = None,
731
- labels: Optional[torch.LongTensor] = None,
732
- use_cache: Optional[bool] = None,
733
- output_attentions: Optional[bool] = None,
734
- output_hidden_states: Optional[bool] = None,
735
- output_router_logits: Optional[bool] = None,
736
- return_dict: Optional[bool] = None,
737
- **kwargs,
738
- ) -> Union[Tuple, MoeCausalLMOutputWithPast]:
739
- output_router_logits = output_router_logits if output_router_logits is not None else self.config.output_router_logits
740
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
741
-
742
- outputs = self.model(
743
- input_ids=input_ids,
744
- attention_mask=attention_mask,
745
- position_ids=position_ids,
746
- past_key_values=past_key_values,
747
- inputs_embeds=inputs_embeds,
748
- use_cache=use_cache,
749
- output_hidden_states=output_hidden_states,
750
- output_router_logits=output_router_logits,
751
- tst_bag_size=kwargs.get("tst_bag_size", 0),
752
- return_dict=True,
753
- )
754
- hidden_states = outputs.last_hidden_state
755
- logits = self.lm_head(hidden_states).float()
756
-
757
- loss = None
758
- aux_loss = None
759
- if labels is not None:
760
- shift_logits = logits[..., :-1, :].contiguous()
761
- shift_labels = labels[..., 1:].contiguous()
762
- loss = F.cross_entropy(
763
- shift_logits.view(-1, self.vocab_size),
764
- shift_labels.view(-1),
765
- ignore_index=-100,
766
- )
767
-
768
- if output_router_logits and outputs.router_logits is not None:
769
- aux_loss = self._compute_router_aux_loss(outputs.router_logits, attention_mask)
770
- if loss is not None:
771
- loss = loss + self.router_aux_loss_coef * aux_loss
772
-
773
- if not return_dict:
774
- output = (logits,) + tuple(v for v in [
775
- outputs.past_key_values, outputs.hidden_states, None, outputs.router_logits, aux_loss,
776
- ] if v is not None)
777
- return (loss,) + output if loss is not None else output
778
-
779
- return MoeCausalLMOutputWithPast(
780
- loss=loss,
781
- aux_loss=aux_loss,
782
- logits=logits,
783
- past_key_values=outputs.past_key_values,
784
- hidden_states=outputs.hidden_states,
785
- attentions=None,
786
- router_logits=outputs.router_logits,
787
- )
788
-
789
- def _compute_router_aux_loss(self, router_logits: Tuple[torch.Tensor, ...], attention_mask=None):
790
- """Standard switch-transformer auxiliary loss for load balancing."""
791
- if router_logits is None or len(router_logits) == 0:
792
- return None
793
- # Each router_logits[i] shape: (bsz, seq, num_experts) → flatten to (n_tokens, num_experts)
794
- flat = []
795
- for r in router_logits:
796
- if r is None:
797
- continue
798
- flat.append(r.reshape(-1, self.num_experts))
799
- if not flat:
800
- return None
801
- all_router_logits = torch.cat(flat, dim=0) # (total_tokens, num_experts)
802
-
803
- routing_weights = F.softmax(all_router_logits.float(), dim=-1)
804
- _, selected_experts = torch.topk(routing_weights, self.num_experts_per_tok, dim=-1)
805
-
806
- # Expert mask: (n_tokens, top_k, num_experts)
807
- expert_mask = F.one_hot(selected_experts, num_classes=self.num_experts).float()
808
- # Tokens-per-expert frequency: average over (n_tokens, top_k) dims → (num_experts,)
809
- tokens_per_expert = expert_mask.mean(dim=(0, 1))
810
- # Router prob per expert: (num_experts,)
811
- router_prob_per_expert = routing_weights.mean(dim=0)
812
- # aux_loss = num_experts * sum(token_freq * prob)
813
- return self.num_experts * torch.sum(tokens_per_expert * router_prob_per_expert)
814
-
815
- def prepare_inputs_for_generation(
816
- self,
817
- input_ids,
818
- past_key_values=None,
819
- attention_mask=None,
820
- inputs_embeds=None,
821
- **kwargs,
822
- ):
823
- if past_key_values is not None:
824
- input_ids = input_ids[:, -1:]
825
- position_ids = kwargs.get("position_ids", None)
826
- if attention_mask is not None and position_ids is None:
827
- position_ids = attention_mask.long().cumsum(-1) - 1
828
- position_ids.masked_fill_(attention_mask == 0, 1)
829
- if past_key_values is not None:
830
- position_ids = position_ids[:, -input_ids.shape[1]:]
831
- return {
832
- "input_ids": input_ids,
833
- "position_ids": position_ids,
834
- "past_key_values": past_key_values,
835
- "use_cache": kwargs.get("use_cache"),
836
- "attention_mask": attention_mask,
837
- }
838
-
839
-
840
- # =============================================================================
841
- # Helper: Latin Square Layer Map (for analysis / debugging)
842
- # =============================================================================
843
- def print_layer_map(num_layers: int = 49):
844
- """Print the Latin Square attention type map."""
845
- print(f"=== AETHER-V2-7way Layer Map ({num_layers} layers) ===")
846
- for L in range(num_layers):
847
- attn = get_attention_type(L)
848
- phase = get_ffn_phase(L)
849
- row = L // 7
850
- col = L % 7
851
- print(f" L{L:02d} (row={row} col={col}): attn={attn:12s} ffn_phase={phase}")
852
-
853
-
854
- __all__ = [
855
- "AETHERV27wayConfig",
856
- "AETHERV27wayModel",
857
- "AETHERV27wayForCausalLM",
858
- "AETHERV27wayPreTrainedModel",
859
- "AETHERV27wayDecoderLayer",
860
- "AETHERV27waySparseMoE",
861
- "build_attention",
862
- "get_attention_type",
863
- "get_ffn_phase",
864
- "LATIN_SQUARE_7x7",
865
- "ATTN_TYPES",
866
- "print_layer_map",
867
- ]
 
1
+ # coding=utf-8
2
+ # Copyright 2026 VIDRAFT (비드래프트). All rights reserved.
3
+ #
4
+ # AETHER-V2-7way: 7-aware attention + 7×7 Latin Square 49-layer MoE
5
+ # Built upon HuggingFace Transformers conventions.
6
+ #
7
+ # Architecture:
8
+ # - 49 layers organized as 7×7 Latin Square
9
+ # - 7 distinct attention types (NSA, Differential, Full, Linear, Sliding, Compress, Hybrid)
10
+ # - 25 experts per layer, top-7 active per token
11
+ # - Each row of latin square = 1 cycle of 7 attention types
12
+ # - Each column = different ordering (Latin square property)
13
+ #
14
+ # Layer index → (row, col) → attention type via LATIN_SQUARE_7x7
15
+ #
16
+ """PyTorch AETHER-V2-7way model."""
17
+
18
+ from __future__ import annotations
19
+ import math
20
+ import warnings
21
+ from typing import List, Optional, Tuple, Union
22
+
23
+ import torch
24
+ import torch.nn as nn
25
+ import torch.nn.functional as F
26
+ from torch.nn import CrossEntropyLoss
27
+
28
+ from transformers.activations import ACT2FN
29
+ from transformers.cache_utils import Cache, DynamicCache
30
+ from transformers.modeling_outputs import (
31
+ BaseModelOutputWithPast,
32
+ CausalLMOutputWithPast,
33
+ MoeCausalLMOutputWithPast,
34
+ MoeModelOutputWithPast,
35
+ )
36
+ from transformers.modeling_utils import PreTrainedModel
37
+ from transformers.generation import GenerationMixin
38
+ from transformers.utils import logging
39
+
40
+ from .configuration_aether_v2_7way import AETHERV27wayConfig
41
+
42
+ # 7-aware attention modules (already authored, in v2_attentions/)
43
+ from .nsa import NSAAttention
44
+ from .differential import DifferentialAttention
45
+
46
+ logger = logging.get_logger(__name__)
47
+
48
+
49
+ # =============================================================================
50
+ # 7×7 Latin Square — Layer → (attention_type, ffn_phase) 매핑
51
+ # =============================================================================
52
+ # Latin Square property: each row & column has each of {0..6} exactly once.
53
+ # row = layer // 7 (0..6)
54
+ # col = layer % 7 (0..6)
55
+ # attention_type = LATIN_SQUARE_7x7[row][col]
56
+ #
57
+ # 5-element cyclic FFN phase (:
58
+ # ffn_phase = layer % 5
59
+ #
60
+ LATIN_SQUARE_7x7 = [
61
+ [0, 1, 2, 3, 4, 5, 6], # row 0: identity
62
+ [1, 2, 3, 4, 5, 6, 0], # row 1: shift +1
63
+ [2, 3, 4, 5, 6, 0, 1], # row 2: shift +2
64
+ [3, 4, 5, 6, 0, 1, 2], # row 3: shift +3
65
+ [4, 5, 6, 0, 1, 2, 3], # row 4: shift +4
66
+ [5, 6, 0, 1, 2, 3, 4], # row 5: shift +5
67
+ [6, 0, 1, 2, 3, 4, 5], # row 6: shift +6
68
+ ]
69
+
70
+ # Attention type names (0..6)
71
+ ATTN_TYPES = [
72
+ "nsa", # 0: Native Sparse Attention (3-branch)
73
+ "differential", # 1: Differential Attention (lambda-gated)
74
+ "full", # 2: Full Attention (standard)
75
+ "linear", # 3: Linear Attention (Mamba-style)
76
+ "sliding", # 4: Sliding Window Attention
77
+ "compress", # 5: Compress-only branch (NSA subset)
78
+ "hybrid", # 6: NSA+Differential combined
79
+ ]
80
+
81
+
82
+ def get_attention_type(layer_idx: int) -> str:
83
+ """Layer index → attention type via Latin Square."""
84
+ row = layer_idx // 7
85
+ col = layer_idx % 7
86
+ type_idx = LATIN_SQUARE_7x7[row][col]
87
+ return ATTN_TYPES[type_idx]
88
+
89
+
90
+ def get_ffn_phase(layer_idx: int) -> int:
91
+ """Layer index → 5-element cyclic phase."""
92
+ return layer_idx % 5
93
+
94
+
95
+ # =============================================================================
96
+ # Rotary Position Embedding (RoPE)
97
+ # =============================================================================
98
+ class AETHERV27wayRotaryEmbedding(nn.Module):
99
+ def __init__(self, dim: int, max_pos: int = 4096, base: float = 10000.0, device=None):
100
+ super().__init__()
101
+ self.dim = dim
102
+ self.max_pos = max_pos
103
+ self.base = base
104
+ inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim))
105
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
106
+ self._build_cos_sin_cache(max_pos, device or torch.device("cpu"))
107
+
108
+ def _build_cos_sin_cache(self, seq_len: int, device, dtype=torch.float32):
109
+ t = torch.arange(seq_len, device=device, dtype=torch.float32)
110
+ freqs = torch.outer(t, self.inv_freq)
111
+ emb = torch.cat([freqs, freqs], dim=-1)
112
+ self.register_buffer("cos_cached", emb.cos().to(dtype), persistent=False)
113
+ self.register_buffer("sin_cached", emb.sin().to(dtype), persistent=False)
114
+
115
+ @torch.no_grad()
116
+ def forward(self, x: torch.Tensor, position_ids: torch.Tensor):
117
+ if position_ids.max() >= self.cos_cached.size(0):
118
+ self._build_cos_sin_cache(int(position_ids.max() + 1), x.device, x.dtype)
119
+ cos = self.cos_cached[position_ids].to(x.dtype)
120
+ sin = self.sin_cached[position_ids].to(x.dtype)
121
+ return cos, sin
122
+
123
+
124
+ def rotate_half(x: torch.Tensor) -> torch.Tensor:
125
+ x1, x2 = x.chunk(2, dim=-1)
126
+ return torch.cat([-x2, x1], dim=-1)
127
+
128
+
129
+ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
130
+ cos = cos.unsqueeze(unsqueeze_dim)
131
+ sin = sin.unsqueeze(unsqueeze_dim)
132
+ q_embed = (q * cos) + (rotate_half(q) * sin)
133
+ k_embed = (k * cos) + (rotate_half(k) * sin)
134
+ return q_embed, k_embed
135
+
136
+
137
+ # =============================================================================
138
+ # RMSNorm
139
+ # =============================================================================
140
+ class AETHERV27wayRMSNorm(nn.Module):
141
+ def __init__(self, hidden_size: int, eps: float = 1e-6):
142
+ super().__init__()
143
+ self.weight = nn.Parameter(torch.ones(hidden_size))
144
+ self.eps = eps
145
+
146
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
147
+ in_dtype = hidden_states.dtype
148
+ hidden_states = hidden_states.to(torch.float32)
149
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
150
+ hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
151
+ return self.weight * hidden_states.to(in_dtype)
152
+
153
+
154
+ # =============================================================================
155
+ # Standard Multi-Head Attention (Full Attention type)
156
+ # =============================================================================
157
+ class FullAttention(nn.Module):
158
+ """Standard multi-head attention with GQA support."""
159
+
160
+ def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
161
+ super().__init__()
162
+ self.config = config
163
+ self.layer_idx = layer_idx
164
+ self.hidden_size = config.hidden_size
165
+ self.num_heads = config.num_attention_heads
166
+ self.num_kv_heads = getattr(config, "num_key_value_heads", config.num_attention_heads)
167
+ self.head_dim = config.head_dim
168
+ self.num_kv_groups = self.num_heads // self.num_kv_heads
169
+
170
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
171
+ self.k_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False)
172
+ self.v_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False)
173
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
174
+
175
+ self.rotary = AETHERV27wayRotaryEmbedding(
176
+ self.head_dim, config.max_position_embeddings, config.rope_theta,
177
+ )
178
+
179
+ def _repeat_kv(self, x: torch.Tensor) -> torch.Tensor:
180
+ if self.num_kv_groups == 1:
181
+ return x
182
+ bsz, n_kv, seq, dim = x.shape
183
+ return x[:, :, None, :, :].expand(bsz, n_kv, self.num_kv_groups, seq, dim).reshape(
184
+ bsz, n_kv * self.num_kv_groups, seq, dim,
185
+ )
186
+
187
+ def forward(
188
+ self,
189
+ hidden_states: torch.Tensor,
190
+ attention_mask: Optional[torch.Tensor] = None,
191
+ position_ids: Optional[torch.LongTensor] = None,
192
+ past_key_value: Optional[Cache] = None,
193
+ use_cache: bool = False,
194
+ **kwargs,
195
+ ) -> Tuple[torch.Tensor, Optional[Cache]]:
196
+ bsz, q_len, _ = hidden_states.size()
197
+
198
+ q = self.q_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
199
+ k = self.k_proj(hidden_states).view(bsz, q_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
200
+ v = self.v_proj(hidden_states).view(bsz, q_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
201
+
202
+ cos, sin = self.rotary(v, position_ids)
203
+ q, k = apply_rotary_pos_emb(q, k, cos, sin)
204
+
205
+ if past_key_value is not None:
206
+ k, v = past_key_value.update(k, v, self.layer_idx)
207
+
208
+ k = self._repeat_kv(k)
209
+ v = self._repeat_kv(v)
210
+
211
+ attn_out = F.scaled_dot_product_attention(
212
+ q, k, v,
213
+ attn_mask=(attention_mask.to(q.dtype) if attention_mask is not None else None),
214
+ dropout_p=0.0 if not self.training else self.config.attention_dropout,
215
+ is_causal=(attention_mask is None and q_len > 1),
216
+ )
217
+ attn_out = attn_out.transpose(1, 2).contiguous().view(bsz, q_len, -1)
218
+ return self.o_proj(attn_out), past_key_value
219
+
220
+
221
+ # =============================================================================
222
+ # Linear Attention (Mamba-style, simplified)
223
+ # =============================================================================
224
+ class LinearAttention(nn.Module):
225
+ """Linear attention (Mamba/RWKV-inspired) for long-context efficiency."""
226
+
227
+ def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
228
+ super().__init__()
229
+ self.config = config
230
+ self.layer_idx = layer_idx
231
+ self.hidden_size = config.hidden_size
232
+ self.num_heads = config.num_attention_heads
233
+ self.head_dim = config.head_dim
234
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
235
+ self.k_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
236
+ self.v_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
237
+ self.gate = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
238
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
239
+ self.norm = AETHERV27wayRMSNorm(self.head_dim, eps=config.rms_norm_eps)
240
+
241
+ def forward(
242
+ self,
243
+ hidden_states: torch.Tensor,
244
+ attention_mask: Optional[torch.Tensor] = None,
245
+ position_ids: Optional[torch.LongTensor] = None,
246
+ past_key_value: Optional[Cache] = None,
247
+ use_cache: bool = False,
248
+ **kwargs,
249
+ ) -> Tuple[torch.Tensor, Optional[Cache]]:
250
+ bsz, q_len, _ = hidden_states.size()
251
+ # causal mask handling: SDPA causal fallback (causal-safe)
252
+ q = self.q_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
253
+ k = self.k_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
254
+ v = self.v_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
255
+ g = self.gate(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).sigmoid()
256
+
257
+ # AetherCache fix: KV cache + causal only when prefill (q_len>1). Training path unchanged.
258
+ if past_key_value is not None:
259
+ k, v = past_key_value.update(k, v, self.layer_idx)
260
+ out = F.scaled_dot_product_attention(q, k, v, dropout_p=0.0, is_causal=(q_len > 1))
261
+ out = out.transpose(1, 2).contiguous() # (bsz, q_len, num_heads, head_dim)
262
+ out = out * g
263
+ out = self.norm(out).reshape(bsz, q_len, -1)
264
+ return self.o_proj(out), past_key_value
265
+
266
+
267
+ # =============================================================================
268
+ # Sliding Window Attention
269
+ # =============================================================================
270
+ class SlidingWindowAttention(FullAttention):
271
+ """Standard MHA but limited to local window for efficiency."""
272
+
273
+ def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
274
+ super().__init__(config, layer_idx)
275
+ self.window_size = getattr(config, "sliding_window_size", 512)
276
+
277
+ def forward(self, hidden_states, attention_mask=None, position_ids=None,
278
+ past_key_value=None, use_cache=False, **kwargs):
279
+ bsz, q_len, _ = hidden_states.size()
280
+ if attention_mask is None and q_len > self.window_size:
281
+ mask = torch.ones(q_len, q_len, dtype=torch.bool, device=hidden_states.device)
282
+ mask = torch.tril(mask) & torch.triu(mask, diagonal=-self.window_size)
283
+ attention_mask = torch.where(mask, 0.0, float("-inf")).unsqueeze(0).unsqueeze(0)
284
+ return super().forward(hidden_states, attention_mask, position_ids, past_key_value, use_cache, **kwargs)
285
+
286
+
287
+ # =============================================================================
288
+ # Compress Attention (NSA-subset, just compress branch)
289
+ # =============================================================================
290
+ class CompressAttention(nn.Module):
291
+ """Compress branch: reduce KV cache via local average, then full attention on compressed."""
292
+
293
+ def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
294
+ super().__init__()
295
+ self.config = config
296
+ self.layer_idx = layer_idx
297
+ self.compress_block = getattr(config, "compress_block_size", 16)
298
+ self.full_attn = FullAttention(config, layer_idx)
299
+
300
+ def forward(self, hidden_states, attention_mask=None, position_ids=None,
301
+ past_key_value=None, use_cache=False, **kwargs):
302
+ # per-token causal-safe block-mean
303
+ # 임시 fallback: FullAttention causal (압축 효율 손실, 안전 우선)
304
+ return self.full_attn(hidden_states, attention_mask, position_ids, past_key_value, use_cache)
305
+
306
+
307
+ # =============================================================================
308
+ # Hybrid Attention (NSA + Differential combined)
309
+ # =============================================================================
310
+ class HybridAttention(nn.Module):
311
+ """Combine NSA + Differential outputs via learnable gate + final norm (stable).
312
+
313
+ Fix v2 (2026-05-05): added post-merge GroupNorm + gate init=0 (sigmoid(0)=0.5 exact balance)
314
+ + lightly scaled output to prevent 49-layer cumulative divergence.
315
+ """
316
+
317
+ def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
318
+ super().__init__()
319
+ self.nsa = NSAAttention(config, layer_idx)
320
+ self.diff = DifferentialAttention(config, layer_idx)
321
+ # AetherCache: nsa caches the layer input, diff caches KV -> they MUST NOT share a slot.
322
+ # (layer_idx is kept for lambda_init math; only the cache slot is offset.)
323
+ self.nsa.cache_idx = layer_idx
324
+ self.diff.cache_idx = int(getattr(config, "num_hidden_layers", 49)) + layer_idx
325
+ # Per-channel gate (richer than scalar), init to 0 → sigmoid(0)=0.5 exact balance
326
+ self.gate = nn.Parameter(torch.zeros(config.hidden_size))
327
+ # per-token RMSNorm (causal-safe)
328
+ self.merge_norm = AETHERV27wayRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
329
+
330
+ def forward(self, hidden_states, attention_mask=None, position_ids=None,
331
+ past_key_value=None, use_cache=False, **kwargs):
332
+ nsa_out, kv1 = self.nsa(hidden_states, attention_mask, position_ids, past_key_value, use_cache, **kwargs)
333
+ diff_out, kv2 = self.diff(hidden_states, attention_mask, position_ids, past_key_value, use_cache, **kwargs)
334
+ # Per-channel learnable mix: g (sigmoid) per channel
335
+ g = torch.sigmoid(self.gate) # shape (hidden_size,)
336
+ out = g * nsa_out + (1.0 - g) * diff_out
337
+ # per-token RMSNorm (causal-safe)
338
+ out = self.merge_norm(out)
339
+ return out, kv1 if kv1 is not None else kv2
340
+
341
+
342
+ # =============================================================================
343
+ # 7-aware Attention Dispatcher
344
+ # =============================================================================
345
+ def build_attention(config: AETHERV27wayConfig, layer_idx: int) -> nn.Module:
346
+ """Pick attention type based on Latin Square index."""
347
+ attn_type = get_attention_type(layer_idx)
348
+ if attn_type == "nsa":
349
+ return NSAAttention(config, layer_idx)
350
+ elif attn_type == "differential":
351
+ return DifferentialAttention(config, layer_idx)
352
+ elif attn_type == "full":
353
+ return FullAttention(config, layer_idx)
354
+ elif attn_type == "linear":
355
+ return LinearAttention(config, layer_idx)
356
+ elif attn_type == "sliding":
357
+ return SlidingWindowAttention(config, layer_idx)
358
+ elif attn_type == "compress":
359
+ return CompressAttention(config, layer_idx)
360
+ elif attn_type == "hybrid":
361
+ return HybridAttention(config, layer_idx)
362
+ raise ValueError(f"Unknown attention type: {attn_type}")
363
+
364
+
365
+ # =============================================================================
366
+ # MoE Block: 25 experts, top-7 active per token
367
+ # =============================================================================
368
+ class AETHERV27wayMLP(nn.Module):
369
+ """Single expert MLP (SwiGLU)."""
370
+
371
+ def __init__(self, config: AETHERV27wayConfig, intermediate_size: Optional[int] = None):
372
+ super().__init__()
373
+ self.hidden_size = config.hidden_size
374
+ self.intermediate_size = intermediate_size or config.expert_intermediate_size
375
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
376
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
377
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
378
+ self.act_fn = ACT2FN[config.hidden_act]
379
+
380
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
381
+ return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
382
+
383
+
384
+ class AETHERV27waySparseMoE(nn.Module):
385
+ """25-expert MoE with top-7 active routing.
386
+
387
+ Each layer has a 5-phase cyclic FFN bias to encode cyclic phases.
388
+ """
389
+
390
+ def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
391
+ super().__init__()
392
+ self.config = config
393
+ self.layer_idx = layer_idx
394
+ self.hidden_size = config.hidden_size
395
+ self.num_experts = config.num_experts
396
+ self.top_k = config.num_experts_per_tok
397
+ self.ffn_phase = get_ffn_phase(layer_idx) # 0..4 (5-element cycle)
398
+
399
+ # Router: hidden → num_experts logits
400
+ self.gate = nn.Linear(self.hidden_size, self.num_experts, bias=False)
401
+
402
+ # 25 experts (each is a SwiGLU MLP)
403
+ self.experts = nn.ModuleList([
404
+ AETHERV27wayMLP(config) for _ in range(self.num_experts)
405
+ ])
406
+
407
+ # cyclic phase bias (learnable, 5 phases)
408
+ self.phase_bias = nn.Parameter(torch.zeros(5, self.num_experts))
409
+
410
+ # Optional shared expert (always active, optional)
411
+ self.use_shared_expert = getattr(config, "use_shared_expert", True)
412
+ if self.use_shared_expert:
413
+ self.shared_expert = AETHERV27wayMLP(
414
+ config, intermediate_size=config.expert_intermediate_size,
415
+ )
416
+ self.shared_expert_gate = nn.Linear(self.hidden_size, 1, bias=False)
417
+
418
+ def _stacked_experts(self):
419
+ """Expert weights stacked into [E, ...] tensors so a decode step can run all top_k
420
+ experts as three bmm calls instead of 3*top_k separate GEMMs. Built once, on first
421
+ use, and only for inference: costs one extra copy of the expert weights in VRAM.
422
+ """
423
+ stk = getattr(self, "_stk", None)
424
+ if stk is None:
425
+ with torch.no_grad():
426
+ stk = (
427
+ torch.stack([e.gate_proj.weight for e in self.experts]), # [E, I, H]
428
+ torch.stack([e.up_proj.weight for e in self.experts]), # [E, I, H]
429
+ torch.stack([e.down_proj.weight for e in self.experts]), # [E, H, I]
430
+ )
431
+ self._stk = stk
432
+ return stk
433
+
434
+ def forward(self, hidden_states: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
435
+ bsz, seq_len, dim = hidden_states.shape
436
+ x = hidden_states.view(-1, dim) # (bsz*seq, dim)
437
+
438
+ # Routing
439
+ router_logits = self.gate(x) # (bsz*seq, num_experts)
440
+ # Add 5-phase cyclic bias
441
+ router_logits = router_logits + self.phase_bias[self.ffn_phase].unsqueeze(0)
442
+
443
+ # Top-k selection
444
+ routing_weights, selected_experts = torch.topk(router_logits, self.top_k, dim=-1)
445
+ routing_weights = F.softmax(routing_weights, dim=-1)
446
+
447
+ # Initialize output
448
+ final_out = torch.zeros_like(x)
449
+
450
+ # Per-expert dispatch. The old loop ran over every expert and called mask.any() to skip
451
+ # the inactive ones -- but .any() and .nonzero() both sync the device, so a decoded token
452
+ # paid num_experts x num_layers stalls just to decide what to skip. Both paths below keep
453
+ # ascending-expert accumulation order, so results are unchanged.
454
+ if x.shape[0] == 1 and not self.training:
455
+ # Single-token decode. Each expert GEMM here is [1,H]x[H,I] -- far too small to keep
456
+ # the GPU busy, so 3*top_k separate launches cost more than the math. Gather the
457
+ # routed experts' weights with a device-side index (no host sync, static shape) and
458
+ # run them as three bmm calls.
459
+ wg, wu, wd = self._stacked_experts()
460
+ idx = selected_experts[0] # [k], stays on device
461
+ xe = x.unsqueeze(0).expand(idx.shape[0], 1, dim) # [k, 1, H]
462
+ g = torch.bmm(xe, wg[idx].transpose(1, 2)) # [k, 1, I]
463
+ u = torch.bmm(xe, wu[idx].transpose(1, 2)) # [k, 1, I]
464
+ act = self.experts[0].act_fn(g) * u # [k, 1, I]
465
+ o = torch.bmm(act, wd[idx].transpose(1, 2)) # [k, 1, H]
466
+ w = routing_weights[0].view(-1, 1, 1).to(o.dtype)
467
+ final_out = (o * w).sum(0) # [1, H]
468
+ else:
469
+ # unique() is sorted, so surviving experts keep ascending order; one sync per layer.
470
+ for e in selected_experts.unique().tolist():
471
+ mask = (selected_experts == e)
472
+ token_idx, k_idx = mask.nonzero(as_tuple=True)
473
+ expert_in = x[token_idx]
474
+ expert_out = self.experts[e](expert_in)
475
+ weight = routing_weights[token_idx, k_idx].unsqueeze(-1).to(expert_out.dtype)
476
+ final_out.index_add_(0, token_idx, (expert_out * weight).to(final_out.dtype))
477
+
478
+ # Shared expert
479
+ if self.use_shared_expert:
480
+ shared_out = self.shared_expert(x)
481
+ shared_gate = torch.sigmoid(self.shared_expert_gate(x))
482
+ final_out = final_out + (shared_out * shared_gate).to(final_out.dtype)
483
+
484
+ final_out = final_out.view(bsz, seq_len, dim)
485
+ return final_out, router_logits.view(bsz, seq_len, self.num_experts)
486
+
487
+
488
+ # =============================================================================
489
+ # Decoder Layer: Attention + MoE FFN with 7-aware + 5-phase logic
490
+ # =============================================================================
491
+ class AETHERV27wayDecoderLayer(nn.Module):
492
+ def __init__(self, config: AETHERV27wayConfig, layer_idx: int):
493
+ super().__init__()
494
+ self.config = config
495
+ self.layer_idx = layer_idx
496
+ self.hidden_size = config.hidden_size
497
+ self.attn_type = get_attention_type(layer_idx)
498
+ self.ffn_phase = get_ffn_phase(layer_idx)
499
+
500
+ # 7-aware attention (1 of 7 types based on Latin square)
501
+ self.self_attn = build_attention(config, layer_idx)
502
+
503
+ # MoE FFN with 5-phase cyclic bias
504
+ self.mlp = AETHERV27waySparseMoE(config, layer_idx)
505
+
506
+ # Norms
507
+ self.input_layernorm = AETHERV27wayRMSNorm(self.hidden_size, eps=config.rms_norm_eps)
508
+ self.post_attention_layernorm = AETHERV27wayRMSNorm(self.hidden_size, eps=config.rms_norm_eps)
509
+
510
+ def forward(
511
+ self,
512
+ hidden_states: torch.Tensor,
513
+ attention_mask: Optional[torch.Tensor] = None,
514
+ position_ids: Optional[torch.LongTensor] = None,
515
+ past_key_value: Optional[Cache] = None,
516
+ output_router_logits: bool = False,
517
+ use_cache: bool = False,
518
+ **kwargs,
519
+ ) -> Tuple[torch.Tensor, Optional[Cache], Optional[torch.Tensor]]:
520
+ # Self-attention with residual
521
+ residual = hidden_states
522
+ hidden_states = self.input_layernorm(hidden_states)
523
+ hidden_states, kv = self.self_attn(
524
+ hidden_states=hidden_states,
525
+ attention_mask=attention_mask,
526
+ position_ids=position_ids,
527
+ past_key_value=past_key_value,
528
+ use_cache=use_cache,
529
+ **kwargs,
530
+ )
531
+ hidden_states = residual + hidden_states
532
+
533
+ # MoE FFN with residual
534
+ residual = hidden_states
535
+ hidden_states = self.post_attention_layernorm(hidden_states)
536
+ hidden_states, router_logits = self.mlp(hidden_states)
537
+ hidden_states = residual + hidden_states
538
+
539
+ outputs = (hidden_states, kv)
540
+ if output_router_logits:
541
+ outputs = outputs + (router_logits,)
542
+ else:
543
+ outputs = outputs + (None,)
544
+ return outputs
545
+
546
+
547
+ # =============================================================================
548
+ # Pretrained base
549
+ # =============================================================================
550
+ class AETHERV27wayPreTrainedModel(PreTrainedModel):
551
+ config_class = AETHERV27wayConfig
552
+ base_model_prefix = "model"
553
+ supports_gradient_checkpointing = True
554
+ _no_split_modules = ["AETHERV27wayDecoderLayer"]
555
+ _supports_cache_class = True
556
+ _supports_static_cache = False
557
+
558
+ def _init_weights(self, module):
559
+ std = self.config.initializer_range
560
+ if isinstance(module, nn.Linear):
561
+ module.weight.data.normal_(mean=0.0, std=std)
562
+ if module.bias is not None:
563
+ module.bias.data.zero_()
564
+ elif isinstance(module, nn.Embedding):
565
+ module.weight.data.normal_(mean=0.0, std=std)
566
+ if module.padding_idx is not None:
567
+ module.weight.data[module.padding_idx].zero_()
568
+ elif isinstance(module, AETHERV27wayRMSNorm):
569
+ module.weight.data.fill_(1.0)
570
+
571
+
572
+ # =============================================================================
573
+ # Main Model
574
+ # =============================================================================
575
+ class AETHERV27wayModel(AETHERV27wayPreTrainedModel):
576
+ """49-layer decoder-only model with 7-aware attention + MoE."""
577
+
578
+ def __init__(self, config: AETHERV27wayConfig):
579
+ super().__init__(config)
580
+ self.padding_idx = config.pad_token_id
581
+ self.vocab_size = config.vocab_size
582
+
583
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
584
+ self.layers = nn.ModuleList([
585
+ AETHERV27wayDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)
586
+ ])
587
+ self.norm = AETHERV27wayRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
588
+ self.gradient_checkpointing = False
589
+ self.post_init()
590
+
591
+ def get_input_embeddings(self):
592
+ return self.embed_tokens
593
+
594
+ def set_input_embeddings(self, value):
595
+ self.embed_tokens = value
596
+
597
+ def forward(
598
+ self,
599
+ input_ids: Optional[torch.LongTensor] = None,
600
+ attention_mask: Optional[torch.Tensor] = None,
601
+ position_ids: Optional[torch.LongTensor] = None,
602
+ past_key_values: Optional[Cache] = None,
603
+ inputs_embeds: Optional[torch.FloatTensor] = None,
604
+ use_cache: Optional[bool] = None,
605
+ output_attentions: Optional[bool] = None,
606
+ output_hidden_states: Optional[bool] = None,
607
+ output_router_logits: Optional[bool] = None,
608
+ return_dict: Optional[bool] = None,
609
+ **kwargs,
610
+ ) -> Union[Tuple, MoeModelOutputWithPast]:
611
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
612
+ output_hidden_states = output_hidden_states if output_hidden_states is not None else False
613
+ output_router_logits = output_router_logits if output_router_logits is not None else self.config.output_router_logits
614
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
615
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
616
+
617
+ if input_ids is not None and inputs_embeds is not None:
618
+ raise ValueError("Cannot specify both input_ids and inputs_embeds")
619
+ if input_ids is not None:
620
+ bsz, seq_len = input_ids.shape
621
+ elif inputs_embeds is not None:
622
+ bsz, seq_len, _ = inputs_embeds.shape
623
+ else:
624
+ raise ValueError("Either input_ids or inputs_embeds must be provided")
625
+
626
+ if inputs_embeds is None:
627
+ inputs_embeds = self.embed_tokens(input_ids)
628
+
629
+ # TST superposition: bag s consecutive token-embeddings (FSDP-safe)
630
+ _tst_bag = kwargs.get("tst_bag_size", 0)
631
+ if _tst_bag and _tst_bag > 1:
632
+ _b, _l, _d = inputs_embeds.shape
633
+ inputs_embeds = inputs_embeds.view(_b, _l // _tst_bag, _tst_bag, _d).mean(dim=2)
634
+ seq_len = inputs_embeds.shape[1]
635
+
636
+ if use_cache and past_key_values is None:
637
+ past_key_values = DynamicCache()
638
+
639
+ past_seen = past_key_values.get_seq_length() if past_key_values is not None else 0
640
+
641
+ if position_ids is None:
642
+ position_ids = torch.arange(
643
+ past_seen, past_seen + seq_len, device=inputs_embeds.device,
644
+ ).unsqueeze(0)
645
+
646
+ hidden_states = inputs_embeds
647
+
648
+ all_hidden_states = () if output_hidden_states else None
649
+ all_router_logits = () if output_router_logits else None
650
+
651
+ for layer_idx, decoder_layer in enumerate(self.layers):
652
+ if output_hidden_states:
653
+ all_hidden_states += (hidden_states,)
654
+
655
+ if self.gradient_checkpointing and self.training:
656
+ layer_out = self._gradient_checkpointing_func(
657
+ decoder_layer.__call__,
658
+ hidden_states, attention_mask, position_ids,
659
+ past_key_values, output_router_logits, use_cache,
660
+ )
661
+ else:
662
+ layer_out = decoder_layer(
663
+ hidden_states=hidden_states,
664
+ attention_mask=attention_mask,
665
+ position_ids=position_ids,
666
+ past_key_value=past_key_values,
667
+ output_router_logits=output_router_logits,
668
+ use_cache=use_cache,
669
+ )
670
+
671
+ hidden_states = layer_out[0]
672
+ if output_router_logits:
673
+ all_router_logits += (layer_out[2],)
674
+
675
+ hidden_states = self.norm(hidden_states)
676
+ if output_hidden_states:
677
+ all_hidden_states += (hidden_states,)
678
+
679
+ if not return_dict:
680
+ return tuple(v for v in [
681
+ hidden_states, past_key_values, all_hidden_states, None, all_router_logits,
682
+ ] if v is not None)
683
+
684
+ return MoeModelOutputWithPast(
685
+ last_hidden_state=hidden_states,
686
+ past_key_values=past_key_values,
687
+ hidden_states=all_hidden_states,
688
+ attentions=None,
689
+ router_logits=all_router_logits,
690
+ )
691
+
692
+
693
+ # =============================================================================
694
+ # Causal LM Wrapper
695
+ # =============================================================================
696
+ class AETHERV27wayForCausalLM(AETHERV27wayPreTrainedModel, GenerationMixin):
697
+ _tied_weights_keys = ["lm_head.weight"]
698
+
699
+ def __init__(self, config: AETHERV27wayConfig):
700
+ super().__init__(config)
701
+ self.model = AETHERV27wayModel(config)
702
+ self.vocab_size = config.vocab_size
703
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
704
+ self.router_aux_loss_coef = getattr(config, "router_aux_loss_coef", 0.001)
705
+ self.num_experts = config.num_experts
706
+ self.num_experts_per_tok = config.num_experts_per_tok
707
+ self.post_init()
708
+
709
+ def get_input_embeddings(self):
710
+ return self.model.embed_tokens
711
+
712
+ def set_input_embeddings(self, value):
713
+ self.model.embed_tokens = value
714
+
715
+ def get_output_embeddings(self):
716
+ return self.lm_head
717
+
718
+ def set_output_embeddings(self, new_embeddings):
719
+ self.lm_head = new_embeddings
720
+
721
+ def get_decoder(self):
722
+ return self.model
723
+
724
+ def forward(
725
+ self,
726
+ input_ids: Optional[torch.LongTensor] = None,
727
+ attention_mask: Optional[torch.Tensor] = None,
728
+ position_ids: Optional[torch.LongTensor] = None,
729
+ past_key_values: Optional[Cache] = None,
730
+ inputs_embeds: Optional[torch.FloatTensor] = None,
731
+ labels: Optional[torch.LongTensor] = None,
732
+ use_cache: Optional[bool] = None,
733
+ output_attentions: Optional[bool] = None,
734
+ output_hidden_states: Optional[bool] = None,
735
+ output_router_logits: Optional[bool] = None,
736
+ return_dict: Optional[bool] = None,
737
+ **kwargs,
738
+ ) -> Union[Tuple, MoeCausalLMOutputWithPast]:
739
+ output_router_logits = output_router_logits if output_router_logits is not None else self.config.output_router_logits
740
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
741
+
742
+ outputs = self.model(
743
+ input_ids=input_ids,
744
+ attention_mask=attention_mask,
745
+ position_ids=position_ids,
746
+ past_key_values=past_key_values,
747
+ inputs_embeds=inputs_embeds,
748
+ use_cache=use_cache,
749
+ output_hidden_states=output_hidden_states,
750
+ output_router_logits=output_router_logits,
751
+ tst_bag_size=kwargs.get("tst_bag_size", 0),
752
+ return_dict=True,
753
+ )
754
+ hidden_states = outputs.last_hidden_state
755
+ logits = self.lm_head(hidden_states).float()
756
+
757
+ loss = None
758
+ aux_loss = None
759
+ if labels is not None:
760
+ shift_logits = logits[..., :-1, :].contiguous()
761
+ shift_labels = labels[..., 1:].contiguous()
762
+ loss = F.cross_entropy(
763
+ shift_logits.view(-1, self.vocab_size),
764
+ shift_labels.view(-1),
765
+ ignore_index=-100,
766
+ )
767
+
768
+ if output_router_logits and outputs.router_logits is not None:
769
+ aux_loss = self._compute_router_aux_loss(outputs.router_logits, attention_mask)
770
+ if loss is not None:
771
+ loss = loss + self.router_aux_loss_coef * aux_loss
772
+
773
+ if not return_dict:
774
+ output = (logits,) + tuple(v for v in [
775
+ outputs.past_key_values, outputs.hidden_states, None, outputs.router_logits, aux_loss,
776
+ ] if v is not None)
777
+ return (loss,) + output if loss is not None else output
778
+
779
+ return MoeCausalLMOutputWithPast(
780
+ loss=loss,
781
+ aux_loss=aux_loss,
782
+ logits=logits,
783
+ past_key_values=outputs.past_key_values,
784
+ hidden_states=outputs.hidden_states,
785
+ attentions=None,
786
+ router_logits=outputs.router_logits,
787
+ )
788
+
789
+ def _compute_router_aux_loss(self, router_logits: Tuple[torch.Tensor, ...], attention_mask=None):
790
+ """Standard switch-transformer auxiliary loss for load balancing."""
791
+ if router_logits is None or len(router_logits) == 0:
792
+ return None
793
+ # Each router_logits[i] shape: (bsz, seq, num_experts) → flatten to (n_tokens, num_experts)
794
+ flat = []
795
+ for r in router_logits:
796
+ if r is None:
797
+ continue
798
+ flat.append(r.reshape(-1, self.num_experts))
799
+ if not flat:
800
+ return None
801
+ all_router_logits = torch.cat(flat, dim=0) # (total_tokens, num_experts)
802
+
803
+ routing_weights = F.softmax(all_router_logits.float(), dim=-1)
804
+ _, selected_experts = torch.topk(routing_weights, self.num_experts_per_tok, dim=-1)
805
+
806
+ # Expert mask: (n_tokens, top_k, num_experts)
807
+ expert_mask = F.one_hot(selected_experts, num_classes=self.num_experts).float()
808
+ # Tokens-per-expert frequency: average over (n_tokens, top_k) dims → (num_experts,)
809
+ tokens_per_expert = expert_mask.mean(dim=(0, 1))
810
+ # Router prob per expert: (num_experts,)
811
+ router_prob_per_expert = routing_weights.mean(dim=0)
812
+ # aux_loss = num_experts * sum(token_freq * prob)
813
+ return self.num_experts * torch.sum(tokens_per_expert * router_prob_per_expert)
814
+
815
+ def prepare_inputs_for_generation(
816
+ self,
817
+ input_ids,
818
+ past_key_values=None,
819
+ attention_mask=None,
820
+ inputs_embeds=None,
821
+ **kwargs,
822
+ ):
823
+ if past_key_values is not None:
824
+ input_ids = input_ids[:, -1:]
825
+ position_ids = kwargs.get("position_ids", None)
826
+ if attention_mask is not None and position_ids is None:
827
+ position_ids = attention_mask.long().cumsum(-1) - 1
828
+ position_ids.masked_fill_(attention_mask == 0, 1)
829
+ if past_key_values is not None:
830
+ position_ids = position_ids[:, -input_ids.shape[1]:]
831
+ return {
832
+ "input_ids": input_ids,
833
+ "position_ids": position_ids,
834
+ "past_key_values": past_key_values,
835
+ "use_cache": kwargs.get("use_cache"),
836
+ "attention_mask": attention_mask,
837
+ }
838
+
839
+
840
+ # =============================================================================
841
+ # Helper: Latin Square Layer Map (for analysis / debugging)
842
+ # =============================================================================
843
+ def print_layer_map(num_layers: int = 49):
844
+ """Print the Latin Square attention type map."""
845
+ print(f"=== AETHER-V2-7way Layer Map ({num_layers} layers) ===")
846
+ for L in range(num_layers):
847
+ attn = get_attention_type(L)
848
+ phase = get_ffn_phase(L)
849
+ row = L // 7
850
+ col = L % 7
851
+ print(f" L{L:02d} (row={row} col={col}): attn={attn:12s} ffn_phase={phase}")
852
+
853
+
854
+ __all__ = [
855
+ "AETHERV27wayConfig",
856
+ "AETHERV27wayModel",
857
+ "AETHERV27wayForCausalLM",
858
+ "AETHERV27wayPreTrainedModel",
859
+ "AETHERV27wayDecoderLayer",
860
+ "AETHERV27waySparseMoE",
861
+ "build_attention",
862
+ "get_attention_type",
863
+ "get_ffn_phase",
864
+ "LATIN_SQUARE_7x7",
865
+ "ATTN_TYPES",
866
+ "print_layer_map",
867
+ ]