mhough commited on
Commit
c618186
·
verified ·
1 Parent(s): 6ae7a2d

Self-contained HF-compatible ZUNA (vendored arch, byte-identical weights)

Browse files
Files changed (11) hide show
  1. README.md +52 -0
  2. bottlenecks.py +18 -0
  3. config.json +60 -0
  4. configuration_zuna.py +176 -0
  5. conv_stem.py +86 -0
  6. lingua_transformer.py +557 -0
  7. model.safetensors +3 -0
  8. modeling_zuna.py +211 -0
  9. transformer.py +1169 -0
  10. utils.py +169 -0
  11. xattn.py +431 -0
README.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ tags:
4
+ - eeg
5
+ - eeg-foundation-model
6
+ - neurotechx
7
+ - zuna
8
+ library_name: transformers
9
+ pipeline_tag: feature-extraction
10
+ ---
11
+
12
+ # ZUNA — EEG Foundation Model (NeuroTechX HF-compatible build)
13
+
14
+ **ZUNA** is the [Zyphra](https://www.zyphra.com/) EEG foundation model — an
15
+ encoder–decoder architecture (encoder → latent embeddings; flow-matching diffusion
16
+ decoder), **382 M parameters**. This is a **self-contained, HuggingFace
17
+ `trust_remote_code` build** prepared by **NeuroTechX** so the model loads anywhere with
18
+ only `torch` + `transformers` + a few public pip deps — no private packages required.
19
+
20
+ Part of the [NeuroTechX EEG-FM Collection](https://huggingface.co/collections/NeuroTechX/eeg-foundation-models-6a443a79e9a158d702f2e8e1)
21
+ and the 2026 NeuroTechX DL/FM-on-EEG white paper (the successor to Roy et al. 2019).
22
+
23
+ ## Install
24
+ ```bash
25
+ pip install torch transformers vector_quantize_pytorch einx einops
26
+ ```
27
+
28
+ ## Usage
29
+ ```python
30
+ from transformers import AutoModel
31
+ model = AutoModel.from_pretrained("NeuroTechX/zuna", trust_remote_code=True).cuda().eval()
32
+
33
+ # encoder-only latents (the typical EEG-FM use):
34
+ latents = model.encode(encoder_input, seq_lens, ...) # [B, L', encoder_output_dim]
35
+ ```
36
+ Or, in the NeuroTechX stack: `make_hf_encoder("NeuroTechX/zuna")` (emeg-fm).
37
+
38
+ ## What this build changes
39
+ A faithful mirror of the weights in
40
+ [`mhough/zuna-base`](https://huggingface.co/mhough/zuna-base), made **standalone**. The
41
+ original `modeling_zuna.py` imported a private `zuna` package, so it could not load via
42
+ `trust_remote_code` for anyone without that environment. Here the `EncoderDecoder`
43
+ architecture is **vendored** from the Zyphra source + Meta's
44
+ [`lingua`](https://github.com/facebookresearch/lingua) (MIT), imports rewritten to be
45
+ self-contained, and the train-only activation-probe stubbed to a no-op at inference.
46
+ **Weights are byte-identical** to the original.
47
+
48
+ ## Attribution & license
49
+ - **Architecture & weights:** Zyphra (ZUNA).
50
+ - **Vendored framework:** Meta `lingua` (MIT).
51
+ - Licensing of the ZUNA weights should be confirmed with Zyphra; this build is offered
52
+ in good faith for open EEG-FM research and reproducibility.
bottlenecks.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from torch import Tensor
2
+ import torch
3
+
4
+ def imq_kernel2(X: Tensor, Y: Tensor, C: float) -> Tensor:
5
+ dist_sq = torch.cdist(X, Y, p=2).pow(2)
6
+ return C / (C + dist_sq)
7
+
8
+
9
+ @torch.compile()
10
+ def mmd_imq(X: Tensor, Y: Tensor, C: float) -> Tensor:
11
+ K_XX = imq_kernel2(X, X, C)
12
+ K_YY = imq_kernel2(Y, Y, C)
13
+ K_XY = imq_kernel2(X, Y, C)
14
+ n, m = X.size(0), Y.size(0)
15
+ term1 = (K_XX.sum().double() - K_XX.diag().sum().double()).double() / (n*(n-1)) if n>1 else 0.0
16
+ term2 = (K_YY.sum().double() - K_YY.diag().sum().double()).double() / (m*(m-1)) if m>1 else 0.0
17
+ term3 = 2 * K_XY.mean()
18
+ return (term1 + term2 - term3).float()
config.json ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "adaptive_loss_weighting": true,
3
+ "architectures": [
4
+ "ZunaModel"
5
+ ],
6
+ "bottleneck_type": "mmd",
7
+ "codebook_size": 1024,
8
+ "compression_free_conv_stem": false,
9
+ "decoder_encoder_dropout": 0.1,
10
+ "decoder_repa_index": {
11
+ "__float__": "Infinity"
12
+ },
13
+ "decoder_timestep_dropout": 0.1,
14
+ "dim": 1024,
15
+ "distill_output_dim": 0,
16
+ "dont_noise_chan_xyz": false,
17
+ "dropout_type": "zeros",
18
+ "dtype": "float32",
19
+ "encoder_hidden_dim": null,
20
+ "encoder_input_dim": 32,
21
+ "encoder_latent_downsample_factor": 1,
22
+ "encoder_output_dim": 32,
23
+ "encoder_repa_index": {
24
+ "__float__": "Infinity"
25
+ },
26
+ "encoder_sliding_window": 65536,
27
+ "ffn_dim_multiplier": null,
28
+ "head_dim": 64,
29
+ "huber_c": null,
30
+ "init_base_std": 0.02,
31
+ "init_std_factor": "disabled",
32
+ "input_dim": 32,
33
+ "learnable_bias": false,
34
+ "levels": [],
35
+ "max_seqlen": 50,
36
+ "model_type": "zuna",
37
+ "multiple_of": 256,
38
+ "n_heads": 8,
39
+ "n_kv_heads": null,
40
+ "n_layers": 16,
41
+ "norm_eps": 1e-05,
42
+ "num_fine_time_pts": 32,
43
+ "repa_dim": 1024,
44
+ "repa_loss_fn": "cosine",
45
+ "rope_dim": 4,
46
+ "rope_theta": 10000.0,
47
+ "seed": 42,
48
+ "seqlen_t": false,
49
+ "sliding_window": 65536,
50
+ "stft_global_sigma": 0.1,
51
+ "t_dim": 64,
52
+ "tok_idx_type": "{x,y,z,tc}",
53
+ "transformers_version": "5.2.0",
54
+ "weight_tying": false,
55
+ "xattn_sliding_window": 65536,
56
+ "auto_map": {
57
+ "AutoModel": "modeling_zuna.ZunaModel",
58
+ "AutoConfig": "configuration_zuna.ZunaConfig"
59
+ }
60
+ }
configuration_zuna.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ZunaConfig: HuggingFace PretrainedConfig wrapper for the Zyphra ZUNA foundation model.
3
+
4
+ Maps all fields from DecoderTransformerArgs (and its parent chain
5
+ BaseTransformerArgs -> DecoderArgs -> DecoderTransformerArgs) to a standard
6
+ HF config, so the model can be loaded with AutoConfig / trust_remote_code=True.
7
+
8
+ Field sources:
9
+ BaseTransformerArgs – lingua/transformer.py
10
+ DecoderArgs – xattn.py
11
+ DecoderTransformerArgs – transformer.py (AY2latent_bci)
12
+ """
13
+
14
+ from typing import List, Optional, Union
15
+ from transformers import PretrainedConfig
16
+
17
+
18
+ class ZunaConfig(PretrainedConfig):
19
+ model_type = "zuna"
20
+
21
+ def __init__(
22
+ self,
23
+ # ── BaseTransformerArgs ──────────────────────────────────────────────
24
+ dim: int = 1024,
25
+ n_layers: int = 10,
26
+ head_dim: Optional[int] = None,
27
+ n_heads: int = 8,
28
+ n_kv_heads: Optional[int] = None,
29
+ ffn_dim_multiplier: Optional[float] = None,
30
+ multiple_of: int = 256,
31
+ norm_eps: float = 1e-5,
32
+ rope_theta: float = 10000.0,
33
+ init_base_std: Optional[float] = 0.02,
34
+ init_std_factor: str = "disabled",
35
+ max_seqlen: int = 1024,
36
+ rope_dim: int = 1, # 0=NoPE, 1=1D-RoPE, 4=4D-RoPE
37
+ tok_idx_type: Optional[str] = "t_coarse",
38
+ # ── DecoderArgs ─────────────────────────────────────────────────────
39
+ t_dim: int = 64,
40
+ seqlen_t: bool = False,
41
+ # ── DecoderTransformerArgs ───────────────────────────────────────────
42
+ seed: int = 42,
43
+ weight_tying: bool = False,
44
+ sliding_window: int = 128,
45
+ xattn_sliding_window: int = 32,
46
+ input_dim: int = 64,
47
+ decoder_encoder_dropout: float = 0.1,
48
+ decoder_timestep_dropout: float = 0.1,
49
+ encoder_sliding_window: int = 128,
50
+ encoder_input_dim: Optional[int] = None, # defaults to input_dim at runtime
51
+ encoder_output_dim: Optional[int] = None, # defaults to input_dim*2 at runtime
52
+ encoder_latent_downsample_factor: int = 2,
53
+ encoder_hidden_dim: Optional[int] = None,
54
+ adaptive_loss_weighting: bool = False,
55
+ num_fine_time_pts: int = 128,
56
+ dont_noise_chan_xyz: bool = False,
57
+ stft_global_sigma: Union[str, float] = 1.0,
58
+ dropout_type: str = "zero", # {"zero", "rand", "learnable"}
59
+ bottleneck_type: str = "mmd",
60
+ distill_output_dim: int = 0,
61
+ codebook_size: int = 1024,
62
+ levels: Optional[List[int]] = None,
63
+ learnable_bias: bool = False,
64
+ huber_c: Optional[float] = None,
65
+ decoder_repa_index: float = float("inf"),
66
+ encoder_repa_index: float = float("inf"),
67
+ repa_dim: int = 1024,
68
+ repa_loss_fn: str = "cosine",
69
+ compression_free_conv_stem: bool = False,
70
+ **kwargs,
71
+ ):
72
+ # ── BaseTransformerArgs ──────────────────────────────────────────────
73
+ self.dim = dim
74
+ self.n_layers = n_layers
75
+ self.head_dim = head_dim
76
+ self.n_heads = n_heads
77
+ self.n_kv_heads = n_kv_heads
78
+ self.ffn_dim_multiplier = ffn_dim_multiplier
79
+ self.multiple_of = multiple_of
80
+ self.norm_eps = norm_eps
81
+ self.rope_theta = rope_theta
82
+ self.init_base_std = init_base_std
83
+ self.init_std_factor = init_std_factor
84
+ self.max_seqlen = max_seqlen
85
+ self.rope_dim = rope_dim
86
+ self.tok_idx_type = tok_idx_type
87
+ # ── DecoderArgs ─────────────────────────────────────────────────────
88
+ self.t_dim = t_dim
89
+ self.seqlen_t = seqlen_t
90
+ # ── DecoderTransformerArgs ───────────────────────────────────────────
91
+ self.seed = seed
92
+ self.weight_tying = weight_tying
93
+ self.sliding_window = sliding_window
94
+ self.xattn_sliding_window = xattn_sliding_window
95
+ self.input_dim = input_dim
96
+ self.decoder_encoder_dropout = decoder_encoder_dropout
97
+ self.decoder_timestep_dropout = decoder_timestep_dropout
98
+ self.encoder_sliding_window = encoder_sliding_window
99
+ self.encoder_input_dim = encoder_input_dim if encoder_input_dim is not None else input_dim
100
+ self.encoder_output_dim = encoder_output_dim if encoder_output_dim is not None else input_dim * 2
101
+ self.encoder_latent_downsample_factor = encoder_latent_downsample_factor
102
+ self.encoder_hidden_dim = encoder_hidden_dim
103
+ self.adaptive_loss_weighting = adaptive_loss_weighting
104
+ self.num_fine_time_pts = num_fine_time_pts
105
+ self.dont_noise_chan_xyz = dont_noise_chan_xyz
106
+ self.stft_global_sigma = stft_global_sigma
107
+ self.dropout_type = dropout_type
108
+ self.bottleneck_type = bottleneck_type
109
+ self.distill_output_dim = distill_output_dim
110
+ self.codebook_size = codebook_size
111
+ self.levels = levels if levels is not None else []
112
+ self.learnable_bias = learnable_bias
113
+ self.huber_c = huber_c
114
+ self.decoder_repa_index = decoder_repa_index
115
+ self.encoder_repa_index = encoder_repa_index
116
+ self.repa_dim = repa_dim
117
+ self.repa_loss_fn = repa_loss_fn
118
+ self.compression_free_conv_stem = compression_free_conv_stem
119
+
120
+ super().__init__(**kwargs)
121
+
122
+ def to_decoder_transformer_args(self):
123
+ """
124
+ Convert back to a DecoderTransformerArgs dataclass instance so the raw
125
+ Zyphra EncoderDecoder can be instantiated.
126
+ """
127
+ from .transformer import (
128
+ DecoderTransformerArgs,
129
+ )
130
+
131
+ return DecoderTransformerArgs(
132
+ dim=self.dim,
133
+ n_layers=self.n_layers,
134
+ head_dim=self.head_dim,
135
+ n_heads=self.n_heads,
136
+ n_kv_heads=self.n_kv_heads,
137
+ ffn_dim_multiplier=self.ffn_dim_multiplier,
138
+ multiple_of=self.multiple_of,
139
+ norm_eps=self.norm_eps,
140
+ rope_theta=self.rope_theta,
141
+ init_base_std=self.init_base_std,
142
+ init_std_factor=self.init_std_factor,
143
+ max_seqlen=self.max_seqlen,
144
+ rope_dim=self.rope_dim,
145
+ tok_idx_type=self.tok_idx_type,
146
+ t_dim=self.t_dim,
147
+ seqlen_t=self.seqlen_t,
148
+ seed=self.seed,
149
+ weight_tying=self.weight_tying,
150
+ sliding_window=self.sliding_window,
151
+ xattn_sliding_window=self.xattn_sliding_window,
152
+ input_dim=self.input_dim,
153
+ decoder_encoder_dropout=self.decoder_encoder_dropout,
154
+ decoder_timestep_dropout=self.decoder_timestep_dropout,
155
+ encoder_sliding_window=self.encoder_sliding_window,
156
+ encoder_input_dim=self.encoder_input_dim,
157
+ encoder_output_dim=self.encoder_output_dim,
158
+ encoder_latent_downsample_factor=self.encoder_latent_downsample_factor,
159
+ encoder_hidden_dim=self.encoder_hidden_dim,
160
+ adaptive_loss_weighting=self.adaptive_loss_weighting,
161
+ num_fine_time_pts=self.num_fine_time_pts,
162
+ dont_noise_chan_xyz=self.dont_noise_chan_xyz,
163
+ stft_global_sigma=self.stft_global_sigma,
164
+ dropout_type=self.dropout_type,
165
+ bottleneck_type=self.bottleneck_type,
166
+ distill_output_dim=self.distill_output_dim,
167
+ codebook_size=self.codebook_size,
168
+ levels=list(self.levels),
169
+ learnable_bias=self.learnable_bias,
170
+ huber_c=self.huber_c,
171
+ decoder_repa_index=self.decoder_repa_index,
172
+ encoder_repa_index=self.encoder_repa_index,
173
+ repa_dim=self.repa_dim,
174
+ repa_loss_fn=self.repa_loss_fn,
175
+ compression_free_conv_stem=self.compression_free_conv_stem,
176
+ )
conv_stem.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from einops import rearrange
5
+ from typing import Optional
6
+ import math
7
+
8
+ class CausalConv2DStem(nn.Module):
9
+ """
10
+ A 'beefier' Causal 2D Convolutional Stem using standard convolutions.
11
+
12
+ Processes input [B, T, C=2*F] -> [B, T, F_out]. Uses standard Conv2D layers
13
+ for increased parameters. Maintains strict time causality via manual padding.
14
+ Uses PyTorch's truncated normal initialization.
15
+ Structure: Pointwise -> Act -> Conv2D -> Act -> Conv2D -> Act -> Pointwise
16
+
17
+ Args:
18
+ input_features (int): Input features C (even, = 2*F).
19
+ hidden_channels (int): Intermediate convolution channels.
20
+ time_kernel_size (int): Kernel size for time dim (>= 1).
21
+ freq_kernel_size (int): Kernel size for freq dim (>= 1, typically odd).
22
+ compress_channels (bool): If True, output F_out = F. If False, F_out = 2*F.
23
+ activation (nn.Module): Activation function. Default: nn.GELU().
24
+ init_std (float): Std dev for truncated normal weight init. Default: 0.02.
25
+ """
26
+ def __init__(
27
+ self,
28
+ input_features: int,
29
+ hidden_channels: int,
30
+ time_kernel_size: int = 3,
31
+ freq_kernel_size: int = 3,
32
+ compress_channels: bool = True,
33
+ activation: Optional[nn.Module] = None,
34
+ ):
35
+ super().__init__()
36
+ if not (isinstance(input_features, int) and input_features > 0 and input_features % 2 == 0):
37
+ raise ValueError("input_features must be an even positive integer.")
38
+ if not (isinstance(hidden_channels, int) and hidden_channels > 0):
39
+ raise ValueError("hidden_channels must be a positive integer.")
40
+ if not (isinstance(time_kernel_size, int) and time_kernel_size >= 1):
41
+ raise ValueError("time_kernel_size must be >= 1.")
42
+ if not (isinstance(freq_kernel_size, int) and freq_kernel_size >= 1):
43
+ raise ValueError("freq_kernel_size must be >= 1.")
44
+
45
+ self.input_features = input_features
46
+ self.freq_dim = input_features // 2
47
+ self.target_channels = 1 if compress_channels else 2
48
+ self._activation = activation() if activation is not None else nn.GELU()
49
+
50
+ self._time_pad_left = time_kernel_size - 1
51
+ self._freq_pad_sym = (freq_kernel_size - 1) // 2
52
+ self._causal_padding = (self._freq_pad_sym, self._freq_pad_sym, self._time_pad_left, 0)
53
+
54
+ self._pointwise1 = nn.Conv2d(2, hidden_channels, 1, 1, bias=False)
55
+ self._conv1 = nn.Conv2d(hidden_channels, hidden_channels, (time_kernel_size, freq_kernel_size), 1, padding=0, bias=False)
56
+ self._conv2 = nn.Conv2d(hidden_channels, hidden_channels, (time_kernel_size, freq_kernel_size), 1, padding=0, bias=False)
57
+ self._pointwise2 = nn.Conv2d(hidden_channels, self.target_channels, 1, 1, bias=True)
58
+ self.output_features = self.target_channels * self.freq_dim
59
+
60
+
61
+ def reset_parameters(self, std: float):
62
+ """Initialize Conv2d weights with truncated normal and biases with zeros."""
63
+ def init_the_shit(module):
64
+ if isinstance(module, nn.Conv2d):
65
+ nn.init.trunc_normal_(module.weight, mean=0.0, std=std, a=-3*std, b=3*std)
66
+ if module.bias is not None:
67
+ nn.init.zeros_(module.bias)
68
+ self.apply(lambda module: init_the_shit(module))
69
+
70
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
71
+ """ Args: x [B, T, C=2*F]. Returns: [B, T, F_out]. """
72
+ B, T, C = x.shape
73
+ if C != self.input_features: raise ValueError(f"Input C={C} != expected {self.input_features}")
74
+
75
+ x = rearrange(x, 'b t (c f) -> b c t f', c=2, f=self.freq_dim)
76
+ x = self._activation(self._pointwise1(x))
77
+ x = F.pad(x, self._causal_padding)
78
+ x = self._activation(self._conv1(x))
79
+ x = F.pad(x, self._causal_padding)
80
+ x = self._activation(self._conv2(x))
81
+ x = self._pointwise2(x) # [B, target_channels, T, F]
82
+ x = rearrange(x, 'b c t f -> b t (c f)') # [B, T, F_out]
83
+ return x
84
+
85
+ def get_output_dim(self) -> int:
86
+ return self.output_features
lingua_transformer.py ADDED
@@ -0,0 +1,557 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+ from typing import Optional, Union, Tuple
6
+
7
+ import torch
8
+ from torch import nn
9
+ from torch.nn import functional as F
10
+ from torch.nn.attention.flex_attention import (
11
+ BlockMask,
12
+ flex_attention,
13
+ _mask_mod_signature,
14
+ )
15
+
16
+ class _NoProbe: # inference no-op (Meta lingua activation-probe is train-only)
17
+ @staticmethod
18
+ def log_stats(x, name=None):
19
+ return x
20
+ probe = _NoProbe()
21
+ flex_attention_comp = torch.compile(flex_attention, dynamic=True, mode='max-autotune')
22
+
23
+ class InitStdFactor(Enum):
24
+ DISABLED = "disabled" # Init std is divided by 1.0
25
+ GLOBAL_DEPTH = "global_depth" # Init std is divided by sqrt(2*n_layers)
26
+ CURRENT_DEPTH = "current_depth" # Init std is divided by sqrt(2*depth)
27
+ DIM_RATIO = "dim_ratio" # Init std is divided by model_dim/4096
28
+
29
+
30
+ @dataclass
31
+ class BaseTransformerArgs:
32
+
33
+ dim: int = 1024
34
+ n_layers: int = 10
35
+ head_dim: Optional[int] = None
36
+ n_heads: Optional[int] = None
37
+ n_kv_heads: Optional[int] = None
38
+
39
+ ffn_dim_multiplier: Optional[float] = None
40
+
41
+ multiple_of: int = 256
42
+
43
+ norm_eps: float = 1e-5
44
+
45
+ rope_theta: float = 10000.0
46
+
47
+ init_base_std: Optional[float] = None
48
+ init_std_factor: str = "disabled"
49
+
50
+ max_seqlen: int = 1024
51
+ rope_dim: int = 1 # 0 = NoPE, 1 = 1D-RoPE, 4 = 4D=RoPE.
52
+ tok_idx_type: Optional[str] = "t_coarse"
53
+
54
+ def cross_entropy(pred, target, **kwargs):
55
+ return F.nll_loss(
56
+ F.log_softmax(pred.flatten(end_dim=-2).float(), -1),
57
+ target.flatten(end_dim=-1),
58
+ **kwargs,
59
+ )
60
+
61
+
62
+ def repeat_kv(x: torch.Tensor, n_rep: int, dim: int) -> torch.Tensor:
63
+ """torch.repeat_interleave(x, dim=2, repeats=n_rep)"""
64
+ assert dim == 2, "Only dim=2 is supported. Check the implementation for other dims."
65
+ bs, slen, n_kv_heads, head_dim = x.shape
66
+ if n_rep == 1:
67
+ return x
68
+ return (
69
+ x[:, :, :, None, :]
70
+ .expand(bs, slen, n_kv_heads, n_rep, head_dim)
71
+ .reshape(bs, slen, n_kv_heads * n_rep, head_dim)
72
+ )
73
+
74
+
75
+ def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0):
76
+ """
77
+ Precompute the frequency tensor for complex exponentials (cis) with given dimensions.
78
+
79
+ This function calculates a frequency tensor with complex exponentials using the given dimension 'dim'
80
+ and the end index 'end'. The 'theta' parameter scales the frequencies.
81
+ The returned tensor contains complex values in complex64 data type.
82
+
83
+ Args:
84
+ dim (int): Dimension of the frequency tensor.
85
+ end (int): End index for precomputing frequencies.
86
+ theta (float, optional): Scaling factor for frequency computation. Defaults to 10000.0.
87
+
88
+ Returns:
89
+ torch.Tensor: Precomputed frequency tensor with complex exponentials.
90
+ """
91
+
92
+ freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
93
+ t = torch.arange(end, device=freqs.device)
94
+ freqs = torch.outer(t, freqs).float()
95
+
96
+ cos, sin = freqs.cos(), freqs.sin()
97
+
98
+ return torch.stack((cos, -sin, sin, cos), dim=-1).view(*freqs.size(), 2, 2)
99
+
100
+
101
+ def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor, seq_dim: int):
102
+ """
103
+ Reshape frequency tensor for broadcasting it with another tensor.
104
+
105
+ This function reshapes the frequency tensor to have the same shape as the target tensor 'x'
106
+ for the purpose of broadcasting the frequency tensor during element-wise operations.
107
+
108
+ Args:
109
+ freqs_cis (torch.Tensor): Frequency tensor to be reshaped.
110
+ x (torch.Tensor): Target tensor for broadcasting compatibility.
111
+ seq_dim (int): Sequence dimension index.
112
+
113
+ Returns:
114
+ torch.Tensor: Reshaped frequency tensor.
115
+ """
116
+
117
+ ndim = x.ndim
118
+ assert 0 <= seq_dim < ndim
119
+ assert freqs_cis.shape == (
120
+ x.shape[seq_dim],
121
+ x.shape[-3],
122
+ 2,
123
+ 2,
124
+ ), f"freqs_cis vs x: {(freqs_cis.shape, x.shape)}. freqs_cis should be{(x.shape[seq_dim], x.shape[-3], 2, 2)}."
125
+ shape = [
126
+ d if i == seq_dim or i == ndim - 3 else 1 for i, d in enumerate(x.shape[:-2])
127
+ ] + [2, 2]
128
+ return freqs_cis.view(*shape)
129
+
130
+
131
+ def apply_rotary_emb(
132
+ xq: torch.Tensor,
133
+ xk: torch.Tensor,
134
+ seq_dim: int,
135
+ freqs_cis: torch.Tensor,
136
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
137
+
138
+ xq_ = xq.reshape(*xq.shape[:-1], -1, 1, 2) # B S D -> B S D/2 1 2
139
+ xk_ = xk.reshape(*xk.shape[:-1], -1, 1, 2) # B S D -> B S D/2 1 2
140
+
141
+ freqs_cis = reshape_for_broadcast(
142
+ freqs_cis, xq_, seq_dim
143
+ ).float() # S D/2 2 2 -> 1 S 1 D/2 2 2
144
+ xq_out = (xq_ * freqs_cis).sum(5).flatten(3)
145
+ xk_out = (xk_ * freqs_cis).sum(5).flatten(3)
146
+
147
+ return xq_out.type_as(xq), xk_out.type_as(xk)
148
+
149
+
150
+
151
+
152
+
153
+ # Rotary embedding as in xformer
154
+ class RotaryEmbedding(torch.nn.Module):
155
+ """
156
+ RotaryEmbedding Module
157
+ """
158
+
159
+ def __init__(self, theta: float, head_dim: int, max_seqlen: int = 1024, rope_dim: int = 1):
160
+ super().__init__()
161
+
162
+ self.theta = theta
163
+ self.head_dim = head_dim
164
+ self.max_seqlen = max_seqlen
165
+ self.rope_dim = rope_dim
166
+
167
+ assert head_dim % rope_dim == 0, f"head_dim must be divisible by rope_dim, got {head_dim} and {rope_dim}"
168
+
169
+ self.register_buffer(
170
+ "freqs_cis",
171
+ precompute_freqs_cis(dim=head_dim//rope_dim, end=max_seqlen, theta=theta),
172
+ persistent=False,
173
+ )
174
+
175
+ def reset_parameters(self):
176
+ self.freqs_cis[...] = precompute_freqs_cis(
177
+ dim=self.head_dim//self.rope_dim, end=self.max_seqlen, theta=self.theta
178
+ )
179
+
180
+ def forward(
181
+ self, seqlen: Optional[int] = None, tok_idx: Optional[torch.Tensor] = None
182
+ ):
183
+ """
184
+ Return freqs_cis corresponding to consecutive seqlen positions or the corresponding tok_idx positions
185
+ Args:
186
+ seqlen (int): Contiguous sequence length
187
+ tok_idx (torch.Tensor[int]): Position indices of each token. This overrides seqlen.
188
+
189
+ Returns:
190
+ Tuple(torch.Tensor, torch.Tensor): Embedded input tensor and freqs_cis
191
+ """
192
+
193
+ tok_idx = None # HARDCODE (CW)! SEE NOTE BELOW. WILL USE SEQLEN PATH.
194
+
195
+ test = (seqlen is not None) or (tok_idx is not None)
196
+ assert test, "Should provide atleast seqlen or tok_idx"
197
+ if tok_idx is not None:
198
+ return self.freqs_cis[tok_idx] # NOTE: DONT WANT TO INDEX WITH TOK_IDX HERE AND THEN AGAIN INSIDE ATTENTION.FORWARD - DOUBLE DOING
199
+ elif seqlen is not None:
200
+ return self.freqs_cis[0:seqlen]
201
+
202
+
203
+ class RMSNorm(nn.Module):
204
+ """
205
+ Initialize the RMSNorm normalization layer.
206
+
207
+ Args:
208
+ dim (int): The dimension of the input tensor.
209
+ eps (float, optional): A small value added to the denominator for numerical stability. Default is 1e-6.
210
+
211
+ Attributes:
212
+ eps (float): A small value added to the denominator for numerical stability.
213
+ weight (nn.Parameter): Learnable scaling parameter.
214
+
215
+ """
216
+
217
+ def __init__(self, dim: int, eps: float = 1e-6, channel_dim=-1):
218
+ super().__init__()
219
+ self.eps = eps
220
+ self.channel_dim = channel_dim
221
+ if channel_dim != -1: #channel_dim is the index of the channel dimension, dim is the number of channels. assume 4 dimensions.
222
+ self.weight = nn.Parameter(torch.ones([1]*channel_dim + [dim] + [1]*(4-channel_dim-1)))
223
+ else:
224
+ self.weight = nn.Parameter(torch.ones(dim))
225
+
226
+
227
+ def _norm(self, x: torch.Tensor):
228
+ return x * torch.rsqrt((x * x).mean(self.channel_dim, keepdim=True) + self.eps)
229
+
230
+ def forward(self, x: torch.Tensor, y: Optional[torch.Tensor] = None) -> torch.Tensor:
231
+ x = probe.log_stats(x, "resid")
232
+ output = self._norm(x.float())
233
+ return (output * self.weight.float()).type_as(x)
234
+
235
+ def reset_parameters(self):
236
+ torch.nn.init.ones_(self.weight) # type: ignore
237
+
238
+ class TiedLinear(nn.Module):
239
+ def __init__(self, tied_module: nn.Module) -> None:
240
+ super().__init__()
241
+ self.tied_module = tied_module
242
+ if not hasattr(tied_module, "weight"):
243
+ raise AttributeError(
244
+ "Provided module does not have attribute 'weight'. Please check your tied_module."
245
+ )
246
+
247
+ def __call__(self, x: torch.Tensor) -> torch.Tensor:
248
+ return F.linear(x, self.tied_module.weight)
249
+
250
+ class Attention(nn.Module):
251
+ def __init__(
252
+ self,
253
+ dim: int,
254
+ head_dim: int,
255
+ n_heads: int,
256
+ n_kv_heads: int,
257
+ rope_theta: float,
258
+ rope_dim: int,
259
+ ):
260
+ super().__init__()
261
+
262
+ self.dim = dim
263
+ self.head_dim = head_dim
264
+ self.rope_theta = rope_theta
265
+ self.rope_dim = rope_dim
266
+
267
+ self.n_heads = n_heads
268
+ self.n_kv_heads = n_kv_heads
269
+ self.heads_per_group = self.n_heads // self.n_kv_heads
270
+
271
+ self.wq = nn.Linear(
272
+ dim,
273
+ n_heads * head_dim,
274
+ bias=False,
275
+ )
276
+ self.wk = nn.Linear(
277
+ dim,
278
+ n_kv_heads * head_dim,
279
+ bias=False,
280
+ )
281
+ self.wv = nn.Linear(
282
+ dim,
283
+ n_kv_heads * head_dim,
284
+ bias=False,
285
+ )
286
+
287
+ self.wo = nn.Linear(
288
+ n_heads * head_dim,
289
+ dim,
290
+ bias=False,
291
+ )
292
+
293
+ def forward(
294
+ self,
295
+ x: torch.Tensor,
296
+ freq_cis: torch.Tensor,
297
+ tok_idx: Optional[torch.Tensor] = None,
298
+ mask: Optional[Union[BlockMask, str]] = None,
299
+ attn_impl: str = "sdpa",
300
+ ) -> torch.Tensor:
301
+
302
+ # B S D
303
+ bsz, seq_len, dim = x.shape
304
+ xq = self.wq(x.view_as(x))
305
+ xk = self.wk(x.view_as(x))
306
+ xv = self.wv(x.view_as(x))
307
+
308
+ output_shape = xq.shape
309
+ # B S D -> B S H Dh (where D = H*Dh)
310
+ xq = xq.view(bsz, seq_len, self.n_heads, self.head_dim)
311
+ xk = xk.view(bsz, seq_len, self.n_kv_heads, self.head_dim)
312
+ xv = xv.view(bsz, seq_len, self.n_kv_heads, self.head_dim)
313
+
314
+ if self.rope_dim==0:
315
+ pass
316
+ elif self.rope_dim==1:
317
+ if tok_idx is not None:
318
+ xq, xk = apply_rotary_emb(xq, xk, 1, freq_cis[tok_idx]) # this edit mirrors what is inside RotaryEmbedding class. To use tok_idx
319
+ else:
320
+ xq, xk = apply_rotary_emb(xq, xk, 1, freq_cis[0:seq_len]) # This is how it was. (SEEMS TO ASSUME WE ARE USING MAX_SEQLEN, NOT TOK_IDX)
321
+ elif self.rope_dim==4:
322
+ freqcis_parts = []
323
+ for i in range(self.rope_dim):
324
+ freqcis_parts.append(freq_cis[tok_idx[:, i]])
325
+ freqcis_4RoPE = torch.cat(freqcis_parts, dim=1)
326
+
327
+ # Now apply 4D-axial-RoPE
328
+ xq, xk = apply_rotary_emb(xq, xk, 1, freqcis_4RoPE)
329
+
330
+
331
+ else:
332
+ print(f"I dont know how to handle {self.rope_dim=} inside lingua.transformer.Attention.forward")
333
+ import IPython; print('\n\nDebug:'); IPython.embed(); import time; time.sleep(0.3)
334
+
335
+
336
+ # print(x, xq, xk, xv, freq_cis, tok_idx, seq_len)
337
+
338
+ # This condition helps us be easily compatible
339
+ # with inference by adding a pluggable KVCache
340
+ if hasattr(self, "kv_cache"):
341
+ xk, xv = self.kv_cache.update(xk, xv, tok_idx)
342
+
343
+ xk = repeat_kv(xk, self.heads_per_group, dim=2)
344
+ xv = repeat_kv(xv, self.heads_per_group, dim=2)
345
+
346
+ if attn_impl == "flex_attention":
347
+ assert mask is None or isinstance(mask, BlockMask)
348
+ xq, xk, xv = map(lambda e: e.transpose(1, 2), (xq, xk, xv))
349
+ if xq.device.type == "mps":
350
+ # MPS does not support flex_attention; fall back to SDPA with dense mask
351
+ if mask is not None:
352
+ S = xq.shape[2]
353
+ q_idx = torch.arange(S, device='cpu')
354
+ kv_idx = torch.arange(S, device='cpu')
355
+ dense_bool = mask.mask_mod(0, 0, q_idx.unsqueeze(1), kv_idx.unsqueeze(0))
356
+ attn_mask = torch.zeros(1, 1, S, S, dtype=xq.dtype, device=xq.device)
357
+ attn_mask.masked_fill_(~dense_bool.unsqueeze(0).unsqueeze(0).to(xq.device), float("-inf"))
358
+ else:
359
+ attn_mask = None
360
+ output = F.scaled_dot_product_attention(xq, xk, xv, attn_mask=attn_mask)
361
+ elif xq.device.type == "cuda":
362
+ output = flex_attention_comp(xq, xk, xv, block_mask=mask)
363
+ else:
364
+ output = flex_attention(xq, xk, xv, block_mask=mask)
365
+ output = output.transpose(1, 2).contiguous() # B H S D -> B S H D
366
+
367
+ elif attn_impl == "sdpa":
368
+ xq, xk, xv = map(lambda e: e.transpose(1, 2), (xq, xk, xv))
369
+ assert mask is None or isinstance(mask, (str, torch.Tensor))
370
+ is_causal = (mask == "causal") if isinstance(mask, str) else False
371
+ mask = mask if isinstance(mask, torch.Tensor) else None
372
+ output = F.scaled_dot_product_attention(
373
+ xq,
374
+ xk,
375
+ xv,
376
+ is_causal=is_causal,
377
+ attn_mask=mask,
378
+ )
379
+ output = output.transpose(1, 2).contiguous() # B H S D -> B S H D
380
+ else:
381
+ raise NotImplementedError(
382
+ f"Attention implementation {attn_impl} not supported"
383
+ )
384
+
385
+ output = self.wo(output.reshape(output_shape))
386
+
387
+ return output
388
+
389
+ def reset_parameters(self, init_std=None, factor=1.0):
390
+ init_std = init_std or (self.dim ** (-0.5))
391
+
392
+ for w in [self.wq, self.wk, self.wv]:
393
+ nn.init.trunc_normal_(
394
+ w.weight,
395
+ mean=0.0,
396
+ std=init_std,
397
+ a=-3 * init_std,
398
+ b=3 * init_std,
399
+ )
400
+
401
+ nn.init.trunc_normal_(
402
+ self.wo.weight,
403
+ mean=0.0,
404
+ std=init_std / factor,
405
+ a=-3 * init_std,
406
+ b=3 * init_std,
407
+ )
408
+
409
+
410
+ class FeedForward(nn.Module):
411
+ def __init__(
412
+ self,
413
+ dim: int,
414
+ hidden_dim: int,
415
+ multiple_of: int,
416
+ ffn_dim_multiplier: Optional[float],
417
+ mp_size: int = 1,
418
+ ):
419
+ super().__init__()
420
+
421
+ hidden_dim = int(2 * hidden_dim / 3)
422
+ if ffn_dim_multiplier is not None:
423
+ hidden_dim = int(ffn_dim_multiplier * hidden_dim)
424
+ hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of)
425
+ assert hidden_dim % mp_size == 0
426
+
427
+ self.dim = dim
428
+ self.hidden_dim = hidden_dim
429
+
430
+ self.w1 = nn.Linear(
431
+ dim,
432
+ hidden_dim,
433
+ bias=False,
434
+ )
435
+ self.w3 = nn.Linear(
436
+ dim,
437
+ hidden_dim,
438
+ bias=False,
439
+ )
440
+ self.w2 = nn.Linear(
441
+ hidden_dim,
442
+ dim,
443
+ bias=False,
444
+ )
445
+
446
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
447
+ # B S D
448
+ x1 = self.w1(x.view_as(x))
449
+ x3 = self.w3(x.view_as(x))
450
+ output = self.w2(F.silu(x1) * x3)
451
+ return output
452
+
453
+ def reset_parameters(self, init_std=None, factor=1.0):
454
+ in_init_std = init_std or (self.dim ** (-0.5))
455
+ out_init_std = init_std or (self.hidden_dim ** (-0.5))
456
+ in_init_std = in_init_std
457
+ out_init_std = out_init_std / factor
458
+ for w in [self.w1, self.w3]:
459
+ nn.init.trunc_normal_(
460
+ w.weight,
461
+ mean=0.0,
462
+ std=in_init_std,
463
+ a=-3 * in_init_std,
464
+ b=3 * in_init_std,
465
+ )
466
+ nn.init.trunc_normal_(
467
+ self.w2.weight,
468
+ mean=0.0,
469
+ std=out_init_std,
470
+ a=-3 * out_init_std,
471
+ b=3 * out_init_std,
472
+ )
473
+
474
+
475
+ class TransformerBlock(nn.Module):
476
+ def __init__(self, args: BaseTransformerArgs):
477
+ super().__init__()
478
+
479
+ assert (args.head_dim is not None) or (
480
+ args.n_heads is not None
481
+ ), "Should specify at least head_dim or n_heads"
482
+ self.head_dim = args.head_dim or args.dim // args.n_heads
483
+ self.n_heads = args.n_heads or args.dim // args.head_dim
484
+ self.n_kv_heads = args.n_kv_heads or self.n_heads
485
+
486
+ assert args.n_heads % self.n_kv_heads == 0
487
+ assert args.dim % args.n_heads == 0
488
+
489
+ self.attention = Attention(
490
+ dim=args.dim,
491
+ head_dim=self.head_dim,
492
+ n_heads=self.n_heads,
493
+ n_kv_heads=self.n_kv_heads,
494
+ rope_theta=args.rope_theta,
495
+ rope_dim=args.rope_dim,
496
+ )
497
+ self.feed_forward = FeedForward(
498
+ dim=args.dim,
499
+ hidden_dim=4 * args.dim,
500
+ multiple_of=args.multiple_of,
501
+ ffn_dim_multiplier=args.ffn_dim_multiplier,
502
+ )
503
+ self.attention_norm = RMSNorm(args.dim, eps=args.norm_eps)
504
+ self.ffn_norm = RMSNorm(args.dim, eps=args.norm_eps)
505
+
506
+ def forward(
507
+ self,
508
+ x: torch.Tensor,
509
+ freq_cis: torch.Tensor,
510
+ tok_idx: Optional[torch.Tensor] = None,
511
+ mask: Optional[Union[BlockMask, str]] = None,
512
+ attn_impl: str = "sdpa",
513
+ do_idx: Optional[torch.Tensor] = None,
514
+ print_layerwise_activation_stats: bool = False,
515
+ ) -> torch.Tensor:
516
+
517
+
518
+ if print_layerwise_activation_stats and do_idx is not None:
519
+
520
+ # Print all the activation stats for the dropped and non-dropped tokens if do_idx is provided
521
+ x_normed = self.attention_norm(x)
522
+ print(f"\n\t Encoder attn_norm (drop-out): mean={x[:, do_idx, :].mean().item():.6f}, std={x[:, do_idx, :].std().item():.6f}", end=" --> ")
523
+ print(f"mean={x_normed[:, do_idx, :].mean().item():.6f}, std={x_normed[:, do_idx, :].std().item():.6f}")
524
+ print(f"\t Encoder attn_norm (non-drop): mean={x[:, ~do_idx, :].mean().item():.6f}, std={x[:, ~do_idx, :].std().item():.6f}", end=" --> ")
525
+ print(f"mean={x_normed[:, ~do_idx, :].mean().item():.6f}, std={x_normed[:, ~do_idx, :].std().item():.6f}")
526
+ h = x + self.attention( # lingua.transformer.Attention
527
+ x_normed,
528
+ freq_cis,
529
+ tok_idx=tok_idx,
530
+ mask=mask,
531
+ attn_impl=attn_impl,
532
+ )
533
+ h_normed = self.ffn_norm(h)
534
+ print(f"\n\t Encoder ffn_norm (drop-out): mean={h[:, do_idx, :].mean().item():.6f}, std={h[:, do_idx, :].std().item():.6f}", end=" --> ")
535
+ print(f"mean={h_normed[:, do_idx, :].mean().item():.6f}, std={h_normed[:, do_idx, :].std().item():.6f}")
536
+ print(f"\t Encoder ffn_norm (non-drop): mean={h[:, ~do_idx, :].mean().item():.6f}, std={h[:, ~do_idx, :].std().item():.6f}", end=" --> ")
537
+ print(f"mean={h_normed[:, ~do_idx, :].mean().item():.6f}, std={h_normed[:, ~do_idx, :].std().item():.6f}")
538
+ out = h + self.feed_forward(h_normed) # lingua.transformer.FeedForward
539
+
540
+ else:
541
+ h = x + self.attention(
542
+ self.attention_norm(x),
543
+ freq_cis,
544
+ tok_idx=tok_idx,
545
+ mask=mask,
546
+ attn_impl=attn_impl,
547
+ )
548
+ out = h + self.feed_forward(self.ffn_norm(h))
549
+
550
+ return out
551
+
552
+ def init_weights(self, init_std=None, factor=1.0):
553
+ self.attention.reset_parameters(init_std, factor)
554
+ self.attention_norm.reset_parameters()
555
+
556
+ self.feed_forward.reset_parameters(init_std, factor)
557
+ self.ffn_norm.reset_parameters()
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:16f00d82e6ac5d42cfdb9d153850d114e8b280fb9dec79ea1de1b9eac03d1271
3
+ size 1528130680
modeling_zuna.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ZunaModel: HuggingFace PreTrainedModel wrapper for the Zyphra ZUNA foundation model.
3
+
4
+ Architecture (from Zyphra source):
5
+ EncoderDecoder
6
+ ├── encoder: EncoderTransformer → produces latent embeddings [B, L', encoder_output_dim]
7
+ └── decoder: DecoderTransformer → flow-matching diffusion decoder
8
+
9
+ The primary user-facing method is encode(), which runs only the encoder and
10
+ returns the latent embeddings. forward() mirrors the Zyphra sample() signature
11
+ for full encode→decode inference.
12
+
13
+ Encoding call (verified against eeg_eval.py and EncoderDecoder.sample()):
14
+
15
+ do_idx = (encoder_input.sum(axis=2) == 0).squeeze(0)
16
+ enc_out, _ = model.model.encoder(
17
+ token_values=encoder_input,
18
+ seq_lens=seq_lens,
19
+ tok_idx=tok_idx,
20
+ do_idx=do_idx,
21
+ )
22
+ # enc_out: [B, L/downsample_factor, encoder_output_dim]
23
+
24
+ State-dict convention: the safetensors file ships keys prefixed with "model."
25
+ which must be stripped before load_state_dict (see convert_weights.py).
26
+ After stripping, keys match EncoderDecoder's submodule names directly:
27
+ encoder.tok_embeddings.weight, decoder.layers.0.*, etc.
28
+ """
29
+
30
+ from dataclasses import dataclass
31
+ from typing import Optional
32
+
33
+ import torch
34
+ from transformers import PreTrainedModel
35
+ from transformers.modeling_outputs import BaseModelOutput
36
+
37
+ from .configuration_zuna import ZunaConfig
38
+
39
+
40
+ class ZunaModel(PreTrainedModel):
41
+ config_class = ZunaConfig
42
+ # The raw Zyphra weights match EncoderDecoder's attribute tree directly
43
+ # (after "model." prefix is stripped in convert_weights.py).
44
+ base_model_prefix = "model"
45
+ supports_gradient_checkpointing = False
46
+
47
+ def __init__(self, config: ZunaConfig):
48
+ super().__init__(config)
49
+
50
+ from .transformer import (
51
+ EncoderDecoder,
52
+ )
53
+
54
+ args = config.to_decoder_transformer_args()
55
+ self.model = EncoderDecoder(args)
56
+
57
+ # Store config fields needed at inference time.
58
+ self.tok_idx_type = config.tok_idx_type
59
+ self.rope_dim = config.rope_dim
60
+
61
+ self.post_init()
62
+
63
+ # ── internal helper ────────────────────────────────────────────────────
64
+
65
+ def _build_tok_idx(
66
+ self,
67
+ encoder_input: torch.Tensor,
68
+ seq_lens: torch.Tensor,
69
+ t_coarse: Optional[torch.Tensor],
70
+ chan_id: Optional[torch.Tensor],
71
+ chan_pos_discrete: Optional[torch.Tensor],
72
+ ) -> Optional[torch.Tensor]:
73
+ """
74
+ Replicate the tok_idx construction from EncoderDecoder.forward()
75
+ (transformer.py lines 1001-1012). Only the types used by the public
76
+ ZUNA checkpoint are implemented here; extend as needed.
77
+ """
78
+ if self.tok_idx_type is None:
79
+ return None
80
+ elif self.tok_idx_type == "t_coarse" and self.rope_dim == 1:
81
+ if t_coarse is None:
82
+ raise ValueError("tok_idx_type='t_coarse' requires t_coarse tensor")
83
+ return t_coarse
84
+ elif self.tok_idx_type == "chan_id" and self.rope_dim == 1:
85
+ if chan_id is None:
86
+ raise ValueError("tok_idx_type='chan_id' requires chan_id tensor")
87
+ return chan_id
88
+ elif self.tok_idx_type == "stack_arange_seqlen" and self.rope_dim == 1:
89
+ return torch.hstack(
90
+ [torch.arange(sl) for sl in seq_lens]
91
+ ).unsqueeze(0).unsqueeze(-1)
92
+ elif self.tok_idx_type == "{x,y,z,tc}" and self.rope_dim == 4:
93
+ if chan_pos_discrete is None or t_coarse is None:
94
+ raise ValueError(
95
+ "tok_idx_type='{x,y,z,tc}' requires chan_pos_discrete and t_coarse"
96
+ )
97
+ return torch.cat((chan_pos_discrete, t_coarse), dim=2)
98
+ else:
99
+ raise ValueError(
100
+ f"Unsupported tok_idx_type={self.tok_idx_type!r} with rope_dim={self.rope_dim}"
101
+ )
102
+
103
+ # ── encode ────────────────────────────────────────────────────────────
104
+
105
+ @torch.no_grad()
106
+ def encode(
107
+ self,
108
+ encoder_input: torch.Tensor,
109
+ seq_lens: torch.Tensor,
110
+ t_coarse: Optional[torch.Tensor] = None,
111
+ chan_id: Optional[torch.Tensor] = None,
112
+ chan_pos_discrete: Optional[torch.Tensor] = None,
113
+ tok_idx: Optional[torch.Tensor] = None,
114
+ ) -> torch.Tensor:
115
+ """
116
+ Run only the encoder and return latent embeddings.
117
+
118
+ Args:
119
+ encoder_input: EEG signal tensor [B, seqlen, input_dim]
120
+ seq_lens: Sequence lengths [B] (used for document masking)
121
+ t_coarse: Coarse time index [B, seqlen, 1] (needed when tok_idx_type='t_coarse')
122
+ chan_id: Channel IDs [B, seqlen, 1] (needed when tok_idx_type='chan_id')
123
+ chan_pos_discrete: Discrete 3D positions [B, seqlen, 3] (needed for 4D-RoPE)
124
+ tok_idx: Pre-built token index tensor; if provided, skips the
125
+ automatic _build_tok_idx() construction above.
126
+
127
+ Returns:
128
+ Latent embeddings [B, seqlen // downsample_factor, encoder_output_dim]
129
+ """
130
+ if encoder_input.ndim == 2:
131
+ encoder_input = encoder_input.unsqueeze(0)
132
+
133
+ if tok_idx is None:
134
+ tok_idx = self._build_tok_idx(
135
+ encoder_input, seq_lens, t_coarse, chan_id, chan_pos_discrete
136
+ )
137
+
138
+ # Identify dropped-out channels: columns that are all-zero
139
+ # (mirrors EncoderDecoder.sample() line 1055)
140
+ do_idx = (encoder_input.sum(axis=2) == 0).squeeze(0)
141
+
142
+ enc_out, _ = self.model.encoder(
143
+ token_values=encoder_input,
144
+ seq_lens=seq_lens,
145
+ tok_idx=tok_idx,
146
+ do_idx=do_idx,
147
+ )
148
+ return enc_out # [B, L', encoder_output_dim]
149
+
150
+ # ── forward ───────────────────────────────────────────────────────────
151
+
152
+ def forward(
153
+ self,
154
+ encoder_input: torch.Tensor,
155
+ seq_lens: torch.Tensor,
156
+ t_coarse: Optional[torch.Tensor] = None,
157
+ chan_id: Optional[torch.Tensor] = None,
158
+ chan_pos_discrete: Optional[torch.Tensor] = None,
159
+ tok_idx: Optional[torch.Tensor] = None,
160
+ sample_steps: int = 50,
161
+ cfg: float = 1.0,
162
+ return_latents: bool = False,
163
+ ) -> BaseModelOutput:
164
+ """
165
+ Full encode → diffusion-decode pass, mirroring EncoderDecoder.sample().
166
+
167
+ Args:
168
+ encoder_input: EEG signal tensor [B, seqlen, input_dim]
169
+ seq_lens: Sequence lengths [B]
170
+ t_coarse: Coarse time index [B, seqlen, 1]
171
+ chan_id: Channel IDs [B, seqlen, 1]
172
+ chan_pos_discrete: Discrete 3D positions [B, seqlen, 3]
173
+ tok_idx: Pre-built token index (overrides auto construction)
174
+ sample_steps: Number of flow-matching diffusion steps (default 50)
175
+ cfg: Classifier-free guidance scale (1.0 = no CFG)
176
+ return_latents: If True, also return encoder latents in hidden_states
177
+
178
+ Returns:
179
+ BaseModelOutput with:
180
+ last_hidden_state: reconstructed signal [B, seqlen, input_dim]
181
+ hidden_states: (enc_out,) if return_latents else None
182
+ """
183
+ if encoder_input.ndim == 2:
184
+ encoder_input = encoder_input.unsqueeze(0)
185
+
186
+ if tok_idx is None:
187
+ tok_idx = self._build_tok_idx(
188
+ encoder_input, seq_lens, t_coarse, chan_id, chan_pos_discrete
189
+ )
190
+
191
+ reconstruction, _ = self.model.sample(
192
+ encoder_input=encoder_input,
193
+ seq_lens=seq_lens,
194
+ tok_idx=tok_idx,
195
+ sample_steps=sample_steps,
196
+ cfg=cfg,
197
+ )
198
+
199
+ hidden_states = None
200
+ if return_latents:
201
+ latents = self.encode(
202
+ encoder_input=encoder_input,
203
+ seq_lens=seq_lens,
204
+ tok_idx=tok_idx,
205
+ )
206
+ hidden_states = (latents,)
207
+
208
+ return BaseModelOutput(
209
+ last_hidden_state=reconstruction,
210
+ hidden_states=hidden_states,
211
+ )
transformer.py ADDED
@@ -0,0 +1,1169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, field
2
+ from math import inf
3
+ from typing import Optional, Tuple, Union, List
4
+ from copy import deepcopy
5
+ import torch
6
+ from torch import nn
7
+ from torch.nn import Parameter
8
+ from torch.nn import functional as F
9
+ from torch.nn.attention.flex_attention import (
10
+ create_block_mask,
11
+ BlockMask,
12
+ _mask_mod_signature,
13
+ noop_mask,
14
+ )
15
+
16
+ torch._dynamo.config.capture_scalar_outputs = True
17
+
18
+ from .lingua_transformer import (
19
+ RMSNorm,
20
+ InitStdFactor,
21
+ RotaryEmbedding,
22
+ TransformerBlock,
23
+ )
24
+ from .xattn import DecoderBlock, FourierConditioner, DecoderArgs, AdaRMSNorm
25
+ from .conv_stem import CausalConv2DStem
26
+ from .bottlenecks import mmd_imq
27
+ from vector_quantize_pytorch import SimVQ, FSQ
28
+ import functools
29
+
30
+ # def create_causal_mask(seqlen, attn_impl, sliding_window):
31
+ # if attn_impl == "sdpa":
32
+ # return "causal"
33
+ # elif attn_impl == "flex_attention":
34
+ # return create_block_mask(causal_mask, None, None, seqlen, seqlen)
35
+ # else:
36
+ # raise NotImplementedError(
37
+ # f"Attention {attn_impl} with {sliding_window} sliding window not implemented"
38
+ # )
39
+
40
+
41
+ def create_document_mask(lengths: torch.Tensor,
42
+ kv_lengths: Optional[torch.Tensor] = None, # for cross-attn
43
+ base_mask_mod: Optional[_mask_mod_signature] = None):
44
+ """
45
+ Create a document mask. Grabbing code from lingua.transformer
46
+ """
47
+
48
+ def generate_doc_mask_mod(
49
+ mask_mod: _mask_mod_signature,
50
+ lengths: torch.Tensor,
51
+ kv_lengths: Optional[torch.Tensor] = None, # for cross-attn
52
+ ) -> _mask_mod_signature:
53
+ """Generates mask mods that apply to inputs to flex attention in the sequence stacked
54
+ format.
55
+
56
+ Args:
57
+ mask_mod: The mask mod to apply to the documents
58
+ lengths: Lengths of each document
59
+
60
+ Note:
61
+ What is the sequence stacked format? When assembling batches of inputs, we
62
+ take multiple sequences and stack them together to form 1 large sequence. We then
63
+ use masking to ensure that the attention scores are only applied to tokens within
64
+ the same document.
65
+
66
+ Example:
67
+
68
+ - Square mask
69
+ doc_mask lengths
70
+ a a b b b c c 2 3 2
71
+ a 1 0 0 0 0 0 0
72
+ a 1 1 0 0 0 0 0
73
+ b 0 0 1 0 0 0 0
74
+ b 0 0 1 1 0 0 0
75
+ b 0 0 1 1 1 0 0
76
+ c 0 0 0 0 0 1 0
77
+ c 0 0 0 0 0 1 1
78
+
79
+ """
80
+
81
+ def lengths_to_start_ids(lengths):
82
+ doc_start = lengths.cumsum(0)
83
+ doc_start = doc_start.roll(1)
84
+ doc_start[0] = 0
85
+ return doc_start
86
+
87
+ def lengths_to_local_ids(lengths):
88
+ assert lengths.ndim == 1
89
+ nb_seqs = lengths.size(0)
90
+ total_seqlen = lengths.sum()
91
+ # This gives the document id of each token
92
+ doc_id = torch.repeat_interleave(lengths)
93
+ # Compute document start for each document
94
+ doc_start = lengths_to_start_ids(lengths)
95
+ # Compute document start for each token
96
+ doc_start = doc_start[doc_id]
97
+ # Compute the position of each token within each document
98
+ tok_id = torch.arange(total_seqlen, device=lengths.device) - doc_start
99
+ return doc_id, tok_id
100
+
101
+ kv_lengths = kv_lengths if kv_lengths is not None else lengths
102
+ q_document_id, q_token_id = lengths_to_local_ids(lengths)
103
+ kv_document_id, kv_token_id = lengths_to_local_ids(kv_lengths)
104
+ q_max_idx = lengths.sum() - 1
105
+ kv_max_idx = kv_lengths.sum() - 1
106
+
107
+ def doc_mask_mod(b, h, q_idx, kv_idx):
108
+ q_idx_cap = torch.minimum(q_max_idx, q_idx)
109
+ kv_idx_cap = torch.minimum(kv_max_idx, kv_idx)
110
+ valid_idx = (q_idx <= q_max_idx) & (kv_idx <= kv_max_idx)
111
+ same_doc = q_document_id[q_idx_cap] == kv_document_id[kv_idx_cap]
112
+ q_logical = q_token_id[q_idx_cap]
113
+ kv_logical = kv_token_id[kv_idx_cap]
114
+ inner_mask = mask_mod(b, h, q_logical, kv_logical)
115
+ return same_doc & inner_mask & valid_idx
116
+
117
+ return doc_mask_mod
118
+
119
+ if base_mask_mod is None:
120
+ base_mask_mod = noop_mask
121
+
122
+ if torch.cuda.is_available():
123
+ doc_mask_mod = generate_doc_mask_mod(base_mask_mod, lengths)
124
+ return create_block_mask(doc_mask_mod, None, None, lengths.sum().item(), lengths.sum().item())
125
+ else:
126
+ # create_block_mask runs on CPU; ensure closure tensors are on CPU too
127
+ doc_mask_mod = generate_doc_mask_mod(base_mask_mod, lengths.cpu())
128
+ return create_block_mask(doc_mask_mod, None, None, lengths.sum().item(), lengths.sum().item(),
129
+ device='cpu', _compile=False)
130
+
131
+
132
+ def attention_flops_per_token(n_layers, seq_len, dim, causal):
133
+ # Formula from https://github.com/Dao-AILab/flash-attention/blob/main/benchmarks/benchmark_flash_attention.py#L27-L30
134
+ return 3.5 * (4 * n_layers * seq_len * dim // (2 if causal else 1))
135
+
136
+
137
+ def get_num_flop_per_token(
138
+ num_non_embed_params: int, n_layers: int, dim: int, seq_len: int
139
+ ) -> int:
140
+ return 6 * num_non_embed_params + attention_flops_per_token(
141
+ n_layers, seq_len, dim, True
142
+ )
143
+
144
+
145
+ def causal_mask(b, h, q_idx, kv_idx):
146
+ return q_idx >= kv_idx
147
+
148
+
149
+ def extract_non_registers(h: torch.Tensor, num_groups: int, original_seqlen: int = None, downsample_factor: int = None) -> torch.Tensor:
150
+ """
151
+ Extract non-register tokens from the output tensor.
152
+ Args:
153
+ h: Output tensor from transformer layers [B, interleaved_seqlen, D].
154
+ num_groups: Number of groups used in interleaving.
155
+ original_seqlen: The sequence length of the input *before* padding
156
+ and interleaving. Used to trim non-register tokens.
157
+
158
+ Returns:
159
+ non_registers: [B, original_seqlen, D]
160
+ """
161
+ bsz, seq_len, dim = h.shape
162
+ # seq_len should be num_groups*(df+1)
163
+ h = h.reshape(bsz, num_groups, downsample_factor + 1, dim)
164
+
165
+ # Extract non-register tokens (indices 1 to df)
166
+ non_registers = h[:, :, 1:, :]
167
+ # Flatten back to sequence dimension
168
+ padded_seqlen = num_groups * downsample_factor
169
+ non_registers = non_registers.reshape(bsz, padded_seqlen, dim)
170
+ # Trim back to the original sequence length, removing padding effects
171
+ non_registers = non_registers[:, :original_seqlen, :] # [B, original_seqlen, D]
172
+
173
+ return non_registers.contiguous()
174
+
175
+ @torch.compile()
176
+ def huber_loss(target, logits, huber_c):
177
+ return huber_c * (torch.sqrt((target - logits) ** 2 + huber_c**2) - huber_c)
178
+
179
+ @torch.compile()
180
+ def cosine_similarity_loss(input, target):
181
+ return (1 - F.cosine_similarity(input, target, dim=-1).mean())
182
+
183
+ @torch.compile()
184
+ def huber_cosine_weighted(input, target, huber_c = 0.1):
185
+ # Compute the Huber loss
186
+ h_loss = huber_loss(input, target, huber_c).mean() * 0.5
187
+
188
+ # Compute the cosine similarity
189
+ cosine_sim = cosine_similarity_loss(input, target)
190
+
191
+ # Combine the two losses
192
+ combined_loss = h_loss + cosine_sim
193
+
194
+ return combined_loss
195
+
196
+ @dataclass
197
+ class DecoderTransformerArgs(DecoderArgs):
198
+
199
+ seed: int = 42
200
+
201
+ weight_tying: bool = False
202
+ sliding_window: int = 128
203
+ xattn_sliding_window: int = 32
204
+ input_dim: int = 64
205
+
206
+ decoder_encoder_dropout: float = 0.1
207
+ decoder_timestep_dropout: float = 0.1
208
+
209
+ encoder_sliding_window: int = 128
210
+ encoder_input_dim: int = input_dim
211
+ encoder_output_dim: int = input_dim*2
212
+ encoder_latent_downsample_factor: int = 2
213
+ encoder_hidden_dim: Optional[int] = None
214
+
215
+ adaptive_loss_weighting: bool = False
216
+ num_fine_time_pts: int = 128
217
+ dont_noise_chan_xyz: bool = False
218
+ stft_global_sigma: Union[str, float] = 1.0
219
+
220
+ dropout_type: str = "zero" # {"zero", "rand", "learnable"}
221
+
222
+ bottleneck_type: str = "mmd"
223
+ distill_output_dim: int = 0
224
+ codebook_size: int = 1024
225
+ levels: List[int] = field(default_factory=list)
226
+ init_base_std: float = 0.02
227
+ learnable_bias: bool = False
228
+
229
+ huber_c: Optional[float] = None
230
+
231
+ decoder_repa_index: float = float('inf')
232
+ encoder_repa_index: float = float('inf')
233
+ repa_dim: int = 1024
234
+ repa_loss_fn: str = "cosine"
235
+
236
+ compression_free_conv_stem: bool = False
237
+
238
+ max_seqlen: int = 1024
239
+
240
+ class BaseTransformerDecoder(nn.Module):
241
+ def __init__(self, args: DecoderTransformerArgs):
242
+ super().__init__()
243
+
244
+ self.dim = args.dim
245
+ self.init_base_std = args.init_base_std
246
+ self.init_std_factor = InitStdFactor(args.init_std_factor)
247
+ self.max_seqlen = args.max_seqlen
248
+ self.rope_embeddings = RotaryEmbedding(
249
+ theta=args.rope_theta,
250
+ head_dim=args.head_dim or args.dim // args.n_heads,
251
+ max_seqlen=args.max_seqlen,
252
+ rope_dim=args.rope_dim,
253
+ )
254
+
255
+ self.layers = nn.ModuleList()
256
+ for _ in range(args.n_layers):
257
+ self.layers.append(DecoderBlock(args))
258
+
259
+ self.repa_index = args.decoder_repa_index
260
+ if self.repa_index != inf:
261
+ # self.repa_proj = nn.Linear(args.dim, args.repa_dim)
262
+ self.repa_proj = nn.Sequential(
263
+ nn.Linear(args.dim, args.repa_dim),
264
+ nn.SiLU(),
265
+ nn.Linear(args.repa_dim, args.repa_dim),
266
+ nn.SiLU(),
267
+ nn.Linear(args.repa_dim, args.repa_dim),
268
+ )
269
+ self.repa_norm = AdaRMSNorm(args.t_dim, args.dim, eps=args.norm_eps)
270
+ self.repa_loss_fn = cosine_similarity_loss if args.repa_loss_fn == "cosine" else huber_cosine_weighted
271
+
272
+ def forward(
273
+ self,
274
+ h,
275
+ x_attended,
276
+ t,
277
+ tok_idx: Optional[torch.Tensor] = None,
278
+ cross_tok_idx: Optional[torch.Tensor] = None,
279
+ mask: Optional[Union[BlockMask, str]] = None,
280
+ cross_attn_mask: Optional[Union[BlockMask, str]] = None,
281
+ attn_impl: str = "sdpa",
282
+ repa_target: Optional[torch.Tensor] = None,
283
+ do_idx: Optional[torch.Tensor] = None,
284
+ ):
285
+
286
+ freq_cis = self.rope_embeddings(seqlen=self.max_seqlen, tok_idx=tok_idx)
287
+ repa_loss = None
288
+
289
+ for i, layer in enumerate(self.layers): # all these layers are type 'xattn.DecoderBlock'
290
+ h = layer(h,
291
+ x_attended,
292
+ t,
293
+ freq_cis,
294
+ tok_idx=tok_idx,
295
+ cross_tok_idx=cross_tok_idx,
296
+ self_attn_mask=mask,
297
+ cross_attn_mask=cross_attn_mask,
298
+ attn_impl=attn_impl,
299
+ do_idx=do_idx,
300
+ )
301
+
302
+ if self.training and self.repa_index != inf and i == self.repa_index:
303
+ repa_loss = self.repa_loss_fn(self.repa_proj(self.repa_norm(h, t)).float(), repa_target,)
304
+
305
+ return h, repa_loss
306
+
307
+
308
+
309
+ def reset_parameters(self):
310
+ # Either use fixed base std or sqrt model dim
311
+ self.rope_embeddings.reset_parameters()
312
+
313
+ def init_weights(self):
314
+ self.reset_parameters()
315
+ for depth, layer in enumerate(self.layers):
316
+ factor = {
317
+ InitStdFactor.CURRENT_DEPTH: (2 * (depth + 1)) ** 0.5,
318
+ InitStdFactor.GLOBAL_DEPTH: (2 * (len(self.layers) + 1)) ** 0.5,
319
+ InitStdFactor.DIM_RATIO: self.dim / 4096,
320
+ InitStdFactor.DISABLED: 1.0,
321
+ }[self.init_std_factor]
322
+
323
+ layer.init_weights(self.init_base_std, factor)
324
+
325
+ # Add these lines for repa_proj initialization
326
+ if self.repa_index != float('inf'):
327
+ init_std = self.init_base_std or (self.dim ** (-0.5))
328
+ self.repa_norm.reset_parameters() # Ensure repa_norm is also reset
329
+
330
+ #now repa_proj is nn.Sequential, let's do it in a loop
331
+ for i, layer in enumerate(self.repa_proj):
332
+ if isinstance(layer, nn.Linear):
333
+ nn.init.trunc_normal_(
334
+ layer.weight,
335
+ mean=0.0,
336
+ std=init_std,
337
+ a=-3 * init_std,
338
+ b=3 * init_std,
339
+ )
340
+ # nn.init.zeros_(layer.weight)
341
+ if layer.bias is not None:
342
+ nn.init.zeros_(layer.bias)
343
+
344
+
345
+ class BaseTransformer(nn.Module):
346
+ def __init__(self, args: DecoderTransformerArgs):
347
+ super().__init__()
348
+
349
+ self.dim = args.dim
350
+ self.init_base_std = args.init_base_std
351
+ self.init_std_factor = InitStdFactor(args.init_std_factor)
352
+ self.max_seqlen = args.max_seqlen
353
+ self.rope_embeddings = RotaryEmbedding(
354
+ theta=args.rope_theta,
355
+ head_dim=args.head_dim or args.dim // args.n_heads,
356
+ max_seqlen=args.max_seqlen,
357
+ rope_dim=args.rope_dim,
358
+ )
359
+
360
+ self.layers = nn.ModuleList()
361
+ for _ in range(args.n_layers):
362
+ self.layers.append(TransformerBlock(args))
363
+
364
+ self.repa_index = args.encoder_repa_index
365
+ if self.repa_index != inf:
366
+ self.repa_proj = nn.Linear(args.dim, args.repa_dim)
367
+ self.repa_norm = RMSNorm(args.dim, eps=args.norm_eps)
368
+ self.repa_loss_fn = cosine_similarity_loss if args.repa_loss_fn == "cosine" else huber_cosine_weighted
369
+
370
+
371
+ def forward(
372
+ self,
373
+ h,
374
+ tok_idx: Optional[torch.Tensor] = None,
375
+ mask: Optional[Union[BlockMask, str]] = None,
376
+ attn_impl: str = "sdpa",
377
+ repa_target: Optional[torch.Tensor] = None,
378
+ do_idx: Optional[torch.Tensor] = None,
379
+ **kwargs
380
+ ):
381
+
382
+ freq_cis = self.rope_embeddings(seqlen=self.max_seqlen,
383
+ tok_idx=tok_idx
384
+ )
385
+ repa_loss = None
386
+
387
+ for i, layer in enumerate(self.layers): # all these layers are type 'TransformerBlock'
388
+ h = layer(h,
389
+ freq_cis,
390
+ tok_idx=tok_idx,
391
+ mask=mask,
392
+ attn_impl=attn_impl,
393
+ do_idx=do_idx,
394
+ )
395
+
396
+ if self.training and self.repa_index != inf and i == self.repa_index:
397
+ repa_loss = cosine_similarity_loss(self.repa_proj(self.repa_norm(extract_non_registers(h, **kwargs))).float(), repa_target,)
398
+
399
+
400
+ return h, repa_loss
401
+
402
+ def reset_parameters(self):
403
+ # Either use fixed base std or sqrt model dim
404
+ self.rope_embeddings.reset_parameters()
405
+
406
+ def init_weights(self):
407
+ self.reset_parameters()
408
+ for depth, layer in enumerate(self.layers):
409
+ factor = {
410
+ InitStdFactor.CURRENT_DEPTH: (2 * (depth + 1)) ** 0.5,
411
+ InitStdFactor.GLOBAL_DEPTH: (2 * (len(self.layers) + 1)) ** 0.5,
412
+ InitStdFactor.DIM_RATIO: self.dim / 4096,
413
+ InitStdFactor.DISABLED: 1.0,
414
+ }[self.init_std_factor]
415
+
416
+ layer.init_weights(self.init_base_std, factor)
417
+
418
+ # Add these lines for repa_proj initialization
419
+ if self.repa_index != float('inf'):
420
+ init_std = self.init_base_std or (self.dim ** (-0.5))
421
+
422
+ nn.init.trunc_normal_(
423
+ self.repa_proj.weight,
424
+ mean=0.0,
425
+ std=init_std,
426
+ a=-3 * init_std,
427
+ b=3 * init_std,
428
+ )
429
+ if self.repa_proj.bias is not None:
430
+ nn.init.zeros_(self.repa_proj.bias)
431
+ self.repa_norm.reset_parameters()
432
+
433
+ class DecoderTransformer(BaseTransformerDecoder):
434
+ def __init__(self, args: DecoderTransformerArgs):
435
+ super().__init__(args)
436
+ self.weight_tying = False
437
+ self.sliding_window = args.sliding_window
438
+ self.xattn_sliding_window = args.xattn_sliding_window
439
+ if args.huber_c is not None:
440
+ self.huber_c = args.huber_c
441
+ else:
442
+ self.huber_c = None
443
+
444
+ self.tok_embeddings = nn.Linear(args.input_dim, args.dim,)
445
+
446
+ self.t_embedder = FourierConditioner(args.t_dim, std=args.init_base_std)
447
+
448
+ self.encoder_proj = nn.Linear(args.encoder_output_dim, args.dim)
449
+
450
+ self.norm = AdaRMSNorm(args.t_dim, args.dim, eps=args.norm_eps)
451
+
452
+ self.output = nn.Linear(
453
+ args.dim,
454
+ args.input_dim,
455
+ bias=False,
456
+ )
457
+ self.init_base_std = args.init_base_std
458
+ self.use_compression_free_conv_stem = False
459
+ if args.compression_free_conv_stem:
460
+ self.use_compression_free_conv_stem = True
461
+
462
+ self.compression_free_conv_stem_input = CausalConv2DStem(
463
+ input_features = args.input_dim,
464
+ hidden_channels = 32,
465
+ activation = nn.SELU,
466
+ compress_channels=False,
467
+ )
468
+ self.compression_free_conv_stem_output = CausalConv2DStem(
469
+ input_features = args.input_dim,
470
+ hidden_channels = 32,
471
+ activation = nn.SELU,
472
+ compress_channels=False,
473
+ )
474
+
475
+ self.adaptive_loss_weighting = args.adaptive_loss_weighting
476
+
477
+
478
+ def forward(
479
+ self,
480
+ tokens: torch.Tensor,
481
+ cross_attended: torch.Tensor,
482
+ timeD: torch.Tensor,
483
+ seq_lens: torch.Tensor, # for document masking packed sequences in self-attention
484
+ cross_seq_lens: torch.Tensor, # for document masking packed sequences in cross-attention
485
+ target: Optional[torch.Tensor] = None,
486
+ tok_idx: Optional[torch.Tensor] = None,
487
+ cross_tok_idx: Optional[torch.Tensor] = None,
488
+ mask: Optional[Union[BlockMask, torch.Tensor, str]] = None,
489
+ cross_attn_mask: Optional[Union[BlockMask, str]] = None,
490
+ attn_impl: str = "flex_attention",
491
+ time_masks: Optional[torch.Tensor] = None,
492
+ channel_loss_weighting: Optional[torch.Tensor] = None, # [1, 1, input_dim*2]
493
+ repa_target: Optional[torch.Tensor] = None,
494
+ freq_masks: Optional[torch.Tensor] = None,
495
+ do_idx: Optional[torch.Tensor] = None,
496
+ print_layerwise_activation_stats: bool = False,
497
+ ):
498
+
499
+ tokens = tokens.squeeze(1)
500
+
501
+ bsz, seqlen, dim = tokens.shape
502
+ _, cross_seqlen, _ = cross_attended.shape
503
+
504
+ # Masking out channels that were set to all-zeros
505
+ if self.training and freq_masks is not None:
506
+ with torch.no_grad():
507
+ tokens *= freq_masks
508
+
509
+ if self.use_compression_free_conv_stem:
510
+ tokens = self.compression_free_conv_stem_input(tokens)
511
+
512
+ h = self.tok_embeddings(tokens)
513
+ t = self.t_embedder(timeD)
514
+
515
+ cross_attended = self.encoder_proj(cross_attended)
516
+
517
+
518
+ # COMBINE SLIDING WINDOW MASK AND DOCUMENT MASK
519
+ SLIDING_WINDOW = self.sliding_window
520
+ def selfattn_sliding_window_func(b, h, q_idx, kv_idx):
521
+ # Self-attention case
522
+ return (q_idx - kv_idx).abs() <= SLIDING_WINDOW
523
+ mask_mod_slide = selfattn_sliding_window_func
524
+ mask = create_document_mask(lengths=seq_lens, base_mask_mod=mask_mod_slide)
525
+ #
526
+ SLIDING_WINDOW = self.xattn_sliding_window
527
+ def crossattn_sliding_window_func(b, h, q_idx, kv_idx):
528
+ # Cross-attention case
529
+ center_k_idx = (q_idx * cross_seqlen) // seqlen
530
+ return (kv_idx - center_k_idx).abs() <= SLIDING_WINDOW
531
+ mask_mod_slide = crossattn_sliding_window_func
532
+ cross_attn_mask = create_document_mask(lengths=seq_lens, kv_lengths=cross_seq_lens, base_mask_mod=mask_mod_slide)
533
+
534
+ visualize_attention_masks = False
535
+ if visualize_attention_masks:
536
+ from .utils import visualize_attention_mask
537
+ torch._dynamo.config.disable = True
538
+ visualize_attention_mask(mask, title_suffix="decoder_self_attn")
539
+ visualize_attention_mask(cross_attn_mask, title_suffix="decoder_cross_attn")
540
+ torch._dynamo.config.disable = False
541
+
542
+
543
+ if tok_idx is not None:
544
+ if tok_idx.ndim==3 and tok_idx.shape[0]==1:
545
+ tok_idx = tok_idx.squeeze().squeeze() # make it the right size for RoPE.
546
+
547
+ if cross_tok_idx is not None:
548
+ if cross_tok_idx.ndim==3 and cross_tok_idx.shape[0]==1:
549
+ cross_tok_idx = cross_tok_idx.squeeze().squeeze() # make it the right size for RoPE.
550
+
551
+ h, repa_loss = super().forward(h,
552
+ cross_attended,
553
+ t=t,
554
+ tok_idx=tok_idx,
555
+ cross_tok_idx=cross_tok_idx,
556
+ mask=mask,
557
+ cross_attn_mask=cross_attn_mask,
558
+ attn_impl=attn_impl,
559
+ repa_target=repa_target,
560
+ do_idx=do_idx,
561
+ )
562
+
563
+ h_normed = self.norm(h, t)
564
+
565
+ # if print_layerwise_activation_stats and do_idx is not None:
566
+ # print(f"\nDecoder output norm: (drop-out) mean={h[:, do_idx, :].mean().item():.6f}, std={h[:, do_idx, :].std().item():.6f}", end=" --> ")
567
+ # print(f"mean={h_normed[:, do_idx, :].mean().item():.6f}, std={h_normed[:, do_idx, :].std().item():.6f}")
568
+ # print(f"Decoder output norm: (non-drop) mean={h[:, ~do_idx, :].mean().item():.6f}, std={h[:, ~do_idx, :].std().item():.6f}", end=" --> ")
569
+ # print(f"mean={h_normed[:, ~do_idx, :].mean().item():.6f}, std={h_normed[:, ~do_idx, :].std().item():.6f}")
570
+
571
+ logits = self.output(h_normed)
572
+
573
+ if self.use_compression_free_conv_stem:
574
+ logits = self.compression_free_conv_stem_output(logits)
575
+
576
+ losses = self.compute_losses(target, logits, time_masks, freq_masks, channel_loss_weighting)
577
+
578
+ if repa_target is not None:
579
+ losses["decoder_repa_loss"] = repa_loss#.mean()
580
+
581
+ return logits, losses
582
+
583
+
584
+
585
+ @torch.compile()
586
+ def compute_losses(self, target, logits, time_masks, freq_masks, channel_loss_weighting):
587
+ losses = {}
588
+
589
+ if target is not None:
590
+ if self.huber_c is None:
591
+ batchwise_loss = F.mse_loss(target.float(), logits.float(), reduction="none") # shape = [B, T, C]
592
+ else:
593
+ batchwise_loss = huber_loss(target.float(), logits.float(), self.huber_c)
594
+
595
+ # Do Adaptive Loss Weighting - to boost loss from channels with small signals so we can better learn small signals.
596
+ if self.adaptive_loss_weighting:
597
+ ALW = batchwise_loss.detach().abs().mean(dim=2).unsqueeze(2) # shape = [B,C,1]
598
+ batchwise_loss = batchwise_loss/(ALW + 1e-5)
599
+
600
+
601
+ if channel_loss_weighting is not None:
602
+ batchwise_loss = batchwise_loss * channel_loss_weighting
603
+
604
+ if freq_masks is not None:
605
+ batchwise_loss = (batchwise_loss * freq_masks).sum(dim=1) / (freq_masks.sum(dim=1) + 1e-6) # shape = [B,C,1]
606
+ else:
607
+ batchwise_loss = batchwise_loss.mean(dim=-1)
608
+
609
+ if time_masks is not None:
610
+ batchwise_loss = (batchwise_loss * time_masks).sum(dim=-1) / time_masks.sum(dim=-1)
611
+
612
+ losses["decoder_rf_loss"] = batchwise_loss.mean()
613
+
614
+ return losses
615
+
616
+
617
+ def reset_parameters(self, init_std=None):
618
+ # Either use fixed base std or sqrt model dim
619
+ super().reset_parameters()
620
+ if init_std is None:
621
+ init_std = self.init_base_std or (self.dim ** (-0.5))
622
+
623
+ self.norm.reset_parameters()
624
+ self.t_embedder.reset_parameters(init_std)
625
+ nn.init.trunc_normal_(
626
+ self.tok_embeddings.weight,
627
+ mean=0.0,
628
+ std=init_std,
629
+ a=-3 * init_std,
630
+ b=3 * init_std,
631
+ )
632
+
633
+ nn.init.trunc_normal_(
634
+ self.encoder_proj.weight,
635
+ mean=0.0,
636
+ std=init_std,
637
+ a=-3 * init_std,
638
+ b=3 * init_std,
639
+ )
640
+ if self.encoder_proj.bias is not None:
641
+ nn.init.zeros_(self.encoder_proj.bias)
642
+
643
+ nn.init.trunc_normal_(
644
+ self.output.weight,
645
+ mean=0.0,
646
+ std=init_std,
647
+ a=-3 * init_std,
648
+ b=3 * init_std,
649
+ )
650
+ if self.use_compression_free_conv_stem:
651
+ self.compression_free_conv_stem_input.reset_parameters(init_std)
652
+ self.compression_free_conv_stem_output.reset_parameters(init_std)
653
+
654
+ def init_weights(self):
655
+ super().init_weights()
656
+ self.reset_parameters()
657
+
658
+ class EncoderTransformer(BaseTransformer):
659
+ def __init__(self, args: DecoderTransformerArgs):
660
+ args = deepcopy(args)
661
+ args.dim = args.dim if args.encoder_hidden_dim is None else args.encoder_hidden_dim
662
+ super().__init__(args)
663
+ self.weight_tying = False
664
+ self.sliding_window = args.encoder_sliding_window
665
+ self.bottleneck_type = args.bottleneck_type
666
+ self.downsample_factor = args.encoder_latent_downsample_factor
667
+ self.distill = args.distill_output_dim != 0
668
+ self.tok_embeddings = nn.Linear(args.encoder_input_dim, args.dim)
669
+ self.norm = RMSNorm(args.dim, eps=args.norm_eps)
670
+ self.registers = torch.nn.Parameter(torch.zeros(1, args.encoder_input_dim))
671
+ self.dropout_type = args.dropout_type
672
+ if self.dropout_type=="learnable":
673
+ self.dropout_vec = torch.nn.Parameter(args.stft_global_sigma*torch.rand(1, args.encoder_input_dim, dtype=torch.float32)) # rand init for learnable dropout vector
674
+ else:
675
+ self.dropout_vec = None # If None, it will just use zeros for dropped out chans (rather than learnable vector).
676
+
677
+ self.init_base_std = args.init_base_std
678
+ self.output = nn.Linear(args.dim, args.encoder_output_dim, bias=False)
679
+
680
+ if args.distill_output_dim != 0:
681
+ self.distill_output = nn.Linear(
682
+ args.dim,
683
+ args.distill_output_dim,
684
+ bias=True,
685
+ )
686
+ self.distill_norm = RMSNorm(args.dim, eps=args.norm_eps)
687
+
688
+ if "sim" in args.bottleneck_type:
689
+ self.quantizer = SimVQ(
690
+ dim = args.encoder_output_dim,
691
+ codebook_size = args.codebook_size,
692
+ rotation_trick = True # use rotation trick from Fifty et al.
693
+ )
694
+ elif "fsq" in args.bottleneck_type:
695
+ self.quantizer = FSQ(
696
+ levels = args.levels
697
+ )
698
+
699
+ self.use_compression_free_conv_stem = False
700
+ if args.compression_free_conv_stem:
701
+ self.use_compression_free_conv_stem = True
702
+
703
+ self.compression_free_conv_stem_input = CausalConv2DStem(
704
+ input_features = args.input_dim,
705
+ hidden_channels = 32,
706
+ activation = nn.SELU,
707
+ compress_channels=False,
708
+ )
709
+
710
+
711
+ def _interleave_registers(self, x: torch.Tensor):
712
+ """
713
+ 1) Pad `x` along the sequence dimension so it’s divisible by `self.downsample_factor`.
714
+ 2) Reshape into groups of length `df`.
715
+ 3) Insert a copy of `self.registers` in front of each group.
716
+ 4) Flatten back out.
717
+
718
+ Returns:
719
+ interleaved: [B, num_groups*(df+1), D]
720
+ num_groups: int
721
+ """
722
+
723
+ bsz, seqlen, dim = x.shape
724
+ df = self.downsample_factor
725
+
726
+ # Number of groups
727
+ num_groups = (seqlen + df - 1) // df
728
+ new_seqlen = num_groups * df
729
+
730
+ # Pad if needed
731
+ if new_seqlen > seqlen:
732
+ pad_len = new_seqlen - seqlen
733
+ x = torch.cat([x, x.new_zeros(bsz, pad_len, dim)], dim=1)
734
+
735
+ # Reshape to [B, num_groups, df, D]
736
+ x = x.reshape(bsz, num_groups, df, dim)
737
+
738
+ # Expand the single register => [B, num_groups, 1, D]
739
+ regs = self.registers.expand(bsz, num_groups, -1).unsqueeze(2)
740
+
741
+ # Cat the register in front of each group => [B, num_groups, df+1, D]
742
+ x = torch.cat([regs, x], dim=2)
743
+
744
+ # Flatten => [B, num_groups*(df+1), D]
745
+ x = x.reshape(bsz, -1, dim).contiguous()
746
+ return x, num_groups
747
+
748
+ def _extract_registers_and_non_registers(
749
+ self,
750
+ h: torch.Tensor,
751
+ num_groups: int,
752
+ original_seqlen: int = None, # Added: original sequence length before padding
753
+ return_non_registers: bool = False # Added: flag to return other tokens
754
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]: # Updated return type hint
755
+ """
756
+ Args:
757
+ h: Output tensor from transformer layers [B, interleaved_seqlen, D].
758
+ num_groups: Number of groups used in interleaving.
759
+ original_seqlen: The sequence length of the input *before* padding
760
+ and interleaving. Used to trim non-register tokens.
761
+ return_non_registers: If True, return both register and non-register
762
+ tokens. Otherwise, return only register tokens.
763
+
764
+ Returns:
765
+ If return_non_registers is False:
766
+ registers: [B, num_groups, D]
767
+ If return_non_registers is True:
768
+ registers: [B, num_groups, D]
769
+ non_registers: [B, original_seqlen, D]
770
+ """
771
+ bsz, seq_len, dim = h.shape
772
+ df = self.downsample_factor
773
+ # seq_len should be num_groups*(df+1)
774
+ h = h.reshape(bsz, num_groups, df + 1, dim)
775
+
776
+ # The register is the first token in each group
777
+ registers = h[:, :, 0, :] # [B, num_groups, D]
778
+
779
+ if not return_non_registers:
780
+ return registers.contiguous(), None
781
+ else:
782
+ # Extract non-register tokens (indices 1 to df)
783
+ non_registers = h[:, :, 1:, :] # [B, num_groups, df, D]
784
+ # Flatten back to sequence dimension
785
+ padded_seqlen = num_groups * df
786
+ non_registers = non_registers.reshape(bsz, padded_seqlen, dim)
787
+ # Trim back to the original sequence length, removing padding effects
788
+ non_registers = non_registers[:, :original_seqlen, :] # [B, original_seqlen, D]
789
+
790
+ return registers.contiguous(), non_registers.contiguous()
791
+
792
+
793
+
794
+ def forward(
795
+ self,
796
+ token_values: torch.Tensor,
797
+ seq_lens: torch.Tensor,
798
+ distill_target: Optional[torch.Tensor] = None,
799
+ tok_idx: Optional[torch.Tensor] = None,
800
+ mask: Optional[Union[BlockMask, torch.Tensor, str]] = None,
801
+ attn_impl: str = "flex_attention",
802
+ repa_target: Optional[torch.Tensor] = None,
803
+ do_idx: Optional[torch.Tensor] = None,
804
+ print_layerwise_activation_stats: bool = False,
805
+ ):
806
+
807
+ _, orig_seqlen, _ = token_values.shape
808
+
809
+ if self.use_compression_free_conv_stem:
810
+ token_values = self.compression_free_conv_stem_input(token_values)
811
+
812
+ token_values, num_groups = self._interleave_registers(token_values)
813
+ bsz, seqlen, _ = token_values.shape
814
+
815
+ if do_idx is not None: # (CW)
816
+ do_idx_pre_reg = do_idx # indices of dropped-out channels without registers interleaved
817
+ do_idx = (token_values.sum(axis=2)==0).squeeze(0) # recompute do_idx after interleaving registers
818
+
819
+
820
+ # Now if using Learable Dropout, replace dropped-out channels with a learned but fixed parameter vector.
821
+ if self.dropout_vec is not None:
822
+ token_values[:,do_idx,:] = self.dropout_vec
823
+
824
+ h = self.tok_embeddings(token_values)
825
+
826
+ # (CW) - COMBINE SLIDING WINDOW MASK AND DOCUMENT MASK
827
+ SLIDING_WINDOW = self.sliding_window
828
+ def sliding_window_func(b, h, q_idx, kv_idx):
829
+ # Self-attention case
830
+ return (q_idx - kv_idx).abs() <= SLIDING_WINDOW
831
+ mask_mod_slide = sliding_window_func
832
+ mask = create_document_mask(seq_lens*2, base_mask_mod=mask_mod_slide) # Hardcoding for CR=1 with interleave_registers thing.
833
+
834
+ visualize_attention_masks = False
835
+ if visualize_attention_masks:
836
+ from .utils import visualize_attention_mask
837
+ torch._dynamo.config.disable = True
838
+ visualize_attention_mask(mask, title_suffix="encoder")
839
+ torch._dynamo.config.disable = False
840
+
841
+ if tok_idx is not None:
842
+ tok_idx = tok_idx.repeat_interleave(repeats=2,dim=1)
843
+ tok_idx = tok_idx.squeeze().squeeze() # make it the right size for RoPE.
844
+
845
+
846
+ # if print_layerwise_activation_stats and do_idx is not None: # (CW)
847
+ # print(f"{do_idx.sum()=} and {(~do_idx).sum()=}")
848
+ # print(f"{token_values.shape=}")
849
+
850
+
851
+ h, repa_loss = super().forward(h, # BaseTransformer.forward
852
+ tok_idx=tok_idx,
853
+ mask=mask,
854
+ attn_impl=attn_impl,
855
+ repa_target=repa_target,
856
+ num_groups=num_groups,
857
+ original_seqlen=orig_seqlen,
858
+ downsample_factor=self.downsample_factor,
859
+ do_idx=do_idx,
860
+ )
861
+
862
+ h, non_regs = self._extract_registers_and_non_registers(h, num_groups, original_seqlen=orig_seqlen, return_non_registers=distill_target is not None)
863
+
864
+
865
+ # if print_layerwise_activation_stats and do_idx is not None: # (CW)
866
+ # h_normed = self.norm(h) # (CW)
867
+ # print(f"\nEncoder output norm (drop-out): mean={h[:, do_idx_pre_reg, :].mean().item():.6f}, std={h[:, do_idx_pre_reg, :].std().item():.6f}", end=" --> ") # (CW)
868
+ # print(f"mean={h_normed[:, do_idx_pre_reg, :].mean().item():.6f}, std={h_normed[:, do_idx_pre_reg, :].std().item():.6f}") # (CW)
869
+ # print(f"Encoder output norm (non-drop): mean={h[:, ~do_idx_pre_reg, :].mean().item():.6f}, std={h[:, ~do_idx_pre_reg, :].std().item():.6f}", end=" --> ") # (CW)
870
+ # print(f"mean={h_normed[:, ~do_idx_pre_reg, :].mean().item():.6f}, std={h_normed[:, ~do_idx_pre_reg, :].std().item():.6f}") # (CW)
871
+ # logits = self.output(h_normed) # (CW)
872
+ logits = self.output(self.norm(h))
873
+
874
+
875
+ logits, losses = self.bottleneck(logits)
876
+ if distill_target is not None:
877
+ losses['encoder_distill'] = (1 - F.cosine_similarity(self.distill_output(self.distill_norm(non_regs)), distill_target, dim=-1).mean()) * 0.1
878
+
879
+ if repa_target is not None:
880
+ losses["encoder_repa_loss"] = repa_loss#.mean()
881
+ return logits, losses
882
+
883
+ def bottleneck(self, h,):
884
+ losses = {}
885
+ latent = h
886
+ b, l, d = h.shape
887
+ if "kl" in self.bottleneck_type:
888
+ mean, logvar = h.chunk(2, dim=-1)
889
+ logvar = logvar.clamp(min=-3)
890
+ std = torch.exp(0.5 * logvar)
891
+ latent = mean + std * torch.randn_like(mean)
892
+ kl_div_loss = -0.5 * (1 + logvar - mean.pow(2) - logvar.exp())
893
+ losses["kl"] = kl_div_loss.mean()
894
+
895
+ if "mmd" in self.bottleneck_type and self.training:
896
+ losses["mmd"] = mmd_imq(latent.view(b*l, d).float(), torch.randn((b*l,d), dtype=torch.float32, device=latent.device,), 10.0)
897
+
898
+ if "sim" in self.bottleneck_type:
899
+ latent, codes, simvq_loss = self.quantizer(h)
900
+ losses["simvq_commit_loss"] = simvq_loss
901
+
902
+ if "fsq" in self.bottleneck_type:
903
+ latent, codes = self.quantizer(h)
904
+
905
+ return latent, losses
906
+
907
+ def reset_parameters(self, init_std=None):
908
+ # Either use fixed base std or sqrt model dim
909
+ super().reset_parameters()
910
+ if init_std is None:
911
+ init_std = self.init_base_std or (self.dim ** (-0.5))
912
+ self.norm.reset_parameters()
913
+ nn.init.trunc_normal_(
914
+ self.tok_embeddings.weight,
915
+ mean=0.0,
916
+ std=init_std,
917
+ a=-3 * init_std,
918
+ b=3 * init_std,
919
+ )
920
+
921
+ nn.init.trunc_normal_(
922
+ self.output.weight,
923
+ mean=0.0,
924
+ std=init_std,
925
+ a=-3 * init_std,
926
+ b=3 * init_std,
927
+ )
928
+
929
+ if self.distill:
930
+ self.distill_norm.reset_parameters()
931
+ nn.init.trunc_normal_(
932
+ self.distill_output.weight,
933
+ mean=0.0,
934
+ std=init_std,
935
+ a=-3 * init_std,
936
+ b=3 * init_std,
937
+ )
938
+ nn.init.zeros_(self.distill_output.bias)
939
+
940
+ nn.init.trunc_normal_(
941
+ self.registers,
942
+ mean=0.0,
943
+ std=init_std,
944
+ a=-3 * init_std,
945
+ b=3 * init_std,
946
+ )
947
+ if self.use_compression_free_conv_stem:
948
+ self.compression_free_conv_stem_input.reset_parameters(init_std)
949
+
950
+ def init_weights(self):
951
+ super().init_weights()
952
+ self.reset_parameters()
953
+
954
+ class EncoderDecoder(nn.Module):
955
+ def __init__(self, args: DecoderTransformerArgs):
956
+ super().__init__()
957
+
958
+ self.encoder = EncoderTransformer(args)
959
+ self.decoder = DecoderTransformer(args)
960
+ self.input_output_dim = args.input_dim
961
+ self.encoder_dropout = args.decoder_encoder_dropout
962
+ self.decoder_timestep_dropout = args.decoder_timestep_dropout
963
+ self.global_sigma = args.stft_global_sigma
964
+
965
+ self.num_fine_time_pts = args.num_fine_time_pts
966
+ self.dont_noise_chan_xyz = args.dont_noise_chan_xyz
967
+ self.rope_dim = args.rope_dim
968
+ self.tok_idx_type = args.tok_idx_type
969
+
970
+
971
+
972
+ def forward(
973
+ self,
974
+ encoder_input: torch.Tensor,
975
+ decoder_input: torch.Tensor,
976
+ t: torch.Tensor,
977
+ chan_pos: torch.Tensor,
978
+ chan_pos_discrete: torch.Tensor,
979
+ chan_id: torch.Tensor,
980
+ t_coarse: torch.Tensor,
981
+ seq_lens: torch.Tensor,
982
+ target: Optional[torch.Tensor] = None,
983
+ distill_target: Optional[torch.Tensor] = None,
984
+ time_masks: Optional[torch.Tensor] = None,
985
+ channel_loss_weighting: Optional[torch.Tensor] = None, # [1, 1, input_dim*2]
986
+ encoder_repa_target: Optional[torch.Tensor] = None,
987
+ decoder_repa_target: Optional[torch.Tensor] = None,
988
+ freq_masks: Optional[torch.Tensor] = None, # 0s where to not compute loss, 1s where to compute loss [B, 1, C]
989
+ ):
990
+
991
+
992
+ if encoder_input.ndim==2:
993
+ encoder_input = encoder_input.unsqueeze(0)
994
+ target = target.unsqueeze(0) # doing to get rid of broadcast warning from DecoderTransformer.compute_losses
995
+ chan_pos = chan_pos.unsqueeze(0)
996
+ chan_pos_discrete = chan_pos_discrete.unsqueeze(0)
997
+ chan_id = chan_id.unsqueeze(0)
998
+ t_coarse = t_coarse.unsqueeze(0)
999
+
1000
+
1001
+ ## Options for tok_idx. Choose 1.
1002
+ if self.tok_idx_type is None:
1003
+ tok_idx = None # this will just use args.model.max_seqlen to construct 1D-RoPE (but requires max_seqlen way too long).
1004
+ elif self.tok_idx_type == "t_coarse" and self.rope_dim==1:
1005
+ tok_idx = t_coarse # this ignores channel and just uses coarse time in 1D-RoPE
1006
+ elif self.tok_idx_type == "chan_id" and self.rope_dim==1:
1007
+ tok_idx = chan_id # this uses channel id in 1D-RoPE # this is same as hstack(arange(seq_lens)) below when seq_len = num_chans, ie chop_signals_only
1008
+ elif self.tok_idx_type == "stack_arange_seqlen" and self.rope_dim==1:
1009
+ tok_idx = torch.hstack([torch.arange(sl) for sl in seq_lens]).unsqueeze(0).unsqueeze(-1) # This has a different tok_id value for each element in sequence (chan or tc).
1010
+ elif self.tok_idx_type == "{x,y,z,tc}" and self.rope_dim==4:
1011
+ tok_idx = torch.cat((chan_pos_discrete,t_coarse), dim=2)
1012
+ else:
1013
+ raise ValueError(f"Dont understand {self.tok_idx_type=} and {self.rope_dim}")
1014
+
1015
+
1016
+ do_idx = (encoder_input.sum(axis=2)==0).squeeze(0) # indices of dropped-out channels (CW)
1017
+ # do_idx = None # [Set do_idx to None to disable printing of activation stats comparing channel drop-out]
1018
+
1019
+ enc_out, enc_losses = self.encoder(encoder_input,
1020
+ distill_target=distill_target, # (CW) - None
1021
+ repa_target=encoder_repa_target, # (CW) - None
1022
+ mask=None,
1023
+ seq_lens=seq_lens, # (CW) - for document masking
1024
+ tok_idx=tok_idx, # (CW) - pass in coarse time index for 1D RoPE
1025
+ do_idx=do_idx, # indices of dropped-out channels (CW)
1026
+ )
1027
+
1028
+ dec_out, dec_losses = self.decoder(tokens=decoder_input,
1029
+ cross_attended=enc_out,
1030
+ timeD=t,
1031
+ target=target,
1032
+ time_masks=time_masks, # (CW) - None
1033
+ channel_loss_weighting=channel_loss_weighting, # (CW) - None
1034
+ repa_target=decoder_repa_target, # (CW) - None
1035
+ freq_masks = freq_masks, # (CW) - masks out bad (all-zero) channels [B, 1, C]
1036
+ mask=None,
1037
+ cross_attn_mask=None,
1038
+ seq_lens=seq_lens, # (CW) - for document masking in self-attention
1039
+ cross_seq_lens=seq_lens, # (CW) - for document masking in cross-attention (with CR=1)
1040
+ tok_idx=tok_idx, # (CW) - pass in coarse time index for 1D RoPE
1041
+ cross_tok_idx=tok_idx, # (CW) - pass in coarse time index for 1D RoPE (with CR=1)
1042
+ do_idx=do_idx, #.squeeze(0) if do_idx is not None else None, # indices of dropped-out channels (CW)
1043
+ )
1044
+
1045
+ return dec_out, enc_losses, dec_losses
1046
+
1047
+
1048
+ @torch.no_grad()
1049
+ def sample(self, encoder_input: torch.Tensor, seq_lens: torch.Tensor, tok_idx: torch.Tensor, sample_steps: int = 50, cfg: float = 1.0):
1050
+
1051
+ device = encoder_input.device
1052
+ dtype = torch.bfloat16 # if device.type == "cuda" else torch.float16 # torch.float32
1053
+ # CPU Autocast only supports dtypes of torch.bfloat16, torch.float16 currently.
1054
+ with torch.autocast(device.type, dtype=dtype):
1055
+
1056
+ do_idx = (encoder_input.sum(axis=2)==0).squeeze(0) # indices of dropped-out channels (CW)
1057
+
1058
+ # do_idx = None # [Set do_idx to None to disable printing of activation stats comparing channel drop-out]
1059
+ enc_out, _ = self.encoder(
1060
+ token_values=encoder_input,
1061
+ seq_lens=seq_lens,
1062
+ tok_idx=tok_idx,
1063
+ do_idx=do_idx,
1064
+ )
1065
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1066
+
1067
+ bsz, seqlen, dim = enc_out.shape
1068
+ dt_time = torch.tensor([1.0 / sample_steps] * bsz, device=enc_out.device).view(-1)
1069
+
1070
+
1071
+ z = self.global_sigma*torch.randn_like(encoder_input).to(enc_out.device) # init to rand
1072
+ # z = torch.zeros_like(encoder_input).to(enc_out.device) # init to zeros
1073
+
1074
+ # Do not noise channel {x,y,z}-position in eeg_signal
1075
+ if self.dont_noise_chan_xyz:
1076
+ if dim==131 or dim==35:
1077
+ z[:,:,:3] = encoder_input[:,:,:3]
1078
+ else:
1079
+ pass
1080
+ # print("NOTE: EEG channel {x,y,z}-position was never concatenated into signal.")
1081
+ # import IPython; print('\n\nDebug:'); IPython.embed(); import time; time.sleep(0.3)
1082
+
1083
+
1084
+ dt = dt_time.unsqueeze(-1).unsqueeze(-1)
1085
+
1086
+ outputs = []
1087
+ for i in range(sample_steps, 0, -1):
1088
+ t = dt_time * i
1089
+ t_model = t.unsqueeze(1).unsqueeze(1)
1090
+
1091
+ vc, _ = self.decoder(tokens=z.unsqueeze(1),
1092
+ cross_attended=enc_out,
1093
+ timeD=t_model,
1094
+ seq_lens=seq_lens, # for document masking in self-attention
1095
+ cross_seq_lens=seq_lens, # for document masking in cross-attention (with CR=1)
1096
+ tok_idx=tok_idx,
1097
+ cross_tok_idx=tok_idx,
1098
+ )
1099
+
1100
+ if cfg != 1.0:
1101
+ vc_uncond, _ = self.decoder(tokens=z.unsqueeze(1),
1102
+ cross_attended=torch.zeros_like(enc_out),
1103
+ timeD=t_model,
1104
+ seq_lens=seq_lens, # for document masking in self-attention
1105
+ cross_seq_lens=seq_lens, # for document masking in cross-attention (with CR=1)
1106
+ tok_idx=tok_idx,
1107
+ cross_tok_idx=tok_idx,
1108
+ )
1109
+
1110
+ vc = vc_uncond + cfg * (vc - vc_uncond) # starts at unconditioned, moves toward conditioned as cfg increases
1111
+
1112
+ z = z - dt * vc
1113
+
1114
+ # Do not noise channel {x,y,z}-position in eeg_signal
1115
+ if self.dont_noise_chan_xyz:
1116
+ if dim==131 or dim==35:
1117
+ z[:,:,:3] = encoder_input[:,:,:3]
1118
+ else:
1119
+ # print("NOTE: EEG channel {x,y,z}-position was never concatenated into signal.")
1120
+ # import IPython; print('\n\nDebug:'); IPython.embed(); import time; time.sleep(0.3)
1121
+ pass
1122
+
1123
+ outputs.append(z)
1124
+
1125
+ return z, outputs
1126
+
1127
+
1128
+
1129
+ def reset_parameters(self):
1130
+ self.encoder.reset_parameters()
1131
+ self.decoder.reset_parameters()
1132
+
1133
+ def init_weights(self):
1134
+ self.encoder.init_weights()
1135
+ self.decoder.init_weights()
1136
+
1137
+
1138
+
1139
+
1140
+ # Optional policy for activation checkpointing. With None, we stick to the default (defined distributed.py: default_no_recompute_ops)
1141
+ def get_no_recompute_ops():
1142
+ return None
1143
+
1144
+
1145
+ # Optional and only used for fully shard options (fsdp) is choose. Highly recommanded for large models
1146
+ def build_fsdp_grouping_plan(model_args: DecoderTransformerArgs) -> List[Tuple[str, bool]]:
1147
+ group_plan: List[Tuple[str, bool]] = []
1148
+
1149
+ # # 1. Encoder Input
1150
+ group_plan.append(("encoder.output", False)) # <-- Changed to True
1151
+ group_plan.append(("decoder.output", False)) # Final output for main loss
1152
+ if model_args.decoder_repa_index != inf:
1153
+ group_plan.append(("decoder.repa_proj", False))
1154
+ if model_args.encoder_repa_index != inf:
1155
+ group_plan.append(("encoder.repa_proj", False))
1156
+
1157
+ # 2. Encoder Transformer Blocks
1158
+ for i in range(model_args.n_layers):
1159
+ group_plan.append((f"encoder.layers.{i}", False))
1160
+
1161
+ # 3. Decoder Transformer Blocks
1162
+ for i in range(model_args.n_layers):
1163
+ group_plan.append((f"decoder.layers.{i}", False))
1164
+
1165
+ # 4. Add Decoder and Encoder themselves
1166
+ group_plan.append(("encoder", False))
1167
+ group_plan.append(("decoder", False))
1168
+
1169
+ return group_plan
utils.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import contextlib
2
+ import math
3
+ import os
4
+ from collections.abc import Generator, Iterable
5
+ from datetime import timedelta
6
+
7
+ import torch
8
+ import torch.distributed._functional_collectives as funcol
9
+ import torch.distributed.distributed_c10d as c10d
10
+ from torch import distributed as dist
11
+ from torch.distributed.device_mesh import DeviceMesh
12
+ from torch.distributed.tensor import DTensor
13
+
14
+ import matplotlib.pyplot as plt
15
+ from datetime import datetime
16
+ import torch
17
+
18
+
19
+ @torch.no_grad()
20
+ def clip_grad_norm_(
21
+ parameters: torch.Tensor | Iterable[torch.Tensor],
22
+ max_norm: float,
23
+ norm_type: float = 2.0,
24
+ error_if_nonfinite: bool = False,
25
+ foreach: bool | None = None,
26
+ pp_mesh: DeviceMesh | None = None,
27
+ ) -> torch.Tensor:
28
+ """
29
+ Clip the gradient norm of an iterable of parameters.
30
+
31
+ Gradient norm clipping requires computing the gradient norm over the entire model.
32
+ `torch.nn.utils.clip_grad_norm_` only computes gradient norm along DP/FSDP/TP dimensions.
33
+ We need to manually reduce the gradient norm across PP stages.
34
+ See https://github.com/pytorch/torchtitan/issues/596 for details.
35
+
36
+ Args:
37
+ parameters: an iterable of Tensors or a single Tensor that will have gradients normalized
38
+ max_norm (float): max norm of the gradients
39
+ norm_type (float): type of the used p-norm. Can be ``'inf'`` for
40
+ infinity norm.
41
+ error_if_nonfinite (bool): if True, an error is thrown if the total
42
+ norm of the gradients from :attr:`parameters` is ``nan``,
43
+ ``inf``, or ``-inf``. Default: False (will switch to True in the future)
44
+ foreach (bool): use the faster foreach-based implementation.
45
+ If ``None``, use the foreach implementation for CUDA and CPU native tensors and silently
46
+ fall back to the slow implementation for other device types.
47
+ Default: ``None``
48
+ pp_mesh: pipeline parallel device mesh. If not None, will reduce gradient norm across PP stages.
49
+
50
+ Returns:
51
+ Total norm of the parameter gradients (viewed as a single vector).
52
+
53
+ """
54
+ grads = [p.grad for p in parameters if p.grad is not None]
55
+ total_norm = torch.nn.utils.get_total_norm(
56
+ grads, norm_type, error_if_nonfinite, foreach
57
+ )
58
+
59
+ # If total_norm is a DTensor, the placements must be `torch.distributed._tensor.ops.math_ops._NormPartial`.
60
+ # We can simply reduce the DTensor to get the total norm in this tensor's process group
61
+ # and then convert it to a local tensor.
62
+ # NOTE: It has two purposes:
63
+ # 1. to make sure the total norm is computed correctly when PP is used (see below)
64
+ # 2. to return a reduced total_norm tensor whose .item() would return the correct value
65
+ if isinstance(total_norm, DTensor):
66
+ # Will reach here if any non-PP parallelism is used.
67
+ # If only using PP, total_norm will be a local tensor.
68
+
69
+ # Remove FT replicate dimension if it exists.
70
+ total_norm = total_norm.full_tensor()
71
+
72
+ total_norm **= norm_type
73
+ dist.all_reduce(total_norm, op=dist.ReduceOp.SUM, group=pp_mesh.get_group())
74
+ total_norm **= 1.0 / norm_type
75
+
76
+ torch.nn.utils.clip_grads_with_norm_(parameters, max_norm, total_norm, foreach)
77
+ return total_norm
78
+
79
+
80
+ def reconstruct_full_mask(mask):
81
+ """
82
+ Utilities for testing and visualizing attention masks in EEG transformer models.
83
+
84
+ Functions:
85
+ - reconstruct_full_mask: Converts sparse block attention mask to full dense mask
86
+ - visualize_attention_mask: Creates and saves attention pattern visualization
87
+ """
88
+
89
+ # Get the block structure (260x260 with 1s for active blocks)
90
+ block_structure = mask.to_dense()[0, 0] # 260x260 sparse block pattern
91
+ full_shape = mask.shape[-1] # Get the full sequence length (33180)
92
+ full_mask = torch.zeros(full_shape, full_shape, device=block_structure.device, dtype=torch.bool)
93
+
94
+ # Reconstruct full mask from active blocks only
95
+ block_size = mask.BLOCK_SIZE[0] # 128
96
+ active_blocks = torch.where(block_structure == 1) # Get coordinates of active blocks
97
+
98
+ for q_block, kv_block in zip(active_blocks[0], active_blocks[1]):
99
+ # Fill in the 128x128 regions for active blocks
100
+ q_start, q_end = q_block * block_size, min((q_block + 1) * block_size, full_shape)
101
+ kv_start, kv_end = kv_block * block_size, min((kv_block + 1) * block_size, full_shape)
102
+
103
+ # Use mask_mod to get actual within-block attention pattern (vectorized)
104
+ q_indices = torch.arange(q_start, q_end, device=block_structure.device)
105
+ kv_indices = torch.arange(kv_start, kv_end, device=block_structure.device)
106
+ q_grid, kv_grid = torch.meshgrid(q_indices, kv_indices, indexing='ij')
107
+
108
+ # Call mask_mod with flattened arrays for efficiency
109
+ block_mask = mask.mask_mod(0, 0, q_grid.flatten(), kv_grid.flatten())
110
+ block_mask = block_mask.reshape(q_end - q_start, kv_end - kv_start)
111
+
112
+ full_mask[q_start:q_end, kv_start:kv_end] = block_mask
113
+
114
+ return full_mask
115
+
116
+ def visualize_attention_mask(mask, sample_size=5000, title_suffix=""):
117
+ """
118
+ Plot the attention mask.
119
+ Attentino mask needs to be constructed using reconstruct_full_mask()
120
+ """
121
+ if mask is not None:
122
+ # Reconstruct full mask from block structure
123
+ full_mask = reconstruct_full_mask(mask)
124
+ mask_2d = full_mask.cpu().numpy()
125
+
126
+ # Create binary attention pattern plot
127
+ plt.figure(figsize=(10, 10))
128
+ # Show sample or full mask depending on size
129
+ display_mask = mask_2d[:sample_size, :sample_size] if mask_2d.shape[0] > sample_size else mask_2d
130
+ plt.imshow(display_mask, cmap='Blues', aspect='equal')
131
+ plt.xlabel('Key Position')
132
+ plt.ylabel('Query Position')
133
+ plt.title(f'Attention Mask')
134
+ plt.colorbar(label='Attention Allowed')
135
+
136
+ # Generate filename with timestamp and config values
137
+ # timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
138
+ save_path = f"figures/attention_mask/mask_{title_suffix}.png"
139
+
140
+ # Create directory if it doesn't exist
141
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
142
+ plt.savefig(save_path, dpi=300, bbox_inches='tight')
143
+ plt.close()
144
+ print(f"Attention mask saved to {save_path}")
145
+
146
+ def plot_random_samples_in_grid(data,
147
+ num_samples=100,
148
+ grid_rows=10,
149
+ grid_cols=10,
150
+ save_path='figures/enc_out_samples_grid.png',
151
+ title='100 Random Samples from encoder output'):
152
+ """
153
+ Plot 100 random samples from xxx in a 10x10 grid and save as PNG.
154
+ """
155
+ random_indices = torch.randperm(data.shape[0])[:num_samples].cpu().numpy()
156
+
157
+ fig, axes = plt.subplots(grid_rows, grid_cols, figsize=(20, 20))
158
+ fig.suptitle(title, fontsize=16)
159
+
160
+ for idx, ax in enumerate(axes.flat):
161
+ sample_idx = random_indices[idx]
162
+ sample = data[sample_idx, :].float().detach().cpu().numpy()
163
+ ax.plot(sample)
164
+ ax.set_title(f'S{sample_idx}', fontsize=6)
165
+ ax.tick_params(labelsize=4)
166
+ ax.grid(True, alpha=0.3)
167
+ plt.tight_layout()
168
+ plt.savefig(save_path, dpi=150, bbox_inches='tight')
169
+ plt.close()
xattn.py ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .lingua_transformer import *
2
+
3
+
4
+ def apply_rotary_emb_xattn(
5
+ xq: torch.Tensor,
6
+ xk: torch.Tensor,
7
+ seq_dim: int,
8
+ freqs_cis_q: torch.Tensor,
9
+ freqs_cis_k: torch.Tensor,
10
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
11
+ xq_ = xq.reshape(*xq.shape[:-1], -1, 1, 2) # B S H D -> B S H D/2 1 2
12
+ xk_ = xk.reshape(*xk.shape[:-1], -1, 1, 2) # B S H D -> B S H D/2 1 2
13
+ freqs_cis_q = reshape_for_broadcast(
14
+ freqs_cis_q, xq_, seq_dim
15
+ ).float() # S D/2 2 2 -> 1 S 1 D/2 2 2
16
+ freqs_cis_k = reshape_for_broadcast(
17
+ freqs_cis_k, xk_, seq_dim
18
+ ).float() # S D/2 2 2 -> 1 S 1 D/2 2 2
19
+ xq_out = (xq_ * freqs_cis_q).sum(5).flatten(3)
20
+ xk_out = (xk_ * freqs_cis_k).sum(5).flatten(3)
21
+ return xq_out.type_as(xq), xk_out.type_as(xk)
22
+
23
+
24
+ class AdaRMSNorm(nn.Module):
25
+ """
26
+ Initialize the RMSNorm normalization layer.
27
+
28
+ Args:
29
+ dim (int): The dimension of the input tensor.
30
+ eps (float, optional): A small value added to the denominator for numerical stability. Default is 1e-6.
31
+
32
+ Attributes:
33
+ eps (float): A small value added to the denominator for numerical stability.
34
+ weight (nn.Parameter): Learnable scaling parameter.
35
+
36
+ """
37
+
38
+ def __init__(self, emb_dim, dim: int, eps: float = 1e-6):
39
+ super().__init__()
40
+ self.eps = eps
41
+ self.weight = nn.Linear(emb_dim, dim, bias=True)
42
+
43
+ def _norm(self, x: torch.Tensor):
44
+ return x * torch.rsqrt((x * x).mean(-1, keepdim=True) + self.eps)
45
+
46
+ def forward(self, x: torch.Tensor, c: torch.Tensor):
47
+ x = probe.log_stats(x, "resid")
48
+ output = self._norm(x.float())
49
+ return (output * self.weight(c).float()).type_as(x)
50
+
51
+ def reset_parameters(self):
52
+ # bias to ones, weight to 0s
53
+ nn.init.ones_(self.weight.bias)
54
+ nn.init.zeros_(self.weight.weight)
55
+
56
+
57
+ class CrossAttention(nn.Module):
58
+ def __init__(
59
+ self,
60
+ dim: int,
61
+ head_dim: int,
62
+ n_heads: int,
63
+ n_kv_heads: int,
64
+ rope_theta: float,
65
+ rope_dim: int,
66
+ ):
67
+ super().__init__()
68
+
69
+ self.dim = dim
70
+ self.head_dim = head_dim
71
+ self.rope_theta = rope_theta
72
+ self.rope_dim = rope_dim
73
+
74
+ self.n_heads = n_heads
75
+ self.n_kv_heads = n_kv_heads
76
+ self.heads_per_group = self.n_heads // self.n_kv_heads
77
+
78
+ self.wq = nn.Linear(
79
+ dim,
80
+ n_heads * head_dim,
81
+ bias=False,
82
+ )
83
+ self.wk = nn.Linear(
84
+ dim,
85
+ n_kv_heads * head_dim,
86
+ bias=False,
87
+ )
88
+ self.wv = nn.Linear(
89
+ dim,
90
+ n_kv_heads * head_dim,
91
+ bias=False,
92
+ )
93
+
94
+ self.wo = nn.Linear(
95
+ n_heads * head_dim,
96
+ dim,
97
+ bias=False,
98
+ )
99
+
100
+ def forward(
101
+ self,
102
+ xq: torch.Tensor,
103
+ xkv: torch.Tensor,
104
+ freq_cis: torch.Tensor,
105
+ tok_idx: Optional[torch.Tensor] = None,
106
+ cross_tok_idx: Optional[torch.Tensor] = None,
107
+ mask: Optional[Union[BlockMask, str]] = None,
108
+ attn_impl: str = "sdpa",
109
+ ) -> torch.Tensor:
110
+ # B S D
111
+ assert attn_impl == "flex_attention", "Only flex_attention is supported for now"
112
+ bsz, seq_len_q, dim = xq.shape
113
+ _, seq_len_kv, _ = xkv.shape
114
+ xq = self.wq(xq.view_as(xq))
115
+ xk = self.wk(xkv.view_as(xkv))
116
+ xv = self.wv(xkv.view_as(xkv))
117
+
118
+ output_shape = xq.shape
119
+ # B S D -> B S H D
120
+ xq = xq.view(bsz, seq_len_q, self.n_heads, self.head_dim)
121
+ xk = xk.view(bsz, seq_len_kv, self.n_kv_heads, self.head_dim)
122
+ xv = xv.view(bsz, seq_len_kv, self.n_kv_heads, self.head_dim)
123
+
124
+ if self.rope_dim==0:
125
+ pass
126
+ elif self.rope_dim==1:
127
+ if tok_idx is not None and cross_tok_idx is not None:
128
+ xq, xk = apply_rotary_emb_xattn(
129
+ xq, xk, 1, freq_cis[tok_idx], freq_cis[cross_tok_idx]
130
+ )
131
+ else:
132
+ xq, xk = apply_rotary_emb_xattn(
133
+ xq, xk, 1, freq_cis[0:seq_len_q], freq_cis[0:seq_len_kv]
134
+ )
135
+ elif self.rope_dim==4:
136
+
137
+ # Build freqcis_4RoPE by indexing freq_cis with each dimension of tok_idx separately and concatenating
138
+ # Cat along a new dimension to get [S, head_dim//2, 2, 2]
139
+ freqcis_parts = []
140
+ freqcis_cross_parts = []
141
+ for i in range(self.rope_dim):
142
+ freqcis_parts.append(freq_cis[tok_idx[:, i]])
143
+ freqcis_cross_parts.append(freq_cis[cross_tok_idx[:, i]])
144
+ freqcis_4RoPE = torch.cat(freqcis_parts, dim=1)
145
+ freqcis_cross_4RoPE = torch.cat(freqcis_cross_parts, dim=1)
146
+
147
+
148
+ xq, xk = apply_rotary_emb_xattn(
149
+ xq, xk, 1, freqcis_4RoPE, freqcis_cross_4RoPE
150
+ )
151
+
152
+ else:
153
+ print(f"I dont know how to handle {self.rope_dim=} inside xattn.CrossAttention.forward")
154
+ import IPython; print('\n\nDebug:'); IPython.embed(); import time; time.sleep(0.3)
155
+
156
+ # This condition helps us be easily compatible
157
+ # with inference by adding a pluggable KVCache
158
+ if hasattr(self, "kv_cache"):
159
+ xk, xv = self.kv_cache.update(xk, xv, tok_idx)
160
+
161
+ xk = repeat_kv(xk, self.heads_per_group, dim=2)
162
+ xv = repeat_kv(xv, self.heads_per_group, dim=2)
163
+
164
+ if attn_impl == "flex_attention":
165
+ assert mask is None or isinstance(mask, BlockMask)
166
+ xq, xk, xv = map(lambda e: e.transpose(1, 2), (xq, xk, xv))
167
+ if xq.device.type == "mps":
168
+ # MPS does not support flex_attention; fall back to SDPA with dense mask
169
+ if mask is not None:
170
+ S_q, S_kv = xq.shape[2], xk.shape[2]
171
+ q_idx = torch.arange(S_q, device='cpu')
172
+ kv_idx = torch.arange(S_kv, device='cpu')
173
+ dense_bool = mask.mask_mod(0, 0, q_idx.unsqueeze(1), kv_idx.unsqueeze(0))
174
+ attn_mask = torch.zeros(1, 1, S_q, S_kv, dtype=xq.dtype, device=xq.device)
175
+ attn_mask.masked_fill_(~dense_bool.unsqueeze(0).unsqueeze(0).to(xq.device), float("-inf"))
176
+ else:
177
+ attn_mask = None
178
+ output = F.scaled_dot_product_attention(xq, xk, xv, attn_mask=attn_mask)
179
+ elif xq.device.type == "cuda":
180
+ output = flex_attention_comp(xq, xk, xv, block_mask=mask)
181
+ else:
182
+ output = flex_attention(xq, xk, xv, block_mask=mask)
183
+ output = output.transpose(1, 2).contiguous() # B H S D -> B S H D
184
+
185
+ elif attn_impl == "sdpa":
186
+ xq, xk, xv = map(lambda e: e.transpose(1, 2), (xq, xk, xv))
187
+ assert mask is None or isinstance(mask, (str, torch.Tensor))
188
+ is_causal = (mask == "causal") if isinstance(mask, str) else False
189
+ mask = mask if isinstance(mask, torch.Tensor) else None
190
+ output = F.scaled_dot_product_attention(
191
+ xq,
192
+ xk,
193
+ xv,
194
+ is_causal=is_causal,
195
+ attn_mask=mask,
196
+ )
197
+ output = output.transpose(1, 2).contiguous() # B H S D -> B S H D
198
+ else:
199
+ raise NotImplementedError(
200
+ f"Attention implementation {attn_impl} not supported"
201
+ )
202
+
203
+ output = self.wo(output.reshape(output_shape))
204
+
205
+ return output
206
+
207
+ def reset_parameters(self, init_std=None, factor=1.0):
208
+ init_std = init_std or (self.dim ** (-0.5))
209
+
210
+ for w in [self.wq, self.wk, self.wv]:
211
+ nn.init.trunc_normal_(
212
+ w.weight,
213
+ mean=0.0,
214
+ std=init_std,
215
+ a=-3 * init_std,
216
+ b=3 * init_std,
217
+ )
218
+
219
+ nn.init.trunc_normal_(
220
+ self.wo.weight,
221
+ mean=0.0,
222
+ std=init_std / factor,
223
+ a=-3 * init_std,
224
+ b=3 * init_std,
225
+ )
226
+
227
+
228
+ class FourierConditioner(nn.Module):
229
+ def __init__(
230
+ self,
231
+ output_dim: int,
232
+ input_dim: int = 1,
233
+ std: float = 0.02,
234
+ min_val: float = 0.0,
235
+ max_val: float = 1.0,
236
+ ):
237
+ super().__init__()
238
+ assert input_dim == 1
239
+ assert output_dim % 2 == 0
240
+ self.output_dim = output_dim
241
+ self.register_buffer("weight", torch.randn([output_dim // 2, input_dim]) * std)
242
+ self.min_val, self.max_val = min_val, max_val
243
+ self.proj = nn.Linear(output_dim, output_dim)
244
+
245
+ def forward(self, x: list[float], device=None):
246
+ x = (x - self.min_val) / (self.max_val - self.min_val)
247
+ f = (2 * torch.pi * x.float() @ self.weight.T).type_as(x)
248
+ return self.proj(torch.cat([f.cos(), f.sin()], dim=-1))
249
+
250
+ def reset_parameters(self, init_std=None, factor=1.0):
251
+ init_std = init_std or (self.output_dim ** (-0.5))
252
+
253
+ self.register_buffer("weight", torch.randn([self.output_dim // 2, 1]).to(self.proj.weight.device) * init_std)
254
+
255
+ nn.init.trunc_normal_(
256
+ self.proj.weight,
257
+ mean=0.0,
258
+ std=init_std / factor,
259
+ a=-3 * init_std,
260
+ b=3 * init_std,
261
+ )
262
+ nn.init.zeros_(self.proj.bias)
263
+
264
+
265
+ @dataclass
266
+ class DecoderArgs(BaseTransformerArgs):
267
+
268
+ t_dim: int = 64
269
+ n_heads: int = 8
270
+ seqlen_t: bool = False
271
+
272
+
273
+ class DecoderBlock(nn.Module):
274
+ def __init__(self, args: DecoderArgs):
275
+ super().__init__()
276
+
277
+ assert (args.head_dim is not None) or (args.n_heads is not None), (
278
+ "Should specify at least head_dim or n_heads"
279
+ )
280
+ self.head_dim = args.head_dim or args.dim // args.n_heads
281
+ self.n_heads = args.n_heads or args.dim // args.head_dim
282
+ self.n_kv_heads = args.n_kv_heads or self.n_heads
283
+
284
+ assert args.n_heads % self.n_kv_heads == 0
285
+ assert args.dim % args.n_heads == 0
286
+
287
+ self.cross_attention = CrossAttention(
288
+ dim=args.dim,
289
+ head_dim=self.head_dim,
290
+ n_heads=self.n_heads,
291
+ n_kv_heads=self.n_kv_heads,
292
+ rope_theta=args.rope_theta,
293
+ rope_dim=args.rope_dim,
294
+ )
295
+ self.cross_attention_x_norm = AdaRMSNorm(
296
+ args.t_dim, args.dim, eps=args.norm_eps
297
+ )
298
+
299
+ self.seqlen_t = args.seqlen_t
300
+ if args.seqlen_t:
301
+ self.cross_attention_y_norm = RMSNorm(
302
+ args.dim, eps=args.norm_eps
303
+ )
304
+ else:
305
+ self.cross_attention_y_norm = AdaRMSNorm(
306
+ args.t_dim, args.dim, eps=args.norm_eps
307
+ )
308
+
309
+ self.attention = Attention(
310
+ dim=args.dim,
311
+ head_dim=self.head_dim,
312
+ n_heads=self.n_heads,
313
+ n_kv_heads=self.n_kv_heads,
314
+ rope_theta=args.rope_theta,
315
+ rope_dim=args.rope_dim,
316
+ )
317
+ self.feed_forward = FeedForward(
318
+ dim=args.dim,
319
+ hidden_dim=4 * args.dim,
320
+ multiple_of=args.multiple_of,
321
+ ffn_dim_multiplier=args.ffn_dim_multiplier,
322
+ )
323
+ self.attention_norm = AdaRMSNorm(args.t_dim, args.dim, eps=args.norm_eps)
324
+ self.ffn_norm = AdaRMSNorm(args.t_dim, args.dim, eps=args.norm_eps)
325
+
326
+ def forward(
327
+ self,
328
+ x: torch.Tensor,
329
+ y: torch.Tensor,
330
+ c: torch.Tensor,
331
+ freq_cis: torch.Tensor,
332
+ tok_idx: Optional[torch.Tensor] = None,
333
+ cross_tok_idx: Optional[torch.Tensor] = None,
334
+ self_attn_mask: Optional[Union[BlockMask, str]] = None,
335
+ cross_attn_mask: Optional[Union[BlockMask, str]] = None,
336
+ attn_impl: str = "sdpa",
337
+ do_idx: Optional[torch.Tensor] = None,
338
+ print_layerwise_activation_stats: bool = False,
339
+ ) -> torch.Tensor:
340
+
341
+ if print_layerwise_activation_stats and do_idx is not None:
342
+
343
+ x_normed = self.cross_attention_x_norm(x, c)
344
+ y_normed = self.cross_attention_y_norm(y, c) if not self.seqlen_t else self.cross_attention_y_norm(y)
345
+
346
+ print(f"\n\tDecoder cross_attn_x_norm: (drop-out) mean={x[:, do_idx, :].mean().item():.6f}, std={x[:, do_idx, :].std().item():.6f}", end=" --> ")
347
+ print(f"mean={x_normed[:, do_idx, :].mean().item():.6f}, std={x_normed[:, do_idx, :].std().item():.6f}")
348
+
349
+ print(f"\tDecoder cross_attn_x_norm: (non-drop) mean={x[:, ~do_idx, :].mean().item():.6f}, std={x[:, ~do_idx, :].std().item():.6f}", end=" --> ")
350
+ print(f"mean={x_normed[:, ~do_idx, :].mean().item():.6f}, std={x_normed[:, ~do_idx, :].std().item():.6f}")
351
+
352
+ print(f"\n\tDecoder cross_attn_y_norm: (drop-out) mean={y[:, do_idx, :].mean().item():.6f}, std={y[:, do_idx, :].std().item():.6f}", end=" --> ")
353
+ print(f"mean={y_normed[:, do_idx, :].mean().item():.6f}, std={y_normed[:, do_idx, :].std().item():.6f}")
354
+
355
+ print(f"\tDecoder cross_attn_y_norm: (non-drop) mean={y[:, ~do_idx, :].mean().item():.6f}, std={y[:, ~do_idx, :].std().item():.6f}", end=" --> ")
356
+ print(f"mean={y_normed[:, ~do_idx, :].mean().item():.6f}, std={y_normed[:, ~do_idx, :].std().item():.6f}")
357
+
358
+ x = x + self.cross_attention(
359
+ x_normed,
360
+ y_normed,
361
+ freq_cis,
362
+ tok_idx=tok_idx,
363
+ cross_tok_idx=cross_tok_idx,
364
+ mask=cross_attn_mask,
365
+ attn_impl=attn_impl,
366
+ )
367
+
368
+ else:
369
+ x = x + self.cross_attention(
370
+ self.cross_attention_x_norm(x, c),
371
+ self.cross_attention_y_norm(y, c) if not self.seqlen_t else self.cross_attention_y_norm(y),
372
+ freq_cis,
373
+ tok_idx=tok_idx,
374
+ cross_tok_idx=cross_tok_idx,
375
+ mask=cross_attn_mask,
376
+ attn_impl=attn_impl,
377
+ )
378
+
379
+
380
+
381
+ if print_layerwise_activation_stats and do_idx is not None:
382
+
383
+ x_normed = self.attention_norm(x, c)
384
+
385
+ print(f"\n\tDecoder self attn_norm: (drop-out) mean={x[:, do_idx, :].mean().item():.6f}, std={x[:, do_idx, :].std().item():.6f}", end=" --> ")
386
+ print(f" mean={x_normed[:, do_idx, :].mean().item():.6f}, std={x_normed[:, do_idx, :].std().item():.6f}")
387
+
388
+ print(f"\tDecoder self attn_norm: (non-drop) mean={x[:, ~do_idx, :].mean().item():.6f}, std={x[:, ~do_idx, :].std().item():.6f}", end=" --> ")
389
+ print(f"mean={x_normed[:, ~do_idx, :].mean().item():.6f}, std={x_normed[:, ~do_idx, :].std().item():.6f}")
390
+
391
+ h = x + self.attention(
392
+ x_normed,
393
+ freq_cis,
394
+ tok_idx=tok_idx,
395
+ mask=self_attn_mask,
396
+ attn_impl=attn_impl,
397
+ )
398
+
399
+ h_normed = self.ffn_norm(h, c)
400
+
401
+ print(f"\n\tDecoder ffn_norm: (drop-out) mean={h[:, do_idx, :].mean().item():.6f}, std={h[:, do_idx, :].std().item():.6f}", end=" --> ")
402
+ print(f"mean={h_normed[:, do_idx, :].mean().item():.6f}, std={h_normed[:, do_idx, :].std().item():.6f}")
403
+
404
+ print(f"\tDecoder ffn_norm: (non-drop) mean={h[:, ~do_idx, :].mean().item():.6f}, std={h[:, ~do_idx, :].std().item():.6f}", end=" --> ")
405
+ print(f"mean={h_normed[:, ~do_idx, :].mean().item():.6f}, std={h_normed[:, ~do_idx, :].std().item():.6f}")
406
+
407
+ out = h + self.feed_forward(h_normed)
408
+
409
+ else:
410
+
411
+ h = x + self.attention(
412
+ self.attention_norm(x, c),
413
+ freq_cis,
414
+ tok_idx=tok_idx,
415
+ mask=self_attn_mask,
416
+ attn_impl=attn_impl,
417
+ )
418
+ out = h + self.feed_forward(self.ffn_norm(h, c))
419
+
420
+ return out
421
+
422
+ def init_weights(self, init_std=None, factor=1.0):
423
+ self.cross_attention.reset_parameters(init_std, factor)
424
+ self.cross_attention_x_norm.reset_parameters()
425
+ self.cross_attention_y_norm.reset_parameters()
426
+
427
+ self.attention.reset_parameters(init_std, factor)
428
+ self.attention_norm.reset_parameters()
429
+
430
+ self.feed_forward.reset_parameters(init_std, factor)
431
+ self.ffn_norm.reset_parameters()