arthu1 commited on
Commit
01cfd4f
·
verified ·
1 Parent(s): 5caaad2

Fix normal Transformers inference loading

Browse files
Files changed (5) hide show
  1. README.md +1 -1
  2. aurora_config.py +63 -0
  3. aurora_model.py +376 -0
  4. modeling_aurora.py +4 -2
  5. tokenizer_config.json +1 -1
README.md CHANGED
@@ -49,7 +49,7 @@ tokenizer = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
49
  model = AutoModelForCausalLM.from_pretrained(repo, trust_remote_code=True)
50
  messages = [{"role": "user", "content": "What is Python?"}]
51
  inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")
52
- outputs = model.generate(inputs, max_new_tokens=96, do_sample=False)
53
  print(tokenizer.decode(outputs[0], skip_special_tokens=True))
54
  ```
55
 
 
49
  model = AutoModelForCausalLM.from_pretrained(repo, trust_remote_code=True)
50
  messages = [{"role": "user", "content": "What is Python?"}]
51
  inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")
52
+ outputs = model.generate(**inputs, max_new_tokens=96, do_sample=False, use_cache=False)
53
  print(tokenizer.decode(outputs[0], skip_special_tokens=True))
54
  ```
55
 
aurora_config.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, fields
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import yaml
8
+
9
+
10
+ @dataclass
11
+ class AuroraConfig:
12
+ model_name: str = "Ember Proelia"
13
+ vocab_size: int = 16000
14
+ hidden_size: int = 896
15
+ num_layers: int = 23
16
+ num_attention_heads: int = 14
17
+ num_key_value_heads: int = 2
18
+ intermediate_size: int = 2432
19
+ context_length: int = 2048
20
+ rope_theta: float = 500000.0
21
+ rms_norm_eps: float = 1.0e-5
22
+ qk_norm: bool = True
23
+ tie_word_embeddings: bool = True
24
+ attention_bias: bool = False
25
+ mlp_bias: bool = False
26
+ dropout: float = 0.0
27
+ num_experts: int = 1
28
+ router_aux_loss_coef: float = 0.0
29
+ router_z_loss_coef: float = 0.0
30
+ router_noise_scale: float = 0.0
31
+ moe_capacity_factor: float = 0.0
32
+ router_use_gate_weight: bool = False
33
+
34
+ @property
35
+ def head_dim(self) -> int:
36
+ return self.hidden_size // self.num_attention_heads
37
+
38
+ def validate(self) -> None:
39
+ if self.vocab_size <= 0 or self.hidden_size <= 0 or self.num_layers <= 0:
40
+ raise ValueError("vocab_size, hidden_size, and num_layers must be positive")
41
+ if self.hidden_size % self.num_attention_heads != 0:
42
+ raise ValueError("hidden_size must divide evenly by num_attention_heads")
43
+ if self.num_attention_heads % self.num_key_value_heads != 0:
44
+ raise ValueError("num_attention_heads must divide evenly by num_key_value_heads")
45
+ if self.head_dim % 2 != 0:
46
+ raise ValueError("head_dim must be even for RoPE")
47
+ if self.context_length <= 0:
48
+ raise ValueError("context_length must be positive")
49
+ if self.num_experts <= 0:
50
+ raise ValueError("num_experts must be positive")
51
+
52
+
53
+ def load_model_config(path: str | Path) -> AuroraConfig:
54
+ path = Path(path)
55
+ with path.open("r", encoding="utf-8") as handle:
56
+ raw: dict[str, Any] = yaml.safe_load(handle) or {}
57
+ allowed = {field.name for field in fields(AuroraConfig)}
58
+ unknown = sorted(set(raw) - allowed)
59
+ if unknown:
60
+ raise ValueError(f"Unknown model config keys: {unknown}")
61
+ cfg = AuroraConfig(**raw)
62
+ cfg.validate()
63
+ return cfg
aurora_model.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+
9
+ from .aurora_config import AuroraConfig
10
+
11
+ try:
12
+ from cut_cross_entropy import linear_cross_entropy
13
+ except ImportError:
14
+ linear_cross_entropy = None
15
+
16
+
17
+ class RMSNorm(nn.Module):
18
+ def __init__(self, dim: int, eps: float) -> None:
19
+ super().__init__()
20
+ self.weight = nn.Parameter(torch.ones(dim))
21
+ self.eps = eps
22
+
23
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
24
+ scale = torch.rsqrt(x.pow(2).mean(dim=-1, keepdim=True) + self.eps)
25
+ return self.weight * x * scale
26
+
27
+
28
+ def precompute_rope_frequencies(
29
+ seq_len: int, head_dim: int, theta: float, device: torch.device, dtype: torch.dtype
30
+ ) -> tuple[torch.Tensor, torch.Tensor]:
31
+ inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim))
32
+ positions = torch.arange(seq_len, device=device).float()
33
+ freqs = torch.outer(positions, inv_freq)
34
+ return freqs.cos().to(dtype=dtype), freqs.sin().to(dtype=dtype)
35
+
36
+
37
+ def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
38
+ cos = cos[None, :, None, :]
39
+ sin = sin[None, :, None, :]
40
+ x_even = x[..., 0::2]
41
+ x_odd = x[..., 1::2]
42
+ out = torch.empty_like(x)
43
+ out[..., 0::2] = x_even * cos - x_odd * sin
44
+ out[..., 1::2] = x_even * sin + x_odd * cos
45
+ return out
46
+
47
+
48
+ class CausalSelfAttention(nn.Module):
49
+ def __init__(self, cfg: AuroraConfig) -> None:
50
+ super().__init__()
51
+ self.cfg = cfg
52
+ self.num_heads = cfg.num_attention_heads
53
+ self.num_kv_heads = cfg.num_key_value_heads
54
+ self.head_dim = cfg.head_dim
55
+ self.kv_repeat = self.num_heads // self.num_kv_heads
56
+
57
+ self.q_proj = nn.Linear(cfg.hidden_size, cfg.num_attention_heads * self.head_dim, bias=cfg.attention_bias)
58
+ self.k_proj = nn.Linear(cfg.hidden_size, cfg.num_key_value_heads * self.head_dim, bias=cfg.attention_bias)
59
+ self.v_proj = nn.Linear(cfg.hidden_size, cfg.num_key_value_heads * self.head_dim, bias=cfg.attention_bias)
60
+ self.o_proj = nn.Linear(cfg.hidden_size, cfg.hidden_size, bias=cfg.attention_bias)
61
+ self.q_norm = RMSNorm(self.head_dim, cfg.rms_norm_eps) if cfg.qk_norm else nn.Identity()
62
+ self.k_norm = RMSNorm(self.head_dim, cfg.rms_norm_eps) if cfg.qk_norm else nn.Identity()
63
+ self.dropout_p = cfg.dropout
64
+
65
+ def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
66
+ batch, seq_len, _ = x.shape
67
+ q = self.q_proj(x).view(batch, seq_len, self.num_heads, self.head_dim)
68
+ k = self.k_proj(x).view(batch, seq_len, self.num_kv_heads, self.head_dim)
69
+ v = self.v_proj(x).view(batch, seq_len, self.num_kv_heads, self.head_dim)
70
+
71
+ q = self.q_norm(q)
72
+ k = self.k_norm(k)
73
+ q = apply_rope(q, cos, sin).transpose(1, 2)
74
+ k = apply_rope(k, cos, sin).transpose(1, 2)
75
+ v = v.transpose(1, 2)
76
+
77
+ # Explicit K/V expansion is mathematically equivalent to GQA and works
78
+ # across CUDA, Apple MPS, and CPU PyTorch backends.
79
+ if self.kv_repeat > 1:
80
+ k = k.repeat_interleave(self.kv_repeat, dim=1)
81
+ v = v.repeat_interleave(self.kv_repeat, dim=1)
82
+ y = F.scaled_dot_product_attention(
83
+ q,
84
+ k,
85
+ v,
86
+ attn_mask=None,
87
+ dropout_p=self.dropout_p if self.training else 0.0,
88
+ is_causal=True,
89
+ )
90
+ y = y.transpose(1, 2).contiguous().view(batch, seq_len, self.cfg.hidden_size)
91
+ return self.o_proj(y)
92
+
93
+
94
+ class SwiGLU(nn.Module):
95
+ def __init__(self, cfg: AuroraConfig, intermediate_size: int | None = None) -> None:
96
+ super().__init__()
97
+ intermediate_size = intermediate_size or cfg.intermediate_size
98
+ self.gate_proj = nn.Linear(cfg.hidden_size, intermediate_size, bias=cfg.mlp_bias)
99
+ self.up_proj = nn.Linear(cfg.hidden_size, intermediate_size, bias=cfg.mlp_bias)
100
+ self.down_proj = nn.Linear(intermediate_size, cfg.hidden_size, bias=cfg.mlp_bias)
101
+
102
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
103
+ return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
104
+
105
+
106
+ class Top1MoE(nn.Module):
107
+ """Top-1 routed SwiGLU experts with Switch-style router regularization."""
108
+
109
+ def __init__(self, cfg: AuroraConfig) -> None:
110
+ super().__init__()
111
+ self.num_experts = cfg.num_experts
112
+ self.router_aux_loss_coef = cfg.router_aux_loss_coef
113
+ self.router_z_loss_coef = cfg.router_z_loss_coef
114
+ self.router_noise_scale = cfg.router_noise_scale
115
+ self.capacity_factor = cfg.moe_capacity_factor
116
+ self.use_gate_weight = cfg.router_use_gate_weight
117
+ # Keep routing in BF16/FP32 rather than quantizing its logits to FP8.
118
+ self.router = nn.Linear(cfg.hidden_size, cfg.num_experts, bias=False)
119
+ self.experts = nn.ModuleList([SwiGLU(cfg) for _ in range(cfg.num_experts)])
120
+ # Detached summaries from the latest batch, for collapse detection in
121
+ # the trainer. They are intentionally not persistent model state.
122
+ self.last_expert_fraction: torch.Tensor | None = None
123
+ self.last_preferred_expert_fraction: torch.Tensor | None = None
124
+ self.last_forced_fraction: torch.Tensor | None = None
125
+ self.last_selected_gate_probability: torch.Tensor | None = None
126
+
127
+ def _capacity_constrained_route(
128
+ self, scores: torch.Tensor, preferred_index: torch.Tensor
129
+ ) -> torch.Tensor:
130
+ """Assign exactly one expert/token while bounding every expert load.
131
+
132
+ Experts keep their highest-scoring first-choice tokens. Overflow is
133
+ deterministically retried against each token's next preference. With
134
+ five experts this small eager-only matching pass is far cheaper than
135
+ an expert MLP and prevents a collapsed router from starving experts.
136
+ """
137
+ token_count = scores.size(0)
138
+ capacity = max(
139
+ math.ceil(token_count / self.num_experts),
140
+ math.ceil(token_count * self.capacity_factor / self.num_experts),
141
+ )
142
+ rankings = torch.argsort(scores, dim=-1, descending=True)
143
+ assigned = torch.full_like(preferred_index, -1)
144
+ remaining = [capacity for _ in range(self.num_experts)]
145
+
146
+ for rank in range(self.num_experts):
147
+ for expert_index in range(self.num_experts):
148
+ slots = remaining[expert_index]
149
+ if slots <= 0:
150
+ continue
151
+ candidates = torch.nonzero(
152
+ (assigned < 0) & (rankings[:, rank] == expert_index), as_tuple=False
153
+ ).flatten()
154
+ candidate_count = candidates.numel()
155
+ if candidate_count == 0:
156
+ continue
157
+ if candidate_count > slots:
158
+ candidate_scores = scores.index_select(0, candidates)[:, expert_index]
159
+ best_positions = torch.topk(candidate_scores, k=slots, sorted=False).indices
160
+ candidates = candidates.index_select(0, best_positions)
161
+ candidate_count = slots
162
+ assigned.index_fill_(0, candidates, expert_index)
163
+ remaining[expert_index] -= candidate_count
164
+
165
+ # The combined capacity is at least the token count and every token
166
+ # ranks every expert, so this is a logic invariant rather than an
167
+ # expected fallback.
168
+ if bool(torch.any(assigned < 0)):
169
+ raise RuntimeError("capacity-constrained MoE routing left tokens unassigned")
170
+ return assigned
171
+
172
+ def forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
173
+ original_shape = x.shape
174
+ flat_x = x.reshape(-1, original_shape[-1])
175
+ router_logits = self.router(flat_x).float()
176
+ router_probs = torch.softmax(router_logits, dim=-1)
177
+ # Routing indices are discrete. Keep this bookkeeping out of the
178
+ # autograd graph; language gradients still reach the selected gate
179
+ # probability when gate weighting is enabled, while router losses use
180
+ # the clean differentiable probabilities below.
181
+ with torch.no_grad():
182
+ routing_logits = router_logits
183
+ if self.training and self.router_noise_scale > 0:
184
+ # Noisy top-1 routing keeps early training exploratory. Only
185
+ # the discrete expert choice is noisy; probability weights and
186
+ # regularization remain based on clean BF16/FP32 logits.
187
+ gumbel_noise = -torch.empty_like(router_logits).exponential_().log()
188
+ routing_logits = router_logits + self.router_noise_scale * gumbel_noise
189
+ preferred_index = torch.argmax(routing_logits, dim=-1)
190
+ route_index = (
191
+ self._capacity_constrained_route(routing_logits, preferred_index)
192
+ if self.capacity_factor
193
+ else preferred_index
194
+ )
195
+ selected_router_probability = router_probs.gather(1, route_index.unsqueeze(1)).squeeze(1)
196
+ route_weight = (
197
+ selected_router_probability
198
+ if self.use_gate_weight
199
+ else torch.ones_like(selected_router_probability)
200
+ )
201
+
202
+ output = torch.zeros_like(flat_x)
203
+ for expert_index, expert in enumerate(self.experts):
204
+ token_indices = torch.nonzero(route_index == expert_index, as_tuple=False).flatten()
205
+ if token_indices.numel() == 0:
206
+ continue
207
+ expert_input = flat_x.index_select(0, token_indices)
208
+ real_token_count = expert_input.size(0)
209
+ # TorchAO's FP8 GEMMs require their M dimension to be divisible
210
+ # by 16. Sparse routing gives every expert a variable number of
211
+ # tokens, so pad only this temporary dispatch buffer and discard
212
+ # the corresponding outputs. This changes no real-token math.
213
+ fp8_padding = (-real_token_count) % 16
214
+ if fp8_padding:
215
+ expert_input = torch.cat(
216
+ (expert_input, expert_input.new_zeros((fp8_padding, expert_input.size(-1)))), dim=0
217
+ )
218
+ expert_output = expert(expert_input)[:real_token_count]
219
+ routed_output = expert_output * route_weight.index_select(0, token_indices).to(expert_output.dtype).unsqueeze(-1)
220
+ # RMSNorm can promote the residual stream to FP32, while the FP8
221
+ # expert projections return BF16 under autocast. Restore the
222
+ # residual dtype before scattering selected expert outputs.
223
+ output = output.index_copy(0, token_indices, routed_output.to(output.dtype))
224
+
225
+ expert_fraction = F.one_hot(route_index, num_classes=self.num_experts).to(router_probs.dtype).mean(dim=0)
226
+ preferred_fraction = F.one_hot(preferred_index, num_classes=self.num_experts).to(router_probs.dtype).mean(dim=0)
227
+ mean_router_prob = router_probs.mean(dim=0)
228
+ self.last_expert_fraction = expert_fraction.detach()
229
+ self.last_preferred_expert_fraction = preferred_fraction.detach()
230
+ self.last_forced_fraction = (route_index != preferred_index).float().mean().detach()
231
+ self.last_selected_gate_probability = selected_router_probability.mean().detach()
232
+ # Balance the router's *clean preference* rather than the capacity-
233
+ # constrained dispatch, which is intentionally already near-uniform.
234
+ aux_loss = self.router_aux_loss_coef * self.num_experts * torch.sum(
235
+ preferred_fraction * mean_router_prob
236
+ )
237
+ z_loss = self.router_z_loss_coef * torch.logsumexp(router_logits, dim=-1).square().mean()
238
+ return output.reshape(original_shape), aux_loss + z_loss
239
+
240
+
241
+ class DecoderBlock(nn.Module):
242
+ def __init__(self, cfg: AuroraConfig) -> None:
243
+ super().__init__()
244
+ self.input_layernorm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps)
245
+ self.self_attn = CausalSelfAttention(cfg)
246
+ self.post_attention_layernorm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps)
247
+ self.mlp: nn.Module = Top1MoE(cfg) if cfg.num_experts > 1 else SwiGLU(cfg)
248
+
249
+ def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
250
+ x = x + self.self_attn(self.input_layernorm(x), cos, sin)
251
+ mlp_input = self.post_attention_layernorm(x)
252
+ if isinstance(self.mlp, Top1MoE):
253
+ mlp_output, router_loss = self.mlp(mlp_input)
254
+ else:
255
+ mlp_output = self.mlp(mlp_input)
256
+ router_loss = x.new_zeros((), dtype=torch.float32)
257
+ return x + mlp_output, router_loss
258
+
259
+
260
+ class AuroraForCausalLM(nn.Module):
261
+ def __init__(self, cfg: AuroraConfig) -> None:
262
+ super().__init__()
263
+ cfg.validate()
264
+ self.cfg = cfg
265
+ self.embed_tokens = nn.Embedding(cfg.vocab_size, cfg.hidden_size)
266
+ self.layers = nn.ModuleList([DecoderBlock(cfg) for _ in range(cfg.num_layers)])
267
+ self.norm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps)
268
+ self.lm_head = nn.Linear(cfg.hidden_size, cfg.vocab_size, bias=False)
269
+ self.last_router_loss: torch.Tensor | None = None
270
+ self.register_buffer("rope_cos_cached", torch.empty(0), persistent=False)
271
+ self.register_buffer("rope_sin_cached", torch.empty(0), persistent=False)
272
+ if cfg.tie_word_embeddings:
273
+ self.lm_head.weight = self.embed_tokens.weight
274
+ self.apply(self._init_weights)
275
+
276
+ def _init_weights(self, module: nn.Module) -> None:
277
+ if isinstance(module, nn.Linear):
278
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
279
+ if module.bias is not None:
280
+ nn.init.zeros_(module.bias)
281
+ elif isinstance(module, nn.Embedding):
282
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
283
+
284
+ def _rope_cache(
285
+ self, seq_len: int, device: torch.device, dtype: torch.dtype
286
+ ) -> tuple[torch.Tensor, torch.Tensor]:
287
+ cache_miss = (
288
+ self.rope_cos_cached.numel() == 0
289
+ or self.rope_cos_cached.size(0) < seq_len
290
+ or self.rope_cos_cached.device != device
291
+ or self.rope_cos_cached.dtype != dtype
292
+ )
293
+ if cache_miss:
294
+ cos, sin = precompute_rope_frequencies(
295
+ self.cfg.context_length,
296
+ self.cfg.head_dim,
297
+ self.cfg.rope_theta,
298
+ device,
299
+ dtype,
300
+ )
301
+ self.rope_cos_cached = cos
302
+ self.rope_sin_cached = sin
303
+ return self.rope_cos_cached[:seq_len], self.rope_sin_cached[:seq_len]
304
+
305
+ def _rope_dtype(self, x: torch.Tensor) -> torch.dtype:
306
+ if x.device.type == "cuda" and torch.is_autocast_enabled("cuda"):
307
+ return torch.get_autocast_dtype("cuda")
308
+ if x.device.type == "cpu" and torch.is_autocast_enabled("cpu"):
309
+ return torch.get_autocast_dtype("cpu")
310
+ return x.dtype
311
+
312
+ def forward(
313
+ self, input_ids: torch.Tensor, labels: torch.Tensor | None = None
314
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
315
+ x = self.embed_tokens(input_ids)
316
+ cos, sin = self._rope_cache(x.size(1), x.device, self._rope_dtype(x))
317
+ router_loss = torch.zeros((), device=x.device, dtype=torch.float32)
318
+ for layer in self.layers:
319
+ x, layer_router_loss = layer(x, cos, sin)
320
+ router_loss = router_loss + layer_router_loss
321
+ # Each layer produces a regularizer of the same scale. Average them
322
+ # so the configured coefficient has the same meaning regardless of
323
+ # depth (instead of becoming 16x stronger in this MoE model).
324
+ router_loss = router_loss / max(1, len(self.layers))
325
+ self.last_router_loss = router_loss.detach()
326
+ x = self.norm(x)
327
+ if labels is not None and linear_cross_entropy is not None:
328
+ # RMSNorm may promote activations to FP32, but Cut Cross Entropy's
329
+ # backward kernel requires BF16/FP16 hidden states.
330
+ loss = linear_cross_entropy(x.to(self.lm_head.weight.dtype), self.lm_head.weight, labels, shift=True)
331
+ logits = x.new_empty(0)
332
+ else:
333
+ logits = self.lm_head(x)
334
+ loss = None
335
+ if labels is not None:
336
+ loss = F.cross_entropy(
337
+ logits[:, :-1].contiguous().view(-1, logits.size(-1)),
338
+ labels[:, 1:].contiguous().view(-1),
339
+ )
340
+ # Keep evaluation perplexity comparable to dense models: router regularization
341
+ # shapes gradients only during training and is not language-model loss.
342
+ if loss is not None and self.training:
343
+ loss = loss + router_loss
344
+ return logits, loss
345
+
346
+
347
+ def count_parameters(model: nn.Module) -> int:
348
+ seen: set[int] = set()
349
+ total = 0
350
+ for param in model.parameters():
351
+ ident = id(param)
352
+ if ident not in seen:
353
+ seen.add(ident)
354
+ total += param.numel()
355
+ return total
356
+
357
+
358
+ def count_active_parameters(model: nn.Module) -> int:
359
+ """Count parameters used by one top-1 path, without double-counting ties."""
360
+ seen: set[int] = set()
361
+ total = 0
362
+ for name, param in model.named_parameters():
363
+ if ".mlp.experts." in name:
364
+ expert_index = name.split(".mlp.experts.", 1)[1].split(".", 1)[0]
365
+ if expert_index != "0":
366
+ continue
367
+ ident = id(param)
368
+ if ident not in seen:
369
+ seen.add(ident)
370
+ total += param.numel()
371
+ return total
372
+
373
+
374
+ def estimate_parameter_count(cfg: AuroraConfig) -> int:
375
+ model = AuroraForCausalLM(cfg)
376
+ return count_parameters(model)
modeling_aurora.py CHANGED
@@ -8,8 +8,8 @@ from transformers import PreTrainedModel
8
  from transformers.modeling_outputs import CausalLMOutputWithPast
9
 
10
  from .configuration_aurora import AuroraHFConfig
11
- from .aurora.config import AuroraConfig as NativeAuroraConfig
12
- from .aurora.model import AuroraForCausalLM as NativeAuroraForCausalLM
13
 
14
 
15
  class AuroraForCausalLM(PreTrainedModel):
@@ -17,6 +17,8 @@ class AuroraForCausalLM(PreTrainedModel):
17
  base_model_prefix = "aurora"
18
  main_input_name = "input_ids"
19
  _supports_cache_class = False
 
 
20
 
21
  def __init__(self, config: AuroraHFConfig):
22
  super().__init__(config)
 
8
  from transformers.modeling_outputs import CausalLMOutputWithPast
9
 
10
  from .configuration_aurora import AuroraHFConfig
11
+ from .aurora_config import AuroraConfig as NativeAuroraConfig
12
+ from .aurora_model import AuroraForCausalLM as NativeAuroraForCausalLM
13
 
14
 
15
  class AuroraForCausalLM(PreTrainedModel):
 
17
  base_model_prefix = "aurora"
18
  main_input_name = "input_ids"
19
  _supports_cache_class = False
20
+ _tied_weights_keys = {}
21
+ all_tied_weights_keys = {}
22
 
23
  def __init__(self, config: AuroraHFConfig):
24
  super().__init__(config)
tokenizer_config.json CHANGED
@@ -5,5 +5,5 @@
5
  "pad_token": "<pad>",
6
  "model_max_length": 2048,
7
  "clean_up_tokenization_spaces": false,
8
- "chat_template": "{% for message in messages %}<|im_start|>{{ message['role'] }}\\n{{ message['content'] }}<|im_end|>\\n{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant\\n{% endif %}"
9
  }
 
5
  "pad_token": "<pad>",
6
  "model_max_length": 2048,
7
  "clean_up_tokenization_spaces": false,
8
+ "chat_template": "{% for message in messages %}<|im_start|>{{ message['role'] }}\n{{ message['content'] }}<|im_end|>\n{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant\n{% endif %}"
9
  }