persadian commited on
Commit
cb0cbab
·
verified ·
1 Parent(s): 37015df

Upload PersadianNanoV4Model.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. PersadianNanoV4Model.py +170 -661
PersadianNanoV4Model.py CHANGED
@@ -1,11 +1,12 @@
1
  # persadian_nano_v4/PersadianNanoV4Model.py
2
- # Stable GPU-ready implementation
3
- # persadian-Nano-V4 (~160M total params)
4
 
5
  import math
6
  import torch
7
  import torch.nn as nn
8
  import torch.nn.functional as F
 
 
9
 
10
  # ============================================================
11
  # CONFIG
@@ -13,110 +14,81 @@ import torch.nn.functional as F
13
 
14
  class PersadianNanoV4Config:
15
  def __init__(self):
16
-
17
- # Core architecture
18
  self.hidden_size = 512
19
  self.intermediate_size = 1024
20
- self.num_hidden_layers = 6
21
-
22
  # Attention
23
  self.num_attention_heads = 8
24
  self.num_key_value_heads = 4
25
-
26
  # MoE
27
  self.num_experts = 4
28
  self.num_experts_per_tok = 2
29
-
30
  # Progressive experts
31
  self.progressive_experts = True
32
  self.min_experts = 1
33
  self.max_experts = 2
34
-
35
  # Hyper connections
36
  self.use_hyper_connection = True
37
-
38
  # Compression
39
  self.use_compressed_attention = True
40
  self.compress_ratio = 4
41
-
42
  # Context
43
  self.max_position_embeddings = 2048
44
-
45
  # Tokenizer
46
  self.vocab_size = 50257
47
  self.bos_token_id = 50256
48
  self.eos_token_id = 50256
49
-
50
  # RoPE
51
  self.rope_theta = 10000.0
52
-
53
  # Regularization
54
  self.dropout = 0.1
55
  self.layer_norm_eps = 1e-5
56
-
57
  # Attention
58
  self.attention_dropout = 0.0
59
  self.use_flash_attention = True
60
-
61
  # Precision
62
  self.torch_dtype = "float16"
63
 
64
-
65
  # ============================================================
66
  # ROTARY EMBEDDING
67
  # ============================================================
68
 
69
  class RotaryEmbedding(nn.Module):
70
-
71
  def __init__(self, dim, base=10000.0):
72
  super().__init__()
73
-
74
- inv_freq = 1.0 / (
75
- base ** (
76
- torch.arange(0, dim, 2).float() / dim
77
- )
78
- )
79
-
80
  self.register_buffer("inv_freq", inv_freq)
81
 
82
  def forward(self, x, position_ids):
83
-
84
- # x shape:
85
- # [batch, seq, heads, head_dim]
86
-
87
  batch, seq_len, num_heads, head_dim = x.shape
88
-
89
- freqs = torch.einsum(
90
- "bi,j->bij",
91
- position_ids.float(),
92
- self.inv_freq
93
- )
94
-
95
  emb = torch.cat([freqs, freqs], dim=-1)
96
-
97
  cos = emb.cos()[:, :, None, :]
98
  sin = emb.sin()[:, :, None, :]
99
-
100
  x1 = x[..., ::2]
101
  x2 = x[..., 1::2]
102
-
103
- rotated = torch.stack(
104
- (-x2, x1),
105
- dim=-1
106
- ).flatten(-2)
107
-
108
  return (x * cos) + (rotated * sin)
109
 
110
-
111
  # ============================================================
112
  # ADAPTIVE HYPER CONNECTION
113
  # ============================================================
114
 
115
  class AdaptiveHyperConnection(nn.Module):
116
-
117
  def __init__(self, hidden_size):
118
  super().__init__()
119
-
120
  self.router = nn.Sequential(
121
  nn.Linear(hidden_size, hidden_size // 4),
122
  nn.SiLU(),
@@ -125,726 +97,263 @@ class AdaptiveHyperConnection(nn.Module):
125
  )
126
 
127
  def forward(self, x, residual):
128
-
129
- weight = self.router(
130
- x.mean(dim=1, keepdim=True)
131
- )
132
-
133
  return residual + (x * weight)
134
 
135
-
136
  # ============================================================
137
  # ATTENTION
138
  # ============================================================
139
 
140
  class CompressedSparseAttention(nn.Module):
141
-
142
  def __init__(self, config):
143
  super().__init__()
144
-
145
  self.config = config
146
-
147
  self.hidden_size = config.hidden_size
148
-
149
  self.num_heads = config.num_attention_heads
150
  self.num_key_value_heads = config.num_key_value_heads
151
-
152
- self.head_dim = (
153
- config.hidden_size //
154
- config.num_attention_heads
155
- )
156
-
157
- # Q projection
158
- self.q_proj = nn.Linear(
159
- config.hidden_size,
160
- config.hidden_size,
161
- bias=False
162
- )
163
-
164
- # KV projection
165
- self.k_proj = nn.Linear(
166
- config.hidden_size,
167
- self.num_key_value_heads * self.head_dim,
168
- bias=False
169
- )
170
-
171
- self.v_proj = nn.Linear(
172
- config.hidden_size,
173
- self.num_key_value_heads * self.head_dim,
174
- bias=False
175
- )
176
-
177
- # Output
178
- self.o_proj = nn.Linear(
179
- config.hidden_size,
180
- config.hidden_size,
181
- bias=False
182
- )
183
-
184
- # Compression
185
- compressed_dim = (
186
- config.hidden_size //
187
- config.compress_ratio
188
- )
189
-
190
- self.compressor = nn.Linear(
191
- config.hidden_size,
192
- compressed_dim
193
- )
194
-
195
- self.k_proj_compressed = nn.Linear(
196
- compressed_dim,
197
- self.num_key_value_heads * self.head_dim,
198
- bias=False
199
- )
200
-
201
- self.v_proj_compressed = nn.Linear(
202
- compressed_dim,
203
- self.num_key_value_heads * self.head_dim,
204
- bias=False
205
- )
206
-
207
- # Rotary
208
- self.rotary_emb = RotaryEmbedding(
209
- self.head_dim,
210
- config.rope_theta
211
- )
212
-
213
- def forward(
214
- self,
215
- hidden_states,
216
- attention_mask=None,
217
- position_ids=None,
218
- ):
219
-
220
  batch_size, seq_len, _ = hidden_states.shape
221
-
222
- # ====================================================
223
- # Q
224
- # ====================================================
225
-
226
- q = self.q_proj(hidden_states)
227
-
228
- q = q.view(
229
- batch_size,
230
- seq_len,
231
- self.num_heads,
232
- self.head_dim
233
- )
234
-
235
- # ====================================================
236
- # COMPRESSED OR NORMAL KV
237
- # ====================================================
238
-
239
- if (
240
- self.config.use_compressed_attention and
241
- seq_len > 512
242
- ):
243
-
244
  compressed = self.compressor(hidden_states)
245
-
246
  k = self.k_proj_compressed(compressed)
247
  v = self.v_proj_compressed(compressed)
248
-
249
  else:
250
-
251
  k = self.k_proj(hidden_states)
252
  v = self.v_proj(hidden_states)
253
-
254
- k = k.view(
255
- batch_size,
256
- seq_len,
257
- self.num_key_value_heads,
258
- self.head_dim
259
- )
260
-
261
- v = v.view(
262
- batch_size,
263
- seq_len,
264
- self.num_key_value_heads,
265
- self.head_dim
266
- )
267
-
268
- # ====================================================
269
- # GQA EXPANSION
270
- # ====================================================
271
-
272
- repeat_factor = (
273
- self.num_heads //
274
- self.num_key_value_heads
275
- )
276
-
277
- k = k.repeat_interleave(
278
- repeat_factor,
279
- dim=2
280
- )
281
-
282
- v = v.repeat_interleave(
283
- repeat_factor,
284
- dim=2
285
- )
286
-
287
- # ====================================================
288
- # ROPE
289
- # ====================================================
290
-
291
  q = self.rotary_emb(q, position_ids)
292
  k = self.rotary_emb(k, position_ids)
293
-
294
- # ====================================================
295
- # TRANSPOSE
296
- # ====================================================
297
-
298
  q = q.transpose(1, 2)
299
  k = k.transpose(1, 2)
300
  v = v.transpose(1, 2)
301
-
302
- # ====================================================
303
- # FLASH ATTENTION
304
- # ====================================================
305
-
306
- if (
307
- self.config.use_flash_attention and
308
- hasattr(F, "scaled_dot_product_attention")
309
- ):
310
-
311
- attn_output = F.scaled_dot_product_attention(
312
- q,
313
- k,
314
- v,
315
- attn_mask=attention_mask,
316
- dropout_p=self.config.attention_dropout,
317
- is_causal=True
318
- )
319
-
320
  else:
321
-
322
- scores = torch.matmul(
323
- q,
324
- k.transpose(-2, -1)
325
- ) / math.sqrt(self.head_dim)
326
-
327
  if attention_mask is not None:
328
  scores = scores + attention_mask
329
-
330
- probs = F.softmax(scores, dim=-1)
331
-
332
- attn_output = torch.matmul(
333
- probs,
334
- v
335
- )
336
-
337
- # ====================================================
338
- # OUTPUT
339
- # ====================================================
340
-
341
- attn_output = attn_output.transpose(1, 2)
342
-
343
- attn_output = attn_output.contiguous().view(
344
- batch_size,
345
- seq_len,
346
- self.hidden_size
347
- )
348
-
349
  return self.o_proj(attn_output)
350
 
351
-
352
  # ============================================================
353
  # MIXTURE OF EXPERTS
354
  # ============================================================
355
 
356
  class MixtureOfExperts(nn.Module):
357
-
358
  def __init__(self, config):
359
  super().__init__()
360
-
361
  self.config = config
362
-
363
  self.num_experts = config.num_experts
364
-
365
  self.experts = nn.ModuleList([
366
  nn.Sequential(
367
- nn.Linear(
368
- config.hidden_size,
369
- config.intermediate_size
370
- ),
371
  nn.SiLU(),
372
- nn.Linear(
373
- config.intermediate_size,
374
- config.hidden_size
375
- ),
376
  nn.Dropout(config.dropout)
377
- )
378
- for _ in range(config.num_experts)
379
  ])
380
-
381
- self.router = nn.Linear(
382
- config.hidden_size,
383
- config.num_experts,
384
- bias=False
385
- )
386
-
387
  self.aux_loss_coef = 0.01
388
 
389
- def forward(
390
- self,
391
- hidden_states,
392
- progressive_factor=1.0
393
- ):
394
-
395
  batch_size, seq_len, hidden_size = hidden_states.shape
396
-
397
- hidden_states_flat = hidden_states.view(
398
- -1,
399
- hidden_size
400
- )
401
-
402
- router_logits = self.router(
403
- hidden_states_flat
404
- )
405
-
406
- routing_weights = F.softmax(
407
- router_logits,
408
- dim=-1
409
- )
410
-
411
- # ====================================================
412
- # PROGRESSIVE EXPERT ACTIVATION
413
- # ====================================================
414
-
415
  if self.config.progressive_experts:
416
-
417
- k = int(
418
- round(
419
- self.config.min_experts +
420
- (
421
- self.config.max_experts -
422
- self.config.min_experts
423
- ) * progressive_factor
424
- )
425
- )
426
-
427
- k = max(
428
- self.config.min_experts,
429
- min(k, self.config.max_experts)
430
- )
431
-
432
  else:
433
  k = self.config.num_experts_per_tok
434
-
435
- # ====================================================
436
- # TOP-K ROUTING
437
- # ====================================================
438
-
439
- top_k_weights, top_k_indices = torch.topk(
440
- routing_weights,
441
- k,
442
- dim=-1
443
- )
444
-
445
- top_k_weights = (
446
- top_k_weights /
447
- top_k_weights.sum(
448
- dim=-1,
449
- keepdim=True
450
- )
451
- )
452
-
453
- # ====================================================
454
- # EXPERT COMPUTE
455
- # ====================================================
456
-
457
- final_hidden = torch.zeros_like(
458
- hidden_states_flat
459
- )
460
-
461
  for expert_idx in range(self.num_experts):
462
-
463
- expert_mask = (
464
- top_k_indices == expert_idx
465
- ).any(dim=-1)
466
-
467
  if expert_mask.any():
468
-
469
- expert_input = hidden_states_flat[
470
- expert_mask
471
- ]
472
-
473
- expert_output = self.experts[
474
- expert_idx
475
- ](expert_input)
476
-
477
- expert_weights = top_k_weights[
478
- expert_mask
479
- ].mean(dim=-1, keepdim=True)
480
-
481
- final_hidden[expert_mask] += (
482
- expert_output * expert_weights
483
- )
484
-
485
- # ====================================================
486
- # AUX LOSS
487
- # ====================================================
488
-
489
  router_probs = routing_weights.mean(dim=0)
490
-
491
  aux_loss = torch.var(router_probs)
492
-
493
- return (
494
- final_hidden.view(
495
- batch_size,
496
- seq_len,
497
- hidden_size
498
- ),
499
- aux_loss * self.aux_loss_coef
500
- )
501
-
502
 
503
  # ============================================================
504
  # DECODER LAYER
505
  # ============================================================
506
 
507
  class PersadianNanoV4DecoderLayer(nn.Module):
508
-
509
  def __init__(self, config, layer_idx):
510
  super().__init__()
511
-
512
  self.config = config
513
-
514
- self.input_layernorm = nn.LayerNorm(
515
- config.hidden_size,
516
- eps=config.layer_norm_eps
517
- )
518
-
519
- self.self_attn = CompressedSparseAttention(
520
- config
521
- )
522
-
523
- self.post_attention_layernorm = nn.LayerNorm(
524
- config.hidden_size,
525
- eps=config.layer_norm_eps
526
- )
527
-
528
  self.moe = MixtureOfExperts(config)
529
-
530
  if config.use_hyper_connection:
 
 
531
 
532
- self.hc_attention = AdaptiveHyperConnection(
533
- config.hidden_size
534
- )
535
-
536
- self.hc_moe = AdaptiveHyperConnection(
537
- config.hidden_size
538
- )
539
-
540
- def forward(
541
- self,
542
- hidden_states,
543
- attention_mask=None,
544
- position_ids=None,
545
- progressive_factor=1.0
546
- ):
547
-
548
- # ====================================================
549
- # ATTENTION
550
- # ====================================================
551
-
552
  residual = hidden_states
553
-
554
- hidden_states = self.input_layernorm(
555
- hidden_states
556
- )
557
-
558
- attn_output = self.self_attn(
559
- hidden_states,
560
- attention_mask,
561
- position_ids
562
- )
563
-
564
  if self.config.use_hyper_connection:
565
-
566
- hidden_states = self.hc_attention(
567
- attn_output,
568
- residual
569
- )
570
-
571
  else:
572
  hidden_states = residual + attn_output
573
-
574
- # ====================================================
575
- # MOE
576
- # ====================================================
577
-
578
  residual = hidden_states
579
-
580
- hidden_states = self.post_attention_layernorm(
581
- hidden_states
582
- )
583
-
584
- moe_output, aux_loss = self.moe(
585
- hidden_states,
586
- progressive_factor
587
- )
588
-
589
  if self.config.use_hyper_connection:
590
-
591
- hidden_states = self.hc_moe(
592
- moe_output,
593
- residual
594
- )
595
-
596
  else:
597
  hidden_states = residual + moe_output
598
-
599
  return hidden_states, aux_loss
600
 
601
-
602
  # ============================================================
603
- # MAIN MODEL
604
  # ============================================================
605
 
606
  class PersadianNanoV4Model(nn.Module):
607
-
608
  def __init__(self, config):
609
  super().__init__()
610
-
611
  self.config = config
612
-
613
- self.embed_tokens = nn.Embedding(
614
- config.vocab_size,
615
- config.hidden_size
616
- )
617
-
618
- self.layers = nn.ModuleList([
619
- PersadianNanoV4DecoderLayer(
620
- config,
621
- i
622
- )
623
- for i in range(config.num_hidden_layers)
624
- ])
625
-
626
- self.norm = nn.LayerNorm(
627
- config.hidden_size,
628
- eps=config.layer_norm_eps
629
- )
630
-
631
- self.lm_head = nn.Linear(
632
- config.hidden_size,
633
- config.vocab_size,
634
- bias=False
635
- )
636
-
637
- # Tie weights
638
  self.lm_head.weight = self.embed_tokens.weight
639
-
640
- # Init
641
  self.apply(self._init_weights)
642
-
643
  def _init_weights(self, module):
644
-
645
  if isinstance(module, nn.Linear):
646
-
647
- nn.init.normal_(
648
- module.weight,
649
- mean=0.0,
650
- std=0.02
651
- )
652
-
653
  if module.bias is not None:
654
  nn.init.zeros_(module.bias)
655
-
656
  elif isinstance(module, nn.Embedding):
657
-
658
- nn.init.normal_(
659
- module.weight,
660
- mean=0.0,
661
- std=0.02
662
- )
663
-
664
- def forward(
665
- self,
666
- input_ids,
667
- attention_mask=None,
668
- progressive_factor=1.0
669
- ):
670
-
671
  batch_size, seq_len = input_ids.shape
672
-
673
- hidden_states = self.embed_tokens(
674
- input_ids
675
- )
676
-
677
- position_ids = torch.arange(
678
- seq_len,
679
- device=input_ids.device
680
- ).unsqueeze(0)
681
-
682
- # ====================================================
683
- # CAUSAL MASK
684
- # ====================================================
685
-
686
  if attention_mask is None:
687
-
688
- mask = torch.triu(
689
- torch.full(
690
- (seq_len, seq_len),
691
- float("-inf"),
692
- device=input_ids.device
693
- ),
694
- diagonal=1
695
- )
696
-
697
  attention_mask = mask.unsqueeze(0).unsqueeze(0)
698
-
699
- total_aux_loss = torch.tensor(
700
- 0.0,
701
- device=input_ids.device
702
- )
703
-
704
  for layer in self.layers:
705
-
706
- hidden_states, aux_loss = layer(
707
- hidden_states,
708
- attention_mask,
709
- position_ids,
710
- progressive_factor
711
- )
712
-
713
  total_aux_loss += aux_loss
714
-
715
  hidden_states = self.norm(hidden_states)
716
-
717
  logits = self.lm_head(hidden_states)
718
-
719
  return logits, total_aux_loss
720
-
721
- @torch.no_grad()
722
- def generate(
723
- self,
724
- input_ids,
725
- max_new_tokens=50,
726
- temperature=0.7,
727
- top_k=50
728
- ):
729
-
730
  self.eval()
731
-
732
  for _ in range(max_new_tokens):
733
-
734
  logits, _ = self.forward(input_ids)
735
-
736
- next_token_logits = (
737
- logits[:, -1, :] / temperature
738
- )
739
-
740
  if top_k > 0:
741
-
742
- values, _ = torch.topk(
743
- next_token_logits,
744
- top_k
745
- )
746
-
747
  min_values = values[:, -1].unsqueeze(-1)
748
-
749
- next_token_logits = torch.where(
750
- next_token_logits < min_values,
751
- torch.full_like(
752
- next_token_logits,
753
- float("-inf")
754
- ),
755
- next_token_logits
756
- )
757
-
758
- probs = F.softmax(
759
- next_token_logits,
760
- dim=-1
761
- )
762
-
763
- next_token = torch.multinomial(
764
- probs,
765
- num_samples=1
766
- )
767
-
768
- input_ids = torch.cat(
769
- [input_ids, next_token],
770
- dim=1
771
- )
772
-
773
- if (
774
- next_token.item() ==
775
- self.config.eos_token_id
776
- ):
777
  break
778
-
779
  return input_ids
780
 
781
-
782
  # ============================================================
783
- # PARAMETER COUNT
784
  # ============================================================
785
 
786
- def count_parameters(model):
787
-
788
- return sum(
789
- p.numel()
790
- for p in model.parameters()
791
- if p.requires_grad
792
- )
793
-
794
-
795
- # ============================================================
796
- # TEST
797
- # ============================================================
798
-
799
- if __name__ == "__main__":
800
-
801
- print("=" * 60)
802
- print("Initializing Persadian-Nano-V4")
803
- print("=" * 60)
804
-
805
- config = PersadianNanoV4Config()
806
-
807
- model = PersadianNanoV4Model(config)
808
-
809
- total_params = count_parameters(model)
810
-
811
- print(f"Total Parameters: {total_params:,}")
812
- print(f"Approx Size: {total_params / 1e6:.2f}M")
813
-
814
- device = (
815
- "cuda"
816
- if torch.cuda.is_available()
817
- else "cpu"
818
- )
819
-
820
- print(f"Device: {device}")
821
-
822
- model = model.to(device)
823
-
824
- dummy_input = torch.randint(
825
- 0,
826
- config.vocab_size,
827
- (1, 128)
828
- ).to(device)
829
-
830
- print("\nRunning forward pass...")
831
-
832
- with torch.no_grad():
833
-
834
- logits, aux_loss = model(dummy_input)
835
-
836
- print("Forward pass successful")
837
- print(f"Logits Shape: {logits.shape}")
838
- print(f"Aux Loss: {aux_loss.item():.6f}")
839
-
840
- print("\nTesting generation...")
841
-
842
- generated = model.generate(
843
- dummy_input[:, :16],
844
- max_new_tokens=10
845
- )
846
-
847
- print(f"Generated Shape: {generated.shape}")
848
-
849
- print("\nPersadian-Nano-V4 is operational")
850
- print("=" * 60)
 
1
  # persadian_nano_v4/PersadianNanoV4Model.py
2
+ # Stable GPU-ready implementation with Hugging Face compatibility
 
3
 
4
  import math
5
  import torch
6
  import torch.nn as nn
7
  import torch.nn.functional as F
8
+ from transformers import PreTrainedModel, PretrainedConfig
9
+ from transformers.modeling_outputs import CausalLMOutputWithPast
10
 
11
  # ============================================================
12
  # CONFIG
 
14
 
15
  class PersadianNanoV4Config:
16
  def __init__(self):
17
+ # Core architecture (MATCH CHECKPOINT)
 
18
  self.hidden_size = 512
19
  self.intermediate_size = 1024
20
+ self.num_hidden_layers = 12
21
+
22
  # Attention
23
  self.num_attention_heads = 8
24
  self.num_key_value_heads = 4
25
+
26
  # MoE
27
  self.num_experts = 4
28
  self.num_experts_per_tok = 2
29
+
30
  # Progressive experts
31
  self.progressive_experts = True
32
  self.min_experts = 1
33
  self.max_experts = 2
34
+
35
  # Hyper connections
36
  self.use_hyper_connection = True
37
+
38
  # Compression
39
  self.use_compressed_attention = True
40
  self.compress_ratio = 4
41
+
42
  # Context
43
  self.max_position_embeddings = 2048
44
+
45
  # Tokenizer
46
  self.vocab_size = 50257
47
  self.bos_token_id = 50256
48
  self.eos_token_id = 50256
49
+
50
  # RoPE
51
  self.rope_theta = 10000.0
52
+
53
  # Regularization
54
  self.dropout = 0.1
55
  self.layer_norm_eps = 1e-5
56
+
57
  # Attention
58
  self.attention_dropout = 0.0
59
  self.use_flash_attention = True
60
+
61
  # Precision
62
  self.torch_dtype = "float16"
63
 
 
64
  # ============================================================
65
  # ROTARY EMBEDDING
66
  # ============================================================
67
 
68
  class RotaryEmbedding(nn.Module):
 
69
  def __init__(self, dim, base=10000.0):
70
  super().__init__()
71
+ inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
 
 
 
 
 
 
72
  self.register_buffer("inv_freq", inv_freq)
73
 
74
  def forward(self, x, position_ids):
 
 
 
 
75
  batch, seq_len, num_heads, head_dim = x.shape
76
+ freqs = torch.einsum("bi,j->bij", position_ids.float(), self.inv_freq)
 
 
 
 
 
 
77
  emb = torch.cat([freqs, freqs], dim=-1)
 
78
  cos = emb.cos()[:, :, None, :]
79
  sin = emb.sin()[:, :, None, :]
 
80
  x1 = x[..., ::2]
81
  x2 = x[..., 1::2]
82
+ rotated = torch.stack((-x2, x1), dim=-1).flatten(-2)
 
 
 
 
 
83
  return (x * cos) + (rotated * sin)
84
 
 
85
  # ============================================================
86
  # ADAPTIVE HYPER CONNECTION
87
  # ============================================================
88
 
89
  class AdaptiveHyperConnection(nn.Module):
 
90
  def __init__(self, hidden_size):
91
  super().__init__()
 
92
  self.router = nn.Sequential(
93
  nn.Linear(hidden_size, hidden_size // 4),
94
  nn.SiLU(),
 
97
  )
98
 
99
  def forward(self, x, residual):
100
+ weight = self.router(x.mean(dim=1, keepdim=True))
 
 
 
 
101
  return residual + (x * weight)
102
 
 
103
  # ============================================================
104
  # ATTENTION
105
  # ============================================================
106
 
107
  class CompressedSparseAttention(nn.Module):
 
108
  def __init__(self, config):
109
  super().__init__()
 
110
  self.config = config
 
111
  self.hidden_size = config.hidden_size
 
112
  self.num_heads = config.num_attention_heads
113
  self.num_key_value_heads = config.num_key_value_heads
114
+ self.head_dim = config.hidden_size // config.num_attention_heads
115
+
116
+ self.q_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
117
+ self.k_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
118
+ self.v_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
119
+ self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
120
+
121
+ compressed_dim = config.hidden_size // config.compress_ratio
122
+ self.compressor = nn.Linear(config.hidden_size, compressed_dim)
123
+ self.k_proj_compressed = nn.Linear(compressed_dim, self.num_key_value_heads * self.head_dim, bias=False)
124
+ self.v_proj_compressed = nn.Linear(compressed_dim, self.num_key_value_heads * self.head_dim, bias=False)
125
+
126
+ self.rotary_emb = RotaryEmbedding(self.head_dim, config.rope_theta)
127
+
128
+ def forward(self, hidden_states, attention_mask=None, position_ids=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  batch_size, seq_len, _ = hidden_states.shape
130
+
131
+ q = self.q_proj(hidden_states).view(batch_size, seq_len, self.num_heads, self.head_dim)
132
+
133
+ if self.config.use_compressed_attention and seq_len > 512:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  compressed = self.compressor(hidden_states)
 
135
  k = self.k_proj_compressed(compressed)
136
  v = self.v_proj_compressed(compressed)
 
137
  else:
 
138
  k = self.k_proj(hidden_states)
139
  v = self.v_proj(hidden_states)
140
+
141
+ k = k.view(batch_size, seq_len, self.num_key_value_heads, self.head_dim)
142
+ v = v.view(batch_size, seq_len, self.num_key_value_heads, self.head_dim)
143
+
144
+ repeat_factor = self.num_heads // self.num_key_value_heads
145
+ k = k.repeat_interleave(repeat_factor, dim=2)
146
+ v = v.repeat_interleave(repeat_factor, dim=2)
147
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  q = self.rotary_emb(q, position_ids)
149
  k = self.rotary_emb(k, position_ids)
150
+
 
 
 
 
151
  q = q.transpose(1, 2)
152
  k = k.transpose(1, 2)
153
  v = v.transpose(1, 2)
154
+
155
+ if self.config.use_flash_attention and hasattr(F, "scaled_dot_product_attention"):
156
+ attn_output = F.scaled_dot_product_attention(q, k, v, attn_mask=attention_mask, is_causal=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  else:
158
+ scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
 
 
 
 
 
159
  if attention_mask is not None:
160
  scores = scores + attention_mask
161
+ attn_output = torch.matmul(F.softmax(scores, dim=-1), v)
162
+
163
+ attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.hidden_size)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  return self.o_proj(attn_output)
165
 
 
166
  # ============================================================
167
  # MIXTURE OF EXPERTS
168
  # ============================================================
169
 
170
  class MixtureOfExperts(nn.Module):
 
171
  def __init__(self, config):
172
  super().__init__()
 
173
  self.config = config
 
174
  self.num_experts = config.num_experts
175
+
176
  self.experts = nn.ModuleList([
177
  nn.Sequential(
178
+ nn.Linear(config.hidden_size, config.intermediate_size),
 
 
 
179
  nn.SiLU(),
180
+ nn.Linear(config.intermediate_size, config.hidden_size),
 
 
 
181
  nn.Dropout(config.dropout)
182
+ ) for _ in range(config.num_experts)
 
183
  ])
184
+
185
+ self.router = nn.Linear(config.hidden_size, config.num_experts, bias=False)
 
 
 
 
 
186
  self.aux_loss_coef = 0.01
187
 
188
+ def forward(self, hidden_states, progressive_factor=1.0):
 
 
 
 
 
189
  batch_size, seq_len, hidden_size = hidden_states.shape
190
+ hidden_states_flat = hidden_states.view(-1, hidden_size)
191
+ router_logits = self.router(hidden_states_flat)
192
+ routing_weights = F.softmax(router_logits, dim=-1)
193
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  if self.config.progressive_experts:
195
+ k = int(round(self.config.min_experts + (self.config.max_experts - self.config.min_experts) * progressive_factor))
196
+ k = max(self.config.min_experts, min(k, self.config.max_experts))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  else:
198
  k = self.config.num_experts_per_tok
199
+
200
+ top_k_weights, top_k_indices = torch.topk(routing_weights, k, dim=-1)
201
+ top_k_weights = top_k_weights / top_k_weights.sum(dim=-1, keepdim=True)
202
+
203
+ final_hidden = torch.zeros_like(hidden_states_flat)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  for expert_idx in range(self.num_experts):
205
+ expert_mask = (top_k_indices == expert_idx).any(dim=-1)
 
 
 
 
206
  if expert_mask.any():
207
+ expert_input = hidden_states_flat[expert_mask]
208
+ expert_output = self.experts[expert_idx](expert_input)
209
+ expert_weights = top_k_weights[expert_mask].mean(dim=-1, keepdim=True)
210
+ final_hidden[expert_mask] += expert_output * expert_weights
211
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  router_probs = routing_weights.mean(dim=0)
 
213
  aux_loss = torch.var(router_probs)
214
+
215
+ return final_hidden.view(batch_size, seq_len, hidden_size), aux_loss * self.aux_loss_coef
 
 
 
 
 
 
 
 
216
 
217
  # ============================================================
218
  # DECODER LAYER
219
  # ============================================================
220
 
221
  class PersadianNanoV4DecoderLayer(nn.Module):
 
222
  def __init__(self, config, layer_idx):
223
  super().__init__()
 
224
  self.config = config
225
+ self.input_layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
226
+ self.self_attn = CompressedSparseAttention(config)
227
+ self.post_attention_layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
 
 
 
 
 
 
 
 
 
 
 
 
228
  self.moe = MixtureOfExperts(config)
229
+
230
  if config.use_hyper_connection:
231
+ self.hc_attention = AdaptiveHyperConnection(config.hidden_size)
232
+ self.hc_moe = AdaptiveHyperConnection(config.hidden_size)
233
 
234
+ def forward(self, hidden_states, attention_mask=None, position_ids=None, progressive_factor=1.0):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  residual = hidden_states
236
+ hidden_states = self.input_layernorm(hidden_states)
237
+ attn_output = self.self_attn(hidden_states, attention_mask, position_ids)
238
+
 
 
 
 
 
 
 
 
239
  if self.config.use_hyper_connection:
240
+ hidden_states = self.hc_attention(attn_output, residual)
 
 
 
 
 
241
  else:
242
  hidden_states = residual + attn_output
243
+
 
 
 
 
244
  residual = hidden_states
245
+ hidden_states = self.post_attention_layernorm(hidden_states)
246
+ moe_output, aux_loss = self.moe(hidden_states, progressive_factor)
247
+
 
 
 
 
 
 
 
248
  if self.config.use_hyper_connection:
249
+ hidden_states = self.hc_moe(moe_output, residual)
 
 
 
 
 
250
  else:
251
  hidden_states = residual + moe_output
252
+
253
  return hidden_states, aux_loss
254
 
 
255
  # ============================================================
256
+ # MAIN MODEL (Original)
257
  # ============================================================
258
 
259
  class PersadianNanoV4Model(nn.Module):
 
260
  def __init__(self, config):
261
  super().__init__()
 
262
  self.config = config
263
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
264
+ self.layers = nn.ModuleList([PersadianNanoV4DecoderLayer(config, i) for i in range(config.num_hidden_layers)])
265
+ self.norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
266
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  self.lm_head.weight = self.embed_tokens.weight
 
 
268
  self.apply(self._init_weights)
269
+
270
  def _init_weights(self, module):
 
271
  if isinstance(module, nn.Linear):
272
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
 
 
 
 
 
 
273
  if module.bias is not None:
274
  nn.init.zeros_(module.bias)
 
275
  elif isinstance(module, nn.Embedding):
276
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
277
+
278
+ def forward(self, input_ids, attention_mask=None, progressive_factor=1.0):
 
 
 
 
 
 
 
 
 
 
 
279
  batch_size, seq_len = input_ids.shape
280
+ hidden_states = self.embed_tokens(input_ids)
281
+ position_ids = torch.arange(seq_len, device=input_ids.device).unsqueeze(0)
282
+
 
 
 
 
 
 
 
 
 
 
 
283
  if attention_mask is None:
284
+ mask = torch.triu(torch.full((seq_len, seq_len), float("-inf"), device=input_ids.device), diagonal=1)
 
 
 
 
 
 
 
 
 
285
  attention_mask = mask.unsqueeze(0).unsqueeze(0)
286
+
287
+ total_aux_loss = torch.tensor(0.0, device=input_ids.device)
 
 
 
 
288
  for layer in self.layers:
289
+ hidden_states, aux_loss = layer(hidden_states, attention_mask, position_ids, progressive_factor)
 
 
 
 
 
 
 
290
  total_aux_loss += aux_loss
291
+
292
  hidden_states = self.norm(hidden_states)
 
293
  logits = self.lm_head(hidden_states)
 
294
  return logits, total_aux_loss
295
+
296
+ def generate(self, input_ids, max_new_tokens=50, temperature=0.7, top_k=50):
 
 
 
 
 
 
 
 
297
  self.eval()
 
298
  for _ in range(max_new_tokens):
 
299
  logits, _ = self.forward(input_ids)
300
+ next_token_logits = logits[:, -1, :] / temperature
 
 
 
 
301
  if top_k > 0:
302
+ values, _ = torch.topk(next_token_logits, top_k)
 
 
 
 
 
303
  min_values = values[:, -1].unsqueeze(-1)
304
+ next_token_logits = torch.where(next_token_logits < min_values, torch.full_like(next_token_logits, float("-inf")), next_token_logits)
305
+ probs = F.softmax(next_token_logits, dim=-1)
306
+ next_token = torch.multinomial(probs, num_samples=1)
307
+ input_ids = torch.cat([input_ids, next_token], dim=1)
308
+ if next_token.item() == self.config.eos_token_id:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  break
 
310
  return input_ids
311
 
 
312
  # ============================================================
313
+ # HF COMPATIBLE WRAPPER
314
  # ============================================================
315
 
316
+ class PersadianNanoV4ConfigHF(PretrainedConfig):
317
+ """HF-compatible config class"""
318
+ model_type = "persadian_nano_v4"
319
+
320
+ def __init__(self, **kwargs):
321
+ super().__init__(**kwargs)
322
+ for key, value in kwargs.items():
323
+ setattr(self, key, value)
324
+
325
+ class PersadianNanoV4ForCausalLM(PreTrainedModel):
326
+ """HF-compatible model class"""
327
+ config_class = PersadianNanoV4ConfigHF
328
+
329
+ def __init__(self, config):
330
+ super().__init__(config)
331
+ # Create the original config object
332
+ original_config = PersadianNanoV4Config()
333
+
334
+ # Copy all attributes from HF config to original config
335
+ for key, value in config.__dict__.items():
336
+ if hasattr(original_config, key):
337
+ setattr(original_config, key, value)
338
+
339
+ self.model = PersadianNanoV4Model(original_config)
340
+
341
+ def forward(self, input_ids, attention_mask=None, labels=None, **kwargs):
342
+ logits, aux_loss = self.model(input_ids, attention_mask)
343
+
344
+ loss = None
345
+ if labels is not None:
346
+ shift_logits = logits[..., :-1, :].contiguous()
347
+ shift_labels = labels[..., 1:].contiguous()
348
+ loss = F.cross_entropy(
349
+ shift_logits.view(-1, shift_logits.size(-1)),
350
+ shift_labels.view(-1)
351
+ )
352
+
353
+ return CausalLMOutputWithPast(
354
+ loss=loss,
355
+ logits=logits,
356
+ )
357
+
358
+ def generate(self, input_ids, **kwargs):
359
+ return self.model.generate(input_ids, **kwargs)