guan-wang commited on
Commit
eaae8d1
·
verified ·
1 Parent(s): 55c760e

Upload EBFM-DCLM-160M

Browse files
README.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ tags:
4
+ - ebfm
5
+ - energy-based-language-model
6
+ - custom-architecture
7
+ pipeline_tag: fill-mask
8
+ ---
9
+
10
+ # EBFM-DCLM-160M
11
+
12
+ OpenEBFM 160M model (variant d12) trained on DCLM. This repository contains a Hugging Face Transformers-compatible export for the pretraining checkpoint.
13
+
14
+ ## How to Use
15
+
16
+ Install the runtime dependencies:
17
+
18
+ ```bash
19
+ pip install torch transformers
20
+ ```
21
+
22
+ Load the tokenizer and model with the custom OpenEBFM code enabled:
23
+
24
+ ```python
25
+ import torch
26
+ from transformers import AutoModelForMaskedLM, AutoTokenizer
27
+
28
+ repo_id = "guan-wang/EBFM-DCLM-160M"
29
+ tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
30
+ model = AutoModelForMaskedLM.from_pretrained(
31
+ repo_id,
32
+ trust_remote_code=True,
33
+ )
34
+
35
+ device = "cuda" if torch.cuda.is_available() else "cpu"
36
+ model.to(device)
37
+ model.eval()
38
+ model.requires_grad_(False)
39
+ inputs = tokenizer("Hello world", return_tensors="pt").to(device)
40
+ outputs = model(**inputs)
41
+
42
+ print(outputs.logits.shape)
43
+ ```
44
+
45
+ The first load may prompt you to review and allow the repository's custom model
46
+ code. Loading remote Python code requires `trust_remote_code=True`.
47
+
48
+ ## Model Details
49
+
50
+ | Detail | Value |
51
+ | --- | --- |
52
+ | Model family | OpenEBFM energy-based language model |
53
+ | Architecture | Custom OpenEBFM implementation (d12) |
54
+ | Parameter scale | 160M |
55
+ | Training dataset | DCLM |
56
+ | Training stage | pretraining |
57
+ | Transformer blocks | 12 |
58
+ | Embedding dimension | 768 |
59
+ | Attention heads | 6 |
60
+ | Context length | 2048 |
61
+ | Vocabulary size | 32768 |
62
+
63
+ The parameter scale is the label used for this model in the OpenEBFM model
64
+ configuration. The architecture and sequence settings above are read from the
65
+ exported checkpoint configuration.
66
+
67
+ ## Files
68
+
69
+ - `config.json`: model configuration.
70
+ - `model*.safetensors`: model weights in the standard Transformers format.
71
+ - `modeling_ebfm.py`: remote model and tokenizer implementation.
72
+ - `configuration_ebfm.py`: remote configuration implementation.
73
+ - `tokenizer.pkl`: serialized EBFM tokenizer.
74
+ - `tokenizer_config.json`: tokenizer auto-loading configuration.
75
+ - `token_bytes.pt`: token byte table used by OpenEBFM metrics.
76
+
77
+
78
+ The code is maintained in the [OpenEBFM repository](https://github.com/datamllab/openebfm).
config.json ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "EBFMForMaskedLM"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_ebfm.EBFMConfig",
7
+ "AutoModelForMaskedLM": "modeling_ebfm.EBFMForMaskedLM",
8
+ "AutoTokenizer": [
9
+ "modeling_ebfm.EBFMTokenizer",
10
+ null
11
+ ]
12
+ },
13
+ "batch_size_per_device": 2,
14
+ "context_length": 2048,
15
+ "denoising_initial_condition": "random_noise",
16
+ "dtype": "float32",
17
+ "ebfm_act_func": "silu",
18
+ "ebfm_norm": "rms",
19
+ "embedding_dim": 768,
20
+ "ffn_dim_multiplier": 2.67,
21
+ "float_precision": "bf16-mixed",
22
+ "free_embed_noise_scale": 1.0,
23
+ "free_embedding_mcmc": true,
24
+ "gaussian_random_noise_scaling": 1.0,
25
+ "gradient_checkpointing": false,
26
+ "mcmc_num_steps": 1,
27
+ "mcmc_step_size": 300.0,
28
+ "mcmc_step_size_learnable": true,
29
+ "modality": "NLP",
30
+ "model_name": "ebfm",
31
+ "model_size": "d12",
32
+ "model_type": "ebfm",
33
+ "multiheaded_attention_heads": 6,
34
+ "num_transformer_blocks": 12,
35
+ "tf_head_type": "direct_unembed",
36
+ "tie_word_embeddings": false,
37
+ "time_embedding": true,
38
+ "transformers_version": "5.19.0",
39
+ "use_sdpa_attention": false,
40
+ "use_tf_head": true,
41
+ "vocab_size": 32768,
42
+ "weight_initialization_gain": 1.0,
43
+ "weight_initialization_method": "xavier"
44
+ }
configuration_ebfm.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Hugging Face configuration for the canonical EBFM architecture."""
2
+
3
+ from transformers import PretrainedConfig
4
+
5
+
6
+ EBFM_HEAD_DIM = 128
7
+
8
+
9
+ def _ebfm_depth_scaling(depth, aspect_ratio=64):
10
+ base_dim = depth * aspect_ratio
11
+ embedding_dim = ((base_dim + EBFM_HEAD_DIM - 1) // EBFM_HEAD_DIM) * EBFM_HEAD_DIM
12
+ return {
13
+ "num_transformer_blocks": depth,
14
+ "embedding_dim": embedding_dim,
15
+ "multiheaded_attention_heads": embedding_dim // EBFM_HEAD_DIM,
16
+ }
17
+
18
+
19
+ MODEL_SIZES = {
20
+ f"d{depth}": _ebfm_depth_scaling(depth) for depth in range(4, 29, 2)
21
+ }
22
+
23
+
24
+ class EBFMConfig(PretrainedConfig):
25
+ """Configuration persisted in a Transformers model repository."""
26
+
27
+ model_type = "ebfm"
28
+
29
+ def __init__(
30
+ self,
31
+ model_size="d6",
32
+ vocab_size=32768,
33
+ embedding_dim=None,
34
+ num_transformer_blocks=None,
35
+ multiheaded_attention_heads=None,
36
+ context_length=2048,
37
+ batch_size_per_device=1,
38
+ weight_initialization_method="xavier",
39
+ weight_initialization_gain=1.0,
40
+ ebfm_norm="rms",
41
+ ebfm_act_func="silu",
42
+ ffn_dim_multiplier=None,
43
+ use_sdpa_attention=False,
44
+ float_precision="32-true",
45
+ gradient_checkpointing=False,
46
+ mcmc_step_size=300.0,
47
+ mcmc_step_size_learnable=False,
48
+ mcmc_num_steps=1,
49
+ denoising_initial_condition="random_noise",
50
+ gaussian_random_noise_scaling=1.0,
51
+ free_embed_noise_scale=1.0,
52
+ tf_head_type="direct_unembed",
53
+ **kwargs,
54
+ ):
55
+ if model_size not in MODEL_SIZES:
56
+ valid_sizes = ", ".join(MODEL_SIZES)
57
+ raise ValueError(
58
+ f"unsupported EBFM model_size={model_size!r}; "
59
+ f"use one of: {valid_sizes}"
60
+ )
61
+ size_defaults = MODEL_SIZES[model_size]
62
+ self.model_name = kwargs.pop("model_name", "ebfm")
63
+ self.model_size = model_size
64
+ self.modality = kwargs.pop("modality", "NLP")
65
+ self.time_embedding = kwargs.pop("time_embedding", True)
66
+ self.vocab_size = int(vocab_size)
67
+ self.embedding_dim = int(embedding_dim or size_defaults["embedding_dim"])
68
+ self.num_transformer_blocks = int(
69
+ num_transformer_blocks or size_defaults["num_transformer_blocks"]
70
+ )
71
+ self.multiheaded_attention_heads = int(
72
+ multiheaded_attention_heads
73
+ or size_defaults["multiheaded_attention_heads"]
74
+ )
75
+ self.context_length = int(context_length)
76
+ self.batch_size_per_device = int(batch_size_per_device)
77
+ self.weight_initialization_method = weight_initialization_method
78
+ self.weight_initialization_gain = float(weight_initialization_gain)
79
+ self.ebfm_norm = ebfm_norm
80
+ self.ebfm_act_func = ebfm_act_func
81
+ self.ffn_dim_multiplier = ffn_dim_multiplier
82
+ self.use_sdpa_attention = bool(use_sdpa_attention)
83
+ self.float_precision = float_precision
84
+ self.gradient_checkpointing = bool(gradient_checkpointing)
85
+ self.mcmc_step_size = float(mcmc_step_size)
86
+ self.mcmc_step_size_learnable = bool(mcmc_step_size_learnable)
87
+ self.mcmc_num_steps = int(mcmc_num_steps)
88
+ self.denoising_initial_condition = denoising_initial_condition
89
+ self.gaussian_random_noise_scaling = float(gaussian_random_noise_scaling)
90
+ self.free_embed_noise_scale = float(free_embed_noise_scale)
91
+ self.tf_head_type = tf_head_type
92
+ self.use_tf_head = True
93
+ self.free_embedding_mcmc = True
94
+ self.architectures = kwargs.pop("architectures", ["EBFMForMaskedLM"])
95
+ kwargs.setdefault(
96
+ "auto_map",
97
+ {
98
+ "AutoConfig": "configuration_ebfm.EBFMConfig",
99
+ "AutoModelForMaskedLM": "modeling_ebfm.EBFMForMaskedLM",
100
+ "AutoTokenizer": ["modeling_ebfm.EBFMTokenizer", None],
101
+ },
102
+ )
103
+ kwargs.setdefault("tie_word_embeddings", False)
104
+ super().__init__(**kwargs)
105
+
106
+ @classmethod
107
+ def from_legacy_hparams(cls, hparams, vocab_size):
108
+ """Create a config from a Lightning checkpoint hyperparameter map."""
109
+
110
+ values = {
111
+ key: hparams[key]
112
+ for key in (
113
+ "model_name",
114
+ "model_size",
115
+ "modality",
116
+ "time_embedding",
117
+ "embedding_dim",
118
+ "num_transformer_blocks",
119
+ "multiheaded_attention_heads",
120
+ "context_length",
121
+ "batch_size_per_device",
122
+ "weight_initialization_method",
123
+ "weight_initialization_gain",
124
+ "ebfm_norm",
125
+ "ebfm_act_func",
126
+ "ffn_dim_multiplier",
127
+ "use_sdpa_attention",
128
+ "float_precision",
129
+ "gradient_checkpointing",
130
+ "mcmc_step_size",
131
+ "mcmc_step_size_learnable",
132
+ "mcmc_num_steps",
133
+ "denoising_initial_condition",
134
+ "gaussian_random_noise_scaling",
135
+ "free_embed_noise_scale",
136
+ "tf_head_type",
137
+ )
138
+ if key in hparams
139
+ }
140
+ values["vocab_size"] = int(vocab_size)
141
+ return cls(**values)
142
+
143
+ def to_hparams(self):
144
+ """Return fields required to reconstruct the training model."""
145
+
146
+ return {
147
+ key: getattr(self, key)
148
+ for key in (
149
+ "model_name",
150
+ "model_size",
151
+ "modality",
152
+ "time_embedding",
153
+ "vocab_size",
154
+ "embedding_dim",
155
+ "num_transformer_blocks",
156
+ "multiheaded_attention_heads",
157
+ "context_length",
158
+ "batch_size_per_device",
159
+ "weight_initialization_method",
160
+ "weight_initialization_gain",
161
+ "ebfm_norm",
162
+ "ebfm_act_func",
163
+ "ffn_dim_multiplier",
164
+ "use_sdpa_attention",
165
+ "float_precision",
166
+ "gradient_checkpointing",
167
+ "mcmc_step_size",
168
+ "mcmc_step_size_learnable",
169
+ "mcmc_num_steps",
170
+ "denoising_initial_condition",
171
+ "gaussian_random_noise_scaling",
172
+ "free_embed_noise_scale",
173
+ "tf_head_type",
174
+ "use_tf_head",
175
+ "free_embedding_mcmc",
176
+ )
177
+ }
178
+
179
+
180
+ __all__ = ["EBFMConfig", "MODEL_SIZES"]
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3260dbea6ddfc0f38e98080189001a4df5c184e8da9a6f7050e14c9265922ae7
3
+ size 642439044
modeling_ebfm.py ADDED
@@ -0,0 +1,2020 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The paper-aligned EBFM model.
2
+
3
+ The repository intentionally exposes one training architecture:
4
+
5
+ * NLP with time embedding;
6
+ * one free-embedding MCMC update (K=1);
7
+ * a direct unembedding TF head;
8
+ * the Muon+AdamW optimizer and linear warmdown schedule are configured by the
9
+ trainer, not by alternate model branches.
10
+
11
+ """
12
+
13
+ import math
14
+ import os
15
+ import pickle
16
+ import shutil
17
+ import types
18
+ from contextlib import nullcontext
19
+ from dataclasses import dataclass
20
+ from pathlib import Path
21
+ from typing import Any, Optional, Tuple
22
+
23
+ import torch
24
+ from torch import nn
25
+ from torch.nn import functional as F
26
+ from torch.utils.checkpoint import checkpoint
27
+
28
+ try:
29
+ from transformers import PreTrainedModel, PreTrainedTokenizer
30
+ from transformers.modeling_outputs import MaskedLMOutput
31
+ except ImportError:
32
+ PreTrainedModel = None
33
+ PreTrainedTokenizer = None
34
+ MaskedLMOutput = None
35
+
36
+ try:
37
+ from .configuration_ebfm import EBFMConfig
38
+ except ImportError:
39
+ try:
40
+ from configuration_ebfm import EBFMConfig
41
+ except ImportError:
42
+ EBFMConfig = None
43
+
44
+
45
+ def sample_top_p(probs: torch.Tensor, p: float) -> torch.Tensor:
46
+ """Sample from a batch of probability distributions with nucleus sampling."""
47
+
48
+ probs_sort, probs_idx = torch.sort(probs, dim=-1, descending=True)
49
+ cumulative = torch.cumsum(probs_sort, dim=-1)
50
+ probs_sort[cumulative - probs_sort > p] = 0.0
51
+ probs_sort.div_(probs_sort.sum(dim=-1, keepdim=True))
52
+ return torch.gather(probs_idx, -1, torch.multinomial(probs_sort, 1))
53
+
54
+
55
+ def call_model_forward_decode(hparams, model, input_tokens, start_pos=0, bsz=None):
56
+ """Run EBFM on the supplied prefix and return vocabulary logits.
57
+
58
+ Callers that pass the full prefix on each decoding iteration must keep
59
+ ``start_pos=0`` because this model does not cache prior key/value states.
60
+ """
61
+
62
+ del hparams, bsz
63
+ _, _, pred_hiddens = model.forward(
64
+ input_tokens,
65
+ start_pos=start_pos,
66
+ learning=False,
67
+ return_pred_hiddens=True,
68
+ )
69
+ pred_hidden = pred_hiddens[-1]
70
+ if pred_hidden is None:
71
+ raise RuntimeError("EBFM returned no post-update hidden state.")
72
+ return model.tf_head(pred_hidden, model.embeddings(input_tokens))
73
+
74
+
75
+ @dataclass
76
+ class EBFMModelArgs:
77
+ """Architecture arguments persisted indirectly through checkpoint hparams."""
78
+
79
+ dim: int = 768
80
+ n_layers: int = 12
81
+ n_heads: int = 12
82
+ n_kv_heads: Optional[int] = None
83
+ vocab_size: int = 32768
84
+ ffn_dim_multiplier: Optional[float] = None
85
+ norm_eps: float = 1e-5
86
+ max_batch_size: int = 64
87
+ max_seq_len: int = 16
88
+ weight_initialization: str = "xavier"
89
+ ebfm_norm: str = "rms"
90
+ ebfm_act_func: str = "silu"
91
+ weight_initialization_gain: float = 1.0
92
+ use_sdpa_attention: bool = False
93
+ float_precision: str = "32-true"
94
+
95
+
96
+ EBFM_HEAD_DIM = 128
97
+
98
+
99
+ def ebfm_depth_scaling(depth, aspect_ratio=64):
100
+ base_dim = depth * aspect_ratio
101
+ head_dim = EBFM_HEAD_DIM
102
+ embedding_dim = ((base_dim + head_dim - 1) // head_dim) * head_dim
103
+ num_heads = embedding_dim // head_dim
104
+ return {
105
+ "num_transformer_blocks": depth,
106
+ "embedding_dim": embedding_dim,
107
+ "multiheaded_attention_heads": num_heads,
108
+ }
109
+
110
+
111
+ model_sizes = {f"d{depth}": ebfm_depth_scaling(depth) for depth in range(4, 29, 2)}
112
+
113
+
114
+ def init_whole_model_weights(
115
+ model,
116
+ weight_initialization_method,
117
+ nonlinearity="linear",
118
+ weight_initialization_gain=1.0,
119
+ ):
120
+ """Initialize all linear layers using the checkpoint-compatible policy."""
121
+
122
+ def init_weights(module):
123
+ if not isinstance(module, nn.Linear):
124
+ return
125
+ if weight_initialization_method == "he":
126
+ valid_nonlinearities = [
127
+ "linear",
128
+ "relu",
129
+ "leaky_relu",
130
+ "selu",
131
+ "tanh",
132
+ ]
133
+ if nonlinearity not in valid_nonlinearities:
134
+ raise ValueError(
135
+ f"Unsupported nonlinearity: {nonlinearity}. "
136
+ f"Must be one of {valid_nonlinearities}"
137
+ )
138
+ nn.init.kaiming_normal_(module.weight, nonlinearity=nonlinearity)
139
+ elif weight_initialization_method == "xavier":
140
+ nn.init.xavier_normal_(module.weight)
141
+ else:
142
+ raise ValueError(
143
+ f"Unknown weight init method: {weight_initialization_method}"
144
+ )
145
+ if weight_initialization_gain != 1.0:
146
+ module.weight.data *= weight_initialization_gain
147
+ if module.bias is not None:
148
+ nn.init.constant_(module.bias, 0.0)
149
+
150
+ model.apply(init_weights)
151
+
152
+
153
+ def setup_ebfm(hparams):
154
+ """Construct the canonical time-embedding trunk from saved hparams."""
155
+
156
+ max_seq_len = hparams.context_length + 1
157
+ if not hasattr(hparams, "vocab_size"):
158
+ raise AttributeError(
159
+ "hparams must define explicit vocab_size before setup_ebfm()"
160
+ )
161
+ transformer_args = EBFMModelArgs(
162
+ dim=hparams.embedding_dim,
163
+ n_layers=hparams.num_transformer_blocks,
164
+ n_heads=hparams.multiheaded_attention_heads,
165
+ max_batch_size=hparams.batch_size_per_device,
166
+ max_seq_len=max_seq_len,
167
+ weight_initialization=hparams.weight_initialization_method,
168
+ ebfm_norm=hparams.ebfm_norm,
169
+ ffn_dim_multiplier=hparams.ffn_dim_multiplier,
170
+ ebfm_act_func=hparams.ebfm_act_func,
171
+ weight_initialization_gain=hparams.weight_initialization_gain,
172
+ vocab_size=hparams.vocab_size,
173
+ use_sdpa_attention=getattr(hparams, "use_sdpa_attention", False),
174
+ float_precision=getattr(hparams, "float_precision", "32-true"),
175
+ )
176
+ return EBFMTimeConcat(
177
+ params=transformer_args,
178
+ gradient_checkpointing=getattr(hparams, "gradient_checkpointing", False),
179
+ )
180
+
181
+
182
+ def has_layer_norm(model):
183
+ return any(isinstance(module, nn.LayerNorm) for _, module in model.named_modules())
184
+
185
+
186
+ def init_wandb_watch(
187
+ wandb_logger,
188
+ model_trainer,
189
+ wandb_watch_log_freq,
190
+ wandb_watch_level="parameters",
191
+ ):
192
+ """Configure optional W&B watches without importing training utilities."""
193
+
194
+ log_mode = wandb_watch_level
195
+ if not has_layer_norm(model_trainer.model):
196
+ wandb_logger.watch(
197
+ model_trainer.model,
198
+ log=log_mode,
199
+ log_freq=wandb_watch_log_freq,
200
+ )
201
+ return
202
+
203
+ non_layernorm_container = nn.Module()
204
+ layernorm_container = nn.Module()
205
+ non_ln_modules = {}
206
+ ln_modules = {}
207
+
208
+ for name, module in model_trainer.model.named_modules():
209
+ if name == "":
210
+ continue
211
+ safe_name = name.replace(".", "_")
212
+ if isinstance(module, nn.LayerNorm):
213
+ ln_modules[safe_name] = module
214
+ else:
215
+ has_ln_child = any(
216
+ isinstance(child, nn.LayerNorm) for child in module.modules()
217
+ )
218
+ if not has_ln_child:
219
+ non_ln_modules[safe_name] = module
220
+
221
+ for name, module in non_ln_modules.items():
222
+ non_layernorm_container.add_module(name, module)
223
+ for name, module in ln_modules.items():
224
+ layernorm_container.add_module(name, module)
225
+
226
+ wandb_logger.watch(
227
+ non_layernorm_container,
228
+ log=log_mode,
229
+ log_freq=wandb_watch_log_freq,
230
+ )
231
+ ln_log_mode = "parameters" if log_mode in ("gradients", "all") else log_mode
232
+ wandb_logger.watch(
233
+ layernorm_container,
234
+ log=ln_log_mode,
235
+ log_freq=wandb_watch_log_freq,
236
+ )
237
+
238
+
239
+ class RMSNorm(torch.nn.Module):
240
+ def __init__(self, dim: int, eps: float = 1e-6, use_fp32_norm: bool = True):
241
+ """
242
+ Initialize the RMSNorm normalization layer.
243
+
244
+ Args:
245
+ dim (int): The dimension of the input tensor.
246
+ eps (float, optional): A small value added to the denominator for numerical stability. Default is 1e-6.
247
+ use_fp32_norm (bool): If True, compute normalization in FP32 then cast back.
248
+ Should be False for bf16-true to avoid FP32 graph nodes.
249
+
250
+ Attributes:
251
+ eps (float): A small value added to the denominator for numerical stability.
252
+ weight (nn.Parameter): Learnable scaling parameter.
253
+
254
+ """
255
+ super().__init__()
256
+ self.eps = eps
257
+ self.weight = nn.Parameter(torch.ones(dim))
258
+ self.use_fp32_norm = use_fp32_norm
259
+
260
+ def _norm(self, x):
261
+ """
262
+ Apply the RMSNorm normalization to the input tensor.
263
+
264
+ Args:
265
+ x (torch.Tensor): The input tensor.
266
+
267
+ Returns:
268
+ torch.Tensor: The normalized tensor.
269
+
270
+ """
271
+ return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
272
+
273
+ def forward(self, x):
274
+ """
275
+ Forward pass through the RMSNorm layer.
276
+
277
+ Args:
278
+ x (torch.Tensor): The input tensor.
279
+
280
+ Returns:
281
+ torch.Tensor: The output tensor after applying RMSNorm.
282
+
283
+ """
284
+ if self.use_fp32_norm:
285
+ output = self._norm(x.float()).type_as(x)
286
+ else:
287
+ output = self._norm(x)
288
+ return output * self.weight
289
+
290
+
291
+ def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0):
292
+ """
293
+ Precompute the cosine and sine frequency tensors for rotary embeddings.
294
+
295
+ Returns a tuple of (cos, sin) tensors with shape (end, dim//2), stored as
296
+ float32 but applied via real-valued arithmetic to avoid dtype casting in
297
+ the forward/backward graph.
298
+
299
+ Args:
300
+ dim (int): Head dimension.
301
+ end (int): Maximum sequence length.
302
+ theta (float, optional): RoPE base. Defaults to 10000.0.
303
+
304
+ Returns:
305
+ Tuple[torch.Tensor, torch.Tensor]: (cos, sin) each of shape (end, dim//2).
306
+ """
307
+ freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
308
+ t = torch.arange(end, device=freqs.device)
309
+ freqs = torch.outer(t, freqs)
310
+ return freqs.cos(), freqs.sin()
311
+
312
+
313
+ def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor):
314
+ """
315
+ Reshape frequency tensor for broadcasting it with another tensor.
316
+
317
+ Args:
318
+ freqs_cis (torch.Tensor): Frequency tensor of shape (seq, head_dim//2) complex.
319
+ x (torch.Tensor): Target tensor of shape (bs, seq, n_heads, head_dim//2) complex.
320
+
321
+ Returns:
322
+ torch.Tensor: Reshaped frequency tensor for broadcast.
323
+ """
324
+ ndim = x.ndim
325
+
326
+ assert ndim >= 2, (
327
+ "reshape_for_broadcast expects x to have at least two dimensions "
328
+ f"with a sequence axis at dim=1; got x.ndim={ndim}, shape={tuple(x.shape)}"
329
+ )
330
+ expected_freqs_shape = (x.shape[1], x.shape[-1])
331
+ assert freqs_cis.shape == expected_freqs_shape, (
332
+ "freqs_cis shape must match x sequence and rotary dimensions: "
333
+ f"expected {expected_freqs_shape}, got {tuple(freqs_cis.shape)}"
334
+ )
335
+ shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
336
+ return freqs_cis.view(*shape)
337
+
338
+
339
+ def apply_rotary_emb(
340
+ xq: torch.Tensor,
341
+ xk: torch.Tensor,
342
+ freqs_cis: Tuple[torch.Tensor, torch.Tensor],
343
+ rope_use_complex_fp32: bool = True,
344
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
345
+ """
346
+ Apply rotary embeddings to query and key tensors.
347
+
348
+ Supports two paths:
349
+ - rope_use_complex_fp32=True: complex-float32 arithmetic (for bf16-mixed / fp32 modes).
350
+ - rope_use_complex_fp32=False: real-valued arithmetic in native dtype (for bf16-true mode).
351
+
352
+ The two paths implement the same rotary transform. The complex path treats
353
+ each even/odd hidden-dim pair as the real/imaginary part of a complex value
354
+ and multiplies by cos + i sin; the real path expands the same multiplication
355
+ into the equivalent 2D rotation formula. Numeric differences should only
356
+ come from dtype and rounding behavior.
357
+
358
+ Args:
359
+ xq (torch.Tensor): Query tensor, shape (..., seq, n_heads, head_dim).
360
+ xk (torch.Tensor): Key tensor, shape (..., seq, n_kv_heads, head_dim).
361
+ freqs_cis (Tuple[torch.Tensor, torch.Tensor]): (cos, sin) each of
362
+ shape (seq, head_dim//2), precomputed by precompute_freqs_cis.
363
+ rope_use_complex_fp32 (bool): If True, use complex-float32 arithmetic.
364
+
365
+ Returns:
366
+ Tuple[torch.Tensor, torch.Tensor]: Rotated xq and xk in original dtype.
367
+ """
368
+ cos, sin = freqs_cis
369
+
370
+ if rope_use_complex_fp32:
371
+ freqs_cis_complex = cos + 1j * sin
372
+ xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
373
+ xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
374
+ freqs_cis_complex = reshape_for_broadcast(freqs_cis_complex, xq_)
375
+ xq_out = torch.view_as_real(xq_ * freqs_cis_complex).flatten(3)
376
+ xk_out = torch.view_as_real(xk_ * freqs_cis_complex).flatten(3)
377
+ return xq_out.type_as(xq), xk_out.type_as(xk)
378
+ else:
379
+ cos = cos.to(dtype=xq.dtype)
380
+ sin = sin.to(dtype=xq.dtype)
381
+ xq_r = xq.reshape(*xq.shape[:-1], -1, 2)
382
+ xk_r = xk.reshape(*xk.shape[:-1], -1, 2)
383
+ xq0, xq1 = xq_r[..., 0], xq_r[..., 1]
384
+ xk0, xk1 = xk_r[..., 0], xk_r[..., 1]
385
+ cos = cos.unsqueeze(0).unsqueeze(2)
386
+ sin = sin.unsqueeze(0).unsqueeze(2)
387
+ xq_out = torch.stack(
388
+ [xq0 * cos - xq1 * sin, xq0 * sin + xq1 * cos], dim=-1
389
+ ).flatten(-2)
390
+ xk_out = torch.stack(
391
+ [xk0 * cos - xk1 * sin, xk0 * sin + xk1 * cos], dim=-1
392
+ ).flatten(-2)
393
+ return xq_out, xk_out
394
+
395
+
396
+ def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor:
397
+ """torch.repeat_interleave(x, dim=2, repeats=n_rep)"""
398
+ bs, slen, n_kv_heads, head_dim = x.shape
399
+ if n_rep == 1:
400
+ return x
401
+ return (
402
+ x[:, :, :, None, :]
403
+ .expand(bs, slen, n_kv_heads, n_rep, head_dim)
404
+ .reshape(bs, slen, n_kv_heads * n_rep, head_dim)
405
+ )
406
+
407
+
408
+ class Attention(nn.Module):
409
+ """Multi-head attention module."""
410
+
411
+ def __init__(self, layer_id: int, args: EBFMModelArgs):
412
+ """
413
+ Initialize the Attention module.
414
+
415
+ Args:
416
+ args (EBFMModelArgs): Model configuration parameters.
417
+
418
+ Attributes:
419
+ n_kv_heads (int): Number of key and value heads.
420
+ n_local_heads (int): Number of local query heads.
421
+ n_local_kv_heads (int): Number of local key and value heads.
422
+ n_rep (int): Number of repetitions for local heads.
423
+ head_dim (int): Dimension size of each attention head.
424
+ wq (ColumnParallelLinear): Linear transformation for queries.
425
+ wk (ColumnParallelLinear): Linear transformation for keys.
426
+ wv (ColumnParallelLinear): Linear transformation for values.
427
+ wo (RowParallelLinear): Linear transformation for output.
428
+ cache_k (torch.Tensor): Cached keys for attention.
429
+ cache_v (torch.Tensor): Cached values for attention.
430
+
431
+ """
432
+ super().__init__()
433
+ self.n_kv_heads = args.n_heads if args.n_kv_heads is None else args.n_kv_heads
434
+ model_parallel_size = 1
435
+ self.n_local_heads = args.n_heads // model_parallel_size
436
+ self.n_local_kv_heads = self.n_kv_heads // model_parallel_size
437
+ self.n_rep = self.n_local_heads // self.n_local_kv_heads
438
+ self.head_dim = args.dim // args.n_heads
439
+ self.wq = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)
440
+ init_whole_model_weights(
441
+ self.wq,
442
+ args.weight_initialization,
443
+ weight_initialization_gain=args.weight_initialization_gain,
444
+ )
445
+
446
+ self.wk = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)
447
+ init_whole_model_weights(
448
+ self.wk,
449
+ args.weight_initialization,
450
+ weight_initialization_gain=args.weight_initialization_gain,
451
+ )
452
+
453
+ self.wv = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)
454
+ init_whole_model_weights(
455
+ self.wv,
456
+ args.weight_initialization,
457
+ weight_initialization_gain=args.weight_initialization_gain,
458
+ )
459
+
460
+ self.wo = nn.Linear(args.n_heads * self.head_dim, args.dim, bias=False)
461
+ init_whole_model_weights(
462
+ self.wo,
463
+ args.weight_initialization,
464
+ weight_initialization_gain=args.weight_initialization_gain,
465
+ )
466
+
467
+ self.time_offset = 1
468
+ self.rope_use_complex_fp32 = getattr(args, "float_precision", "") != "bf16-true"
469
+ self.use_sdpa_attention = args.use_sdpa_attention
470
+
471
+ self.register_buffer("superdiag_rows", torch.arange(args.max_seq_len - 1))
472
+ self.register_buffer(
473
+ "superdiag_cols",
474
+ torch.arange(self.time_offset, args.max_seq_len + self.time_offset - 1),
475
+ )
476
+
477
+ def forward(
478
+ self,
479
+ x: torch.Tensor,
480
+ start_pos: int,
481
+ freqs_cis: torch.Tensor,
482
+ mask: Optional[torch.Tensor],
483
+ ):
484
+ """
485
+ Forward pass of the attention module.
486
+
487
+ Args:
488
+ x (torch.Tensor): Input tensor.
489
+ start_pos (int): Starting position for caching.
490
+ freqs_cis (torch.Tensor): Precomputed frequency tensor.
491
+ mask (torch.Tensor, optional): Attention mask tensor.
492
+
493
+ Returns:
494
+ torch.Tensor: Output tensor after attention.
495
+
496
+ """
497
+
498
+ bsz, full_seqlen, _ = x.shape
499
+ original_seqlen = (full_seqlen + 1) // 2
500
+ xq, xk, xv = self.wq(x), self.wk(x), self.wv(x)
501
+
502
+ xq = xq.view(bsz, full_seqlen, self.n_local_heads, self.head_dim)
503
+ xk = xk.view(bsz, full_seqlen, self.n_local_kv_heads, self.head_dim)
504
+ xv = xv.view(bsz, full_seqlen, self.n_local_kv_heads, self.head_dim)
505
+
506
+ xq_o = xq[:, :original_seqlen, :, :]
507
+ xk_o = xk[:, :original_seqlen, :, :]
508
+ xv_o = xv[:, :original_seqlen, :, :]
509
+
510
+ xq_p = xq[:, original_seqlen:, :, :]
511
+ xk_p = xk[:, original_seqlen:, :, :]
512
+ xv_p = xv[:, original_seqlen:, :, :]
513
+
514
+ cos, sin = freqs_cis
515
+ xq_o, xk_o = apply_rotary_emb(
516
+ xq_o,
517
+ xk_o,
518
+ freqs_cis=(cos[:original_seqlen], sin[:original_seqlen]),
519
+ rope_use_complex_fp32=self.rope_use_complex_fp32,
520
+ )
521
+
522
+ xq_p, xk_p = apply_rotary_emb(
523
+ xq_p,
524
+ xk_p,
525
+ freqs_cis=(
526
+ cos[self.time_offset : original_seqlen + 1],
527
+ sin[self.time_offset : original_seqlen + 1],
528
+ ),
529
+ rope_use_complex_fp32=self.rope_use_complex_fp32,
530
+ )
531
+
532
+ xq_o = xq_o.transpose(1, 2)
533
+ keys_o = xk_o.transpose(1, 2)
534
+ values_o = xv_o.transpose(1, 2)
535
+
536
+ if self.use_sdpa_attention:
537
+ with torch.backends.cuda.sdp_kernel(
538
+ enable_flash=False,
539
+ enable_math=True,
540
+ enable_mem_efficient=False,
541
+ enable_cudnn=False,
542
+ ):
543
+ output_o = F.scaled_dot_product_attention(
544
+ xq_o, keys_o, values_o, is_causal=True
545
+ )
546
+ output_o = (
547
+ output_o.transpose(1, 2).contiguous().view(bsz, original_seqlen, -1)
548
+ )
549
+ else:
550
+ scores_o = torch.matmul(xq_o, keys_o.transpose(2, 3)) / math.sqrt(
551
+ self.head_dim
552
+ )
553
+ if mask is not None:
554
+ o_mask = mask[:-1, :-1]
555
+ scores_o = scores_o + o_mask
556
+ scores_o = F.softmax(scores_o.float(), dim=-1).type_as(xq_o)
557
+ output_o = torch.matmul(scores_o, values_o)
558
+ output_o = (
559
+ output_o.transpose(1, 2).contiguous().view(bsz, original_seqlen, -1)
560
+ )
561
+
562
+ xq_p = xq_p.transpose(1, 2)
563
+ keys_p = xk_p.transpose(1, 2)
564
+ values_p = xv_p.transpose(1, 2)
565
+ n_pred = full_seqlen - original_seqlen
566
+
567
+ if self.use_sdpa_attention:
568
+ K = original_seqlen
569
+ pred_seqlen = n_pred
570
+
571
+ keys_all = torch.cat([keys_o, keys_p], dim=2)
572
+ values_all = torch.cat([values_o, values_p], dim=2)
573
+ orig_rows = torch.arange(pred_seqlen, device=x.device).unsqueeze(1)
574
+ orig_cols = torch.arange(K, device=x.device).unsqueeze(0)
575
+ orig_mask_part = torch.where(
576
+ orig_cols <= orig_rows + (self.time_offset - 1),
577
+ x.new_zeros(1),
578
+ x.new_full((1,), float("-inf")),
579
+ )
580
+
581
+ self_mask_part = x.new_full((pred_seqlen, pred_seqlen), float("-inf"))
582
+ self_mask_part.fill_diagonal_(0.0)
583
+
584
+ mask_p = torch.cat([orig_mask_part, self_mask_part], dim=1)
585
+
586
+ with torch.backends.cuda.sdp_kernel(
587
+ enable_flash=False,
588
+ enable_math=True,
589
+ enable_mem_efficient=False,
590
+ enable_cudnn=False,
591
+ ):
592
+ output_p = F.scaled_dot_product_attention(
593
+ xq_p, keys_all, values_all, attn_mask=mask_p
594
+ )
595
+ output_p = output_p.transpose(1, 2).contiguous().view(bsz, n_pred, -1)
596
+ else:
597
+ scores_p = torch.matmul(xq_p, keys_o.transpose(2, 3)) / math.sqrt(
598
+ self.head_dim
599
+ )
600
+
601
+ temp_append = torch.zeros(
602
+ (scores_p.shape[0], scores_p.shape[1], scores_p.shape[2], 1),
603
+ dtype=scores_p.dtype,
604
+ device=scores_p.device,
605
+ )
606
+ scores_p = torch.cat((scores_p, temp_append), dim=-1)
607
+
608
+ insertion_superdiagonal = (xq_p * keys_p).sum(dim=3) / math.sqrt(
609
+ self.head_dim
610
+ )
611
+ insertion_superdiagonal = insertion_superdiagonal.to(scores_p.dtype)
612
+
613
+ seq_len_minus_1 = scores_p.shape[2]
614
+ if seq_len_minus_1 <= self.superdiag_rows.numel():
615
+ superdiag_rows = self.superdiag_rows[:seq_len_minus_1]
616
+ superdiag_cols = self.superdiag_cols[:seq_len_minus_1]
617
+ else:
618
+ superdiag_rows = torch.arange(seq_len_minus_1, device=scores_p.device)
619
+ superdiag_cols = torch.arange(
620
+ self.time_offset,
621
+ self.time_offset + seq_len_minus_1,
622
+ device=scores_p.device,
623
+ )
624
+
625
+ zero_superdiag = torch.zeros_like(
626
+ insertion_superdiagonal, dtype=scores_p.dtype, device=scores_p.device
627
+ )
628
+ diagonal_removal_mask = torch.ones_like(
629
+ scores_p, dtype=scores_p.dtype, device=scores_p.device
630
+ )
631
+ diagonal_removal_mask[:, :, superdiag_rows, superdiag_cols] = zero_superdiag
632
+ scores_p = scores_p * diagonal_removal_mask
633
+
634
+ diagonal_addition_mask = torch.zeros_like(
635
+ scores_p, dtype=scores_p.dtype, device=scores_p.device
636
+ )
637
+ diagonal_addition_mask[:, :, superdiag_rows, superdiag_cols] = (
638
+ insertion_superdiagonal
639
+ )
640
+ scores_p = scores_p + diagonal_addition_mask
641
+
642
+ if mask is not None:
643
+ p_mask = mask[self.time_offset :, :]
644
+ scores_p = scores_p + p_mask
645
+ if scores_p.dtype != torch.float32:
646
+ scores_p = scores_p.float()
647
+ scores_p = F.softmax(scores_p, dim=-1)
648
+ if scores_p.dtype != xq_p.dtype:
649
+ scores_p = scores_p.to(xq_p.dtype)
650
+
651
+ scores_p_superdiagonal = scores_p.diagonal(
652
+ offset=self.time_offset, dim1=2, dim2=3
653
+ )
654
+
655
+ scores_p = scores_p * diagonal_removal_mask
656
+
657
+ scores_p = scores_p[:, :, :, :-1]
658
+ output_p = torch.matmul(scores_p, values_o)
659
+
660
+ next_pred_self_attention = values_p * scores_p_superdiagonal.unsqueeze(
661
+ dim=-1
662
+ )
663
+
664
+ output_p = output_p + next_pred_self_attention
665
+ output_p = output_p.transpose(1, 2).contiguous().view(bsz, n_pred, -1)
666
+
667
+ output = torch.cat((output_o, output_p), dim=1)
668
+ return self.wo(output)
669
+
670
+
671
+ class FeedForward(nn.Module):
672
+ def __init__(
673
+ self,
674
+ dim: int,
675
+ ffn_dim_multiplier: Optional[float],
676
+ weight_initialization: str,
677
+ ebfm_act_func: str = "silu",
678
+ weight_initialization_gain: float = 1.0,
679
+ ):
680
+ """
681
+ Initialize the FeedForward module.
682
+
683
+ Args:
684
+ dim (int): Input dimension.
685
+ hidden_dim (int): Hidden dimension of the feedforward layer.
686
+ multiple_of (int): Value to ensure hidden dimension is a multiple of this value.
687
+ ffn_dim_multiplier (float, optional): Custom multiplier for hidden dimension. Defaults to None.
688
+
689
+ Attributes:
690
+ w1 (ColumnParallelLinear): Linear transformation for the first layer.
691
+ w2 (RowParallelLinear): Linear transformation for the second layer.
692
+ w3 (ColumnParallelLinear): Linear transformation for the third layer.
693
+
694
+ """
695
+ super().__init__()
696
+ hidden_dim = (
697
+ dim if ffn_dim_multiplier is None else int(dim * ffn_dim_multiplier)
698
+ )
699
+
700
+ self.w1 = nn.Linear(dim, hidden_dim, bias=False)
701
+ init_whole_model_weights(
702
+ self.w1,
703
+ weight_initialization,
704
+ weight_initialization_gain=weight_initialization_gain,
705
+ )
706
+
707
+ self.w2 = nn.Linear(hidden_dim, dim, bias=False)
708
+ init_whole_model_weights(
709
+ self.w2,
710
+ weight_initialization,
711
+ weight_initialization_gain=weight_initialization_gain,
712
+ )
713
+
714
+ self.w3 = nn.Linear(dim, hidden_dim, bias=False)
715
+ init_whole_model_weights(
716
+ self.w3,
717
+ weight_initialization,
718
+ weight_initialization_gain=weight_initialization_gain,
719
+ )
720
+
721
+ self.act_func = {"silu": F.silu, "relu": F.relu, "gelu": F.gelu, "elu": F.elu}[
722
+ ebfm_act_func
723
+ ]
724
+
725
+ def forward(self, x):
726
+ return self.w2(self.act_func(self.w1(x)) * self.w3(x))
727
+
728
+
729
+ class TransformerBlock(nn.Module):
730
+ def __init__(self, layer_id: int, args: EBFMModelArgs):
731
+ """
732
+ Initialize a TransformerBlock.
733
+
734
+ Args:
735
+ layer_id (int): Identifier for the layer.
736
+ args (EBFMModelArgs): Model configuration parameters.
737
+
738
+ Attributes:
739
+ n_heads (int): Number of attention heads.
740
+ dim (int): Dimension size of the model.
741
+ head_dim (int): Dimension size of each attention head.
742
+ attention (Attention): Attention module.
743
+ feed_forward (FeedForward): FeedForward module.
744
+ layer_id (int): Identifier for the layer.
745
+ attention_norm (RMSNorm): Layer normalization for attention output.
746
+ ffn_norm (RMSNorm): Layer normalization for feedforward output.
747
+
748
+ """
749
+ super().__init__()
750
+ self.n_heads = args.n_heads
751
+ self.dim = args.dim
752
+ self.head_dim = args.dim // args.n_heads
753
+ self.attention = Attention(layer_id, args)
754
+ self.feed_forward = FeedForward(
755
+ dim=args.dim,
756
+ ffn_dim_multiplier=args.ffn_dim_multiplier,
757
+ weight_initialization=args.weight_initialization,
758
+ ebfm_act_func=args.ebfm_act_func,
759
+ weight_initialization_gain=args.weight_initialization_gain,
760
+ )
761
+ self.layer_id = layer_id
762
+ use_fp32 = getattr(args, "float_precision", "") != "bf16-true"
763
+ if args.ebfm_norm != "rms":
764
+ raise ValueError(f"Invalid ebfm_norm value: {args.ebfm_norm}")
765
+ self.attention_norm = RMSNorm(
766
+ args.dim, eps=args.norm_eps, use_fp32_norm=use_fp32
767
+ )
768
+ self.ffn_norm = RMSNorm(args.dim, eps=args.norm_eps, use_fp32_norm=use_fp32)
769
+
770
+ def forward(
771
+ self,
772
+ x: torch.Tensor,
773
+ start_pos: int,
774
+ freqs_cis: torch.Tensor,
775
+ mask: Optional[torch.Tensor],
776
+ ):
777
+ """
778
+ Perform a forward pass through the TransformerBlock.
779
+
780
+ Args:
781
+ x (torch.Tensor): Input tensor.
782
+ start_pos (int): Starting position for attention caching.
783
+ freqs_cis (torch.Tensor): Precomputed cosine and sine frequencies.
784
+ mask (torch.Tensor, optional): Masking tensor for attention. Defaults to None.
785
+
786
+ Returns:
787
+ torch.Tensor: Output tensor after applying attention and feedforward layers.
788
+
789
+ """
790
+
791
+ h = x + self.attention(self.attention_norm(x), start_pos, freqs_cis, mask)
792
+ out = h + self.feed_forward(self.ffn_norm(h))
793
+ return out
794
+
795
+
796
+ class EBFMTimeConcat(nn.Module):
797
+ def __init__(self, params: EBFMModelArgs, gradient_checkpointing=False):
798
+ """
799
+ Initialize a Transformer model.
800
+
801
+ Args:
802
+ params (EBFMModelArgs): Model configuration parameters.
803
+ Attributes:
804
+ params (EBFMModelArgs): Model configuration parameters.
805
+ n_layers (int): Number of layers in the model.
806
+ layers (torch.nn.ModuleList): List of Transformer blocks.
807
+ norm (RMSNorm): Layer normalization for the model output.
808
+ output (ColumnParallelLinear): Linear layer for final output.
809
+ freqs_cis (torch.Tensor): Precomputed cosine and sine frequencies.
810
+
811
+ """
812
+ super().__init__()
813
+ self.params = params
814
+ self.n_layers = params.n_layers
815
+ self.gradient_checkpointing = gradient_checkpointing
816
+ self.layers = torch.nn.ModuleList()
817
+ for layer_id in range(params.n_layers):
818
+ self.layers.append(TransformerBlock(layer_id, params))
819
+
820
+ use_fp32 = getattr(params, "float_precision", "") != "bf16-true"
821
+ if params.ebfm_norm != "rms":
822
+ raise ValueError(f"Invalid ebfm_norm value: {params.ebfm_norm}")
823
+ self.norm = RMSNorm(params.dim, eps=params.norm_eps, use_fp32_norm=use_fp32)
824
+
825
+ freqs_cos, freqs_sin = precompute_freqs_cis(
826
+ self.params.dim // self.params.n_heads, self.params.max_seq_len + 1
827
+ )
828
+ self.register_buffer("freqs_cos", freqs_cos, persistent=False)
829
+ self.register_buffer("freqs_sin", freqs_sin, persistent=False)
830
+
831
+ self.final_layer = nn.Linear(params.dim, 1, bias=False)
832
+ init_whole_model_weights(self.final_layer, self.params.weight_initialization)
833
+
834
+ def forward(
835
+ self,
836
+ embeddings: torch.Tensor,
837
+ start_pos: int,
838
+ mcmc_step=0,
839
+ real_token_ids: Optional[torch.Tensor] = None,
840
+ predicted_tokens: Optional[torch.Tensor] = None,
841
+ return_pred_hidden: bool = False,
842
+ ):
843
+ """
844
+ Perform a forward pass through the Transformer model.
845
+
846
+ Args:
847
+ embeds (torch.Tensor): Embeddings (instead of tokens since is for vision).
848
+ start_pos (int): Starting position for attention caching.
849
+ mcmc_step (int): Unused argument; the trunk is shared across MCMC steps.
850
+ return_pred_hidden (bool): When True, also return the post-norm hidden
851
+ state at candidate positions ([B, S, D]) used by the TF head.
852
+
853
+ Returns:
854
+ torch.Tensor: Output energies after applying the Transformer model.
855
+ (energies, pred_hidden) tuple when return_pred_hidden=True.
856
+
857
+ """
858
+ _bsz, seqlen = embeddings.shape[:2]
859
+ seqlen = (seqlen + 2) // 2
860
+
861
+ required_length = start_pos + seqlen
862
+ if required_length > self.freqs_cos.shape[0]:
863
+ new_cos, new_sin = precompute_freqs_cis(
864
+ self.params.dim // self.params.n_heads, required_length
865
+ )
866
+ self.freqs_cos = new_cos.to(embeddings.device)
867
+ self.freqs_sin = new_sin.to(embeddings.device)
868
+
869
+ freqs_cis = (
870
+ self.freqs_cos[start_pos : start_pos + seqlen],
871
+ self.freqs_sin[start_pos : start_pos + seqlen],
872
+ )
873
+
874
+ mask = None
875
+ if seqlen > 1:
876
+ mask = torch.empty((seqlen, seqlen), device=embeddings.device).fill_(
877
+ float("-inf")
878
+ )
879
+
880
+ mask.triu_(diagonal=1)
881
+
882
+ mask = torch.hstack(
883
+ [torch.zeros((seqlen, start_pos), device=embeddings.device), mask]
884
+ ).type_as(embeddings)
885
+
886
+ for layer in self.layers:
887
+ if (
888
+ self.gradient_checkpointing
889
+ and self.training
890
+ and torch.is_grad_enabled()
891
+ ):
892
+ embeddings = checkpoint(
893
+ layer,
894
+ embeddings,
895
+ start_pos,
896
+ freqs_cis,
897
+ mask,
898
+ use_reentrant=False,
899
+ )
900
+ else:
901
+ embeddings = layer(embeddings, start_pos, freqs_cis, mask)
902
+ embeddings = self.norm(embeddings)
903
+ split_idx = embeddings.shape[1] // 2
904
+ energies = self.final_layer(embeddings)
905
+ energies = energies[:, split_idx:]
906
+ if return_pred_hidden:
907
+ pred_hidden = embeddings[:, split_idx:]
908
+ return energies, pred_hidden
909
+ return energies
910
+
911
+
912
+ class _HParams(dict):
913
+ """Small attribute-accessible mapping used by the EBFM module."""
914
+
915
+ def __getattr__(self, name):
916
+ try:
917
+ return self[name]
918
+ except KeyError as exc:
919
+ raise AttributeError(name) from exc
920
+
921
+ def __setattr__(self, name, value):
922
+ self[name] = value
923
+
924
+
925
+ class _EBFMModule(nn.Module):
926
+ """Minimal EBFM base module; training lifecycle belongs to ``ModelTrainer``."""
927
+
928
+ def __init__(self):
929
+ super().__init__()
930
+ self.hparams = _HParams()
931
+
932
+ @property
933
+ def device(self):
934
+ try:
935
+ return next(self.parameters()).device
936
+ except StopIteration:
937
+ return torch.device("cpu")
938
+
939
+
940
+ PRECISION_TO_MCMC_DTYPE = {
941
+ "bf16-true": torch.bfloat16,
942
+ "bf16-mixed": torch.float32,
943
+ "16-mixed": torch.float32,
944
+ "32-true": torch.float32,
945
+ }
946
+
947
+
948
+ class EBFM_NLP(_EBFMModule):
949
+ """EBFM's single paper path for NLP training and validation."""
950
+
951
+ def __init__(self, hparams):
952
+ super().__init__()
953
+ if isinstance(hparams, dict):
954
+ self.hparams.update(hparams)
955
+ else:
956
+ self.hparams.update(vars(hparams))
957
+
958
+ self._validate_paper_path()
959
+
960
+ self.tokenizer = (
961
+ self.hparams.tokenizer_obj
962
+ if hasattr(self.hparams, "tokenizer_obj")
963
+ else self.hparams.tokenizer
964
+ )
965
+ self.tokenizer_pad_token_id = None
966
+ if hasattr(self.tokenizer, "get_vocab_size"):
967
+ self.vocab_size = self.tokenizer.get_vocab_size()
968
+ else:
969
+ self.vocab_size = len(self.tokenizer)
970
+ self.hparams.vocab_size = self.vocab_size
971
+
972
+ self.alpha = nn.Parameter(
973
+ torch.tensor(float(self.hparams.mcmc_step_size), dtype=torch.float32),
974
+ requires_grad=bool(self.hparams.mcmc_step_size_learnable),
975
+ )
976
+
977
+ self.embeddings = nn.Embedding(self.vocab_size, self.hparams.embedding_dim)
978
+ init_whole_model_weights(
979
+ self.embeddings,
980
+ self.hparams.weight_initialization_method,
981
+ weight_initialization_gain=self.hparams.weight_initialization_gain,
982
+ )
983
+
984
+ self.vocab_to_embed = nn.Linear(
985
+ self.vocab_size,
986
+ self.hparams.embedding_dim,
987
+ bias=False,
988
+ device=self.device,
989
+ )
990
+ init_whole_model_weights(
991
+ self.vocab_to_embed,
992
+ self.hparams.weight_initialization_method,
993
+ weight_initialization_gain=self.hparams.weight_initialization_gain,
994
+ )
995
+
996
+ self.transformer = setup_ebfm(self.hparams)
997
+
998
+ self.use_tf_head = True
999
+ self.use_free_embedding_mcmc = True
1000
+ self.tf_head = build_tf_head(self.hparams)
1001
+ init_whole_model_weights(
1002
+ self.tf_head,
1003
+ self.hparams.weight_initialization_method,
1004
+ weight_initialization_gain=self.hparams.weight_initialization_gain,
1005
+ )
1006
+ self.finished_warming_up = False
1007
+
1008
+ def _validate_paper_path(self):
1009
+ """Reject removed architectures instead of silently creating a mismatch."""
1010
+
1011
+ required = {
1012
+ "modality": "NLP",
1013
+ "time_embedding": True,
1014
+ "mcmc_num_steps": 1,
1015
+ "tf_head_type": "direct_unembed",
1016
+ "use_tf_head": True,
1017
+ "free_embedding_mcmc": True,
1018
+ }
1019
+ for name, expected in required.items():
1020
+ actual = getattr(self.hparams, name, None)
1021
+ if actual != expected:
1022
+ raise ValueError(
1023
+ f"EBFM only supports the paper path: {name}={expected!r}; "
1024
+ f"received {actual!r}."
1025
+ )
1026
+
1027
+ model_size = getattr(self.hparams, "model_size", None)
1028
+ if model_size is not None and model_size not in model_sizes:
1029
+ valid_sizes = ", ".join(model_sizes)
1030
+ raise ValueError(
1031
+ f"unsupported EBFM model_size={model_size!r}; use one of: {valid_sizes}"
1032
+ )
1033
+
1034
+ def _apply(self, fn):
1035
+ """Keep the learned MCMC step size in fp32 during dtype conversion."""
1036
+
1037
+ result = super()._apply(fn)
1038
+ result.alpha.data = result.alpha.data.to(dtype=torch.float32)
1039
+ return result
1040
+
1041
+ @torch.compiler.disable
1042
+ def _mcmc_step_excluded(
1043
+ self,
1044
+ predicted_tokens,
1045
+ real_embeddings_input,
1046
+ mcmc_step,
1047
+ start_pos,
1048
+ learning,
1049
+ compute_pred_hidden=False,
1050
+ ):
1051
+ """Run the paper's one gradient update outside a compiled graph."""
1052
+
1053
+ predicted_tokens = predicted_tokens.detach().requires_grad_()
1054
+ all_embeddings = torch.cat(
1055
+ (real_embeddings_input.detach(), predicted_tokens), dim=1
1056
+ )
1057
+
1058
+ transformer = getattr(self, "transformer_eager", self.transformer)
1059
+ energy_preds = transformer(
1060
+ all_embeddings,
1061
+ start_pos=start_pos,
1062
+ mcmc_step=mcmc_step,
1063
+ real_token_ids=None,
1064
+ predicted_tokens=predicted_tokens,
1065
+ ).reshape(-1, 1)
1066
+
1067
+ with torch.amp.autocast(device_type="cuda", enabled=False):
1068
+ predicted_tokens_grad = torch.autograd.grad(
1069
+ energy_preds.float().sum(),
1070
+ predicted_tokens,
1071
+ create_graph=learning,
1072
+ )[0]
1073
+
1074
+ if (
1075
+ torch.isnan(predicted_tokens_grad).any()
1076
+ or torch.isinf(predicted_tokens_grad).any()
1077
+ ):
1078
+ raise ValueError("NaN or Inf gradients detected during EBFM MCMC.")
1079
+
1080
+ alpha = torch.clamp(self.alpha, min=0.0001).float()
1081
+ predicted_tokens = (predicted_tokens - alpha * predicted_tokens_grad).to(
1082
+ dtype=all_embeddings.dtype
1083
+ )
1084
+
1085
+ pred_hidden = None
1086
+ if compute_pred_hidden:
1087
+ post_embeddings = torch.cat(
1088
+ (real_embeddings_input.detach(), predicted_tokens), dim=1
1089
+ )
1090
+ _, pred_hidden = transformer(
1091
+ post_embeddings,
1092
+ start_pos=start_pos,
1093
+ mcmc_step=mcmc_step,
1094
+ real_token_ids=None,
1095
+ predicted_tokens=predicted_tokens,
1096
+ return_pred_hidden=True,
1097
+ )
1098
+
1099
+ return predicted_tokens, energy_preds.detach(), pred_hidden
1100
+
1101
+ def forward(
1102
+ self,
1103
+ x,
1104
+ start_pos=0,
1105
+ learning=True,
1106
+ return_raw_logits=False,
1107
+ no_randomness=True,
1108
+ return_pred_hiddens=False,
1109
+ ):
1110
+ """Return one updated free-embedding state and its energy."""
1111
+
1112
+ del return_raw_logits, no_randomness
1113
+ real_embeddings_input = self.embeddings(x)
1114
+ predicted_tokens = self.corrupt_embeddings(real_embeddings_input)
1115
+
1116
+ with torch.set_grad_enabled(True):
1117
+ _, energy_preds, pred_hidden = self._mcmc_step_excluded(
1118
+ predicted_tokens,
1119
+ real_embeddings_input,
1120
+ mcmc_step=0,
1121
+ start_pos=start_pos,
1122
+ learning=learning,
1123
+ compute_pred_hidden=return_pred_hiddens,
1124
+ )
1125
+
1126
+ if return_pred_hiddens:
1127
+ return [None], [energy_preds], [pred_hidden]
1128
+ return [None], [energy_preds]
1129
+
1130
+ def forward_loss_wrapper(
1131
+ self, x, phase="train", token_bytes=None, global_step=None
1132
+ ):
1133
+ """Compute the paper's direct-unembedding next-token CE loss."""
1134
+
1135
+ del global_step
1136
+ input_ids = x[0].squeeze(dim=0)
1137
+ next_token_indices = x[1].squeeze(dim=0).reshape(-1)
1138
+ _, predicted_energies, predicted_hiddens = self(
1139
+ input_ids,
1140
+ learning=phase == "train",
1141
+ return_pred_hiddens=True,
1142
+ )
1143
+ pred_hidden = predicted_hiddens[-1]
1144
+ if pred_hidden is None:
1145
+ raise RuntimeError("EBFM did not return the post-update hidden state.")
1146
+
1147
+ logits = self.tf_head(pred_hidden, self.embeddings(input_ids))
1148
+ logits_2d = logits.reshape(-1, self.vocab_size)
1149
+ supervised_tokens = (next_token_indices != -1).sum()
1150
+ supervised_tokens_denom = supervised_tokens.clamp_min(1)
1151
+ per_token_ce = F.cross_entropy(
1152
+ logits_2d,
1153
+ next_token_indices,
1154
+ ignore_index=-1,
1155
+ reduction="none",
1156
+ )
1157
+ loss = per_token_ce.sum() / supervised_tokens_denom
1158
+ ppl = torch.exp(loss.detach())
1159
+
1160
+ if token_bytes is not None:
1161
+ bpb, bpb_nats, bpb_bytes = calculate_bpb_score(
1162
+ next_token_indices,
1163
+ per_token_ce.detach(),
1164
+ token_bytes,
1165
+ )
1166
+ else:
1167
+ bpb = bpb_nats = bpb_bytes = 0
1168
+
1169
+ initial_energy = predicted_energies[0].mean().detach()
1170
+ return {
1171
+ "loss": loss,
1172
+ "initial_loss": loss.detach(),
1173
+ "final_step_loss": loss.detach(),
1174
+ "initial_final_pred_energies_gap": torch.zeros_like(initial_energy),
1175
+ "perplexity": ppl,
1176
+ "bpb": bpb,
1177
+ "bpb_nats": bpb_nats,
1178
+ "bpb_bytes": bpb_bytes,
1179
+ "supervised_tokens": supervised_tokens.detach(),
1180
+ "empty_supervision_batch": (supervised_tokens == 0).to(dtype=torch.int64),
1181
+ }
1182
+
1183
+ def corrupt_embeddings(self, embeddings):
1184
+ """Sample the paper's random D-dimensional free-embedding initial state."""
1185
+
1186
+ float_precision = getattr(self.hparams, "float_precision", "")
1187
+ mcmc_dtype = PRECISION_TO_MCMC_DTYPE.get(float_precision, torch.float32)
1188
+ noise_scale = self.hparams.gaussian_random_noise_scaling * float(
1189
+ getattr(self.hparams, "free_embed_noise_scale", 1.0)
1190
+ )
1191
+ if self.hparams.denoising_initial_condition != "random_noise":
1192
+ raise ValueError(
1193
+ "The paper path uses denoising_initial_condition='random_noise'."
1194
+ )
1195
+ return (
1196
+ torch.randn(
1197
+ embeddings.shape[0],
1198
+ embeddings.shape[1],
1199
+ self.hparams.embedding_dim,
1200
+ dtype=mcmc_dtype,
1201
+ device=self.device,
1202
+ )
1203
+ * noise_scale
1204
+ ).to(dtype=embeddings.dtype)
1205
+
1206
+ def warm_up_finished(self):
1207
+ self.finished_warming_up = True
1208
+
1209
+
1210
+ class TFDirectUnembedHead(nn.Module):
1211
+ """The single supported TF head: post-update hidden state to vocabulary."""
1212
+
1213
+ def __init__(self, dim: int, vocab_size: int):
1214
+ super().__init__()
1215
+ self.proj = nn.Linear(dim, vocab_size, bias=False)
1216
+
1217
+ def forward(self, pred_hidden: torch.Tensor, prev_token_embed: torch.Tensor):
1218
+ del prev_token_embed
1219
+ return self.proj(pred_hidden)
1220
+
1221
+
1222
+ class StandaloneTokenizer:
1223
+ """Load the serialized tokenizer without importing the training package."""
1224
+
1225
+ def __init__(self, encoding):
1226
+ self.encoding = encoding
1227
+ self.bos_token_id = encoding.encode_single_token("<|bos|>")
1228
+ self.eos_token_id = self.bos_token_id
1229
+ self.pad_token_id = self.bos_token_id
1230
+
1231
+ @classmethod
1232
+ def from_directory(cls, tokenizer_dir):
1233
+ path = os.path.join(os.fspath(tokenizer_dir), "tokenizer.pkl")
1234
+ with open(path, "rb") as handle:
1235
+ return cls(pickle.load(handle))
1236
+
1237
+ def get_vocab_size(self):
1238
+ return self.encoding.n_vocab
1239
+
1240
+ def get_bos_token_id(self):
1241
+ return self.bos_token_id
1242
+
1243
+ def encode_special(self, token):
1244
+ return self.encoding.encode_single_token(token)
1245
+
1246
+ def encode(self, text, prepend=None, append=None, num_threads=8):
1247
+ prepend_id = None
1248
+ append_id = None
1249
+ if prepend is not None:
1250
+ prepend_id = (
1251
+ prepend if isinstance(prepend, int) else self.encode_special(prepend)
1252
+ )
1253
+ if append is not None:
1254
+ append_id = (
1255
+ append if isinstance(append, int) else self.encode_special(append)
1256
+ )
1257
+
1258
+ if isinstance(text, str):
1259
+ rows = [self.encoding.encode_ordinary(text)]
1260
+ elif isinstance(text, list):
1261
+ rows = self.encoding.encode_ordinary_batch(text, num_threads=num_threads)
1262
+ else:
1263
+ raise TypeError(f"expected str or list[str], got {type(text).__name__}")
1264
+
1265
+ for row in rows:
1266
+ if prepend_id is not None:
1267
+ row.insert(0, prepend_id)
1268
+ if append_id is not None:
1269
+ row.append(append_id)
1270
+ return rows[0] if isinstance(text, str) else rows
1271
+
1272
+ def __call__(self, text, prepend=None, append=None, num_threads=8):
1273
+ return self.encode(
1274
+ text,
1275
+ prepend=prepend,
1276
+ append=append,
1277
+ num_threads=num_threads,
1278
+ )
1279
+
1280
+ def decode(self, token_ids):
1281
+ return self.encoding.decode(token_ids)
1282
+
1283
+
1284
+ class EBFMTokenizerWrapper:
1285
+ """Minimal tokenizer adapter required while reconstructing the model."""
1286
+
1287
+ def __init__(self, tokenizer_obj):
1288
+ self.tokenizer = tokenizer_obj
1289
+ self.eos_token_id = tokenizer_obj.get_bos_token_id()
1290
+ self.bos_token_id = self.eos_token_id
1291
+ self.pad_token_id = self.eos_token_id
1292
+ self.unk_token_id = 0
1293
+
1294
+ def __len__(self):
1295
+ return self.tokenizer.get_vocab_size()
1296
+
1297
+ def get_vocab_size(self):
1298
+ return self.tokenizer.get_vocab_size()
1299
+
1300
+ def encode(self, text, **kwargs):
1301
+ del kwargs
1302
+ return self.tokenizer.encode(text)
1303
+
1304
+ def decode(self, token_ids, **kwargs):
1305
+ del kwargs
1306
+ return self.tokenizer.decode(token_ids)
1307
+
1308
+
1309
+ class _SerializedObject:
1310
+ pass
1311
+
1312
+
1313
+ class _CheckpointUnpickler(pickle.Unpickler):
1314
+ def find_class(self, module, name):
1315
+ if module.startswith("nanochat."):
1316
+ return _SerializedObject
1317
+ return super().find_class(module, name)
1318
+
1319
+
1320
+ _CHECKPOINT_PICKLE = types.ModuleType("pickle")
1321
+ _CHECKPOINT_PICKLE.Unpickler = _CheckpointUnpickler
1322
+ _CHECKPOINT_PICKLE.Pickler = pickle.Pickler
1323
+ _CHECKPOINT_PICKLE.load = pickle.load
1324
+ _CHECKPOINT_PICKLE.dump = pickle.dump
1325
+
1326
+
1327
+ def load_checkpoint_file(checkpoint_path, map_location="cpu"):
1328
+ """Load a Lightning checkpoint without importing the training repository."""
1329
+
1330
+ return torch.load(
1331
+ checkpoint_path,
1332
+ map_location=map_location,
1333
+ weights_only=False,
1334
+ pickle_module=_CHECKPOINT_PICKLE,
1335
+ )
1336
+
1337
+
1338
+ def get_tokenizer(tokenizer_dir):
1339
+ """Load ``tokenizer.pkl`` from a model repository directory."""
1340
+
1341
+ if tokenizer_dir is None:
1342
+ raise ValueError("tokenizer_dir is required when loading an EBFM checkpoint")
1343
+ return StandaloneTokenizer.from_directory(tokenizer_dir)
1344
+
1345
+
1346
+ def get_token_bytes(device="cpu", tokenizer_dir=None):
1347
+ """Load the per-token UTF-8 byte counts used for BPB evaluation."""
1348
+
1349
+ if tokenizer_dir is None:
1350
+ raise ValueError("tokenizer_dir is required when loading token bytes")
1351
+ path = os.path.join(os.fspath(tokenizer_dir), "token_bytes.pt")
1352
+ if not os.path.isfile(path):
1353
+ raise FileNotFoundError(f"token byte table does not exist: {path}")
1354
+ return torch.load(path, map_location=device, weights_only=False)
1355
+
1356
+
1357
+ @torch.no_grad()
1358
+ def calculate_bpb_score(next_token_indices, per_token_loss, token_bytes):
1359
+ """Return bits-per-byte, total nats, and total token bytes."""
1360
+
1361
+ if (next_token_indices < 0).any():
1362
+ valid = next_token_indices >= 0
1363
+ safe_indices = torch.where(
1364
+ valid, next_token_indices, torch.zeros_like(next_token_indices)
1365
+ )
1366
+ num_bytes = torch.where(
1367
+ valid,
1368
+ token_bytes[safe_indices],
1369
+ torch.zeros_like(next_token_indices, dtype=token_bytes.dtype),
1370
+ )
1371
+ else:
1372
+ num_bytes = token_bytes[next_token_indices]
1373
+
1374
+ total_nats = (per_token_loss * (num_bytes > 0)).sum()
1375
+ total_bytes = num_bytes.sum().to(torch.int64)
1376
+ if torch.distributed.is_initialized():
1377
+ torch.distributed.all_reduce(total_nats, op=torch.distributed.ReduceOp.SUM)
1378
+ torch.distributed.all_reduce(total_bytes, op=torch.distributed.ReduceOp.SUM)
1379
+ total_nats_value = total_nats.item()
1380
+ total_bytes_value = total_bytes.item()
1381
+ bpb = (
1382
+ total_nats_value / (math.log(2) * total_bytes_value)
1383
+ if total_bytes_value > 0
1384
+ else float("inf")
1385
+ )
1386
+ return bpb, total_nats_value, total_bytes_value
1387
+
1388
+
1389
+ def build_tf_head(hparams) -> nn.Module:
1390
+ """Build the paper's direct-unembedding head and reject old variants."""
1391
+
1392
+ head_type = getattr(hparams, "tf_head_type", "direct_unembed")
1393
+ if head_type != "direct_unembed":
1394
+ raise ValueError(
1395
+ f"EBFM only supports tf_head_type='direct_unembed'; received {head_type!r}."
1396
+ )
1397
+ return TFDirectUnembedHead(
1398
+ dim=int(hparams.embedding_dim),
1399
+ vocab_size=int(hparams.vocab_size),
1400
+ )
1401
+
1402
+
1403
+ class EBFMInferenceWrapper:
1404
+ """Small inference adapter shared by BPB, CORE and generation code."""
1405
+
1406
+ def __init__(self, model, tokenizer, device, max_seq_len: int = 256):
1407
+ import torch
1408
+
1409
+ self.model = model
1410
+ self.tokenizer = tokenizer
1411
+ self.device = device
1412
+ self.max_seq_len = max_seq_len
1413
+ self.model_dtype = model.embeddings.weight.dtype
1414
+ self.use_autocast = device.type == "cuda" and self.model_dtype in (
1415
+ torch.float16,
1416
+ torch.bfloat16,
1417
+ )
1418
+ self._precision_state_logged = False
1419
+
1420
+ def __call__(self, input_ids, targets=None, loss_reduction="mean"):
1421
+ import torch
1422
+ import torch.nn.functional as F
1423
+
1424
+ autocast = (
1425
+ torch.amp.autocast(
1426
+ device_type=self.device.type,
1427
+ dtype=self.model_dtype,
1428
+ enabled=self.use_autocast,
1429
+ )
1430
+ if self.device.type in {"cuda", "cpu"}
1431
+ else nullcontext()
1432
+ )
1433
+ with torch.no_grad(), autocast:
1434
+ if not self._precision_state_logged:
1435
+ print(
1436
+ "[checkpoint] "
1437
+ f"model_dtype={self.model_dtype}, autocast={self.use_autocast}"
1438
+ )
1439
+ self._precision_state_logged = True
1440
+
1441
+ if getattr(self.model, "use_tf_head", False):
1442
+ _, _, hidden_states = self.model.forward(
1443
+ input_ids,
1444
+ start_pos=0,
1445
+ learning=False,
1446
+ return_raw_logits=True,
1447
+ return_pred_hiddens=True,
1448
+ )
1449
+ prev_embed = self.model.embeddings(input_ids)
1450
+ distributions = [
1451
+ None if hidden is None else self.model.tf_head(hidden, prev_embed)
1452
+ for hidden in hidden_states
1453
+ ]
1454
+ else:
1455
+ distributions, _ = self.model.forward(
1456
+ input_ids,
1457
+ start_pos=0,
1458
+ learning=False,
1459
+ return_raw_logits=True,
1460
+ )
1461
+
1462
+ if not distributions or distributions[-1] is None:
1463
+ raise RuntimeError(
1464
+ "EBFM returned no final logits; check TF-head/free-embedding settings."
1465
+ )
1466
+ logits = distributions[-1]
1467
+ if targets is None:
1468
+ return logits
1469
+ return F.cross_entropy(
1470
+ logits.reshape(-1, logits.size(-1)),
1471
+ targets.reshape(-1),
1472
+ ignore_index=-1,
1473
+ reduction=loss_reduction,
1474
+ )
1475
+
1476
+ def get_device(self):
1477
+ return self.device
1478
+
1479
+
1480
+ def resolve_tokenizer_dir(checkpoint_path: str, tokenizer_path: str | None = None):
1481
+ """Resolve an explicit or checkpoint-adjacent tokenizer directory."""
1482
+
1483
+ if tokenizer_path:
1484
+ tokenizer_dir = Path(tokenizer_path).expanduser().resolve()
1485
+ if not tokenizer_dir.is_dir():
1486
+ raise FileNotFoundError(
1487
+ f"tokenizer directory does not exist: {tokenizer_dir}"
1488
+ )
1489
+ return tokenizer_dir
1490
+
1491
+ sibling_dir = Path(checkpoint_path).expanduser().resolve().parent / "tokenizer"
1492
+ if sibling_dir.is_dir():
1493
+ return sibling_dir
1494
+ return None
1495
+
1496
+
1497
+ def _resolve_device(torch, device: str | Any):
1498
+ if isinstance(device, str):
1499
+ if device == "auto":
1500
+ device = "cuda" if torch.cuda.is_available() else "cpu"
1501
+ return torch.device(device)
1502
+ return device
1503
+
1504
+
1505
+ def _resolve_dtype(torch, dtype: str | Any, device) -> Any:
1506
+ if not isinstance(dtype, str):
1507
+ return dtype
1508
+ if dtype == "float32":
1509
+ return torch.float32
1510
+ if dtype == "bfloat16":
1511
+ return torch.bfloat16
1512
+ return torch.bfloat16 if device.type == "cuda" else torch.float32
1513
+
1514
+
1515
+ def _is_huggingface_source(source: str | os.PathLike[str]) -> bool:
1516
+ """Return whether a source points to a Transformers model or Hub ID."""
1517
+
1518
+ source_path = Path(os.fspath(source)).expanduser()
1519
+ if source_path.is_dir():
1520
+ return (source_path / "config.json").is_file()
1521
+ if source_path.exists():
1522
+ return False
1523
+ return not str(source).lower().endswith((".ckpt", ".pt", ".pth"))
1524
+
1525
+
1526
+ def _resolve_hf_tokenizer_source(source, tokenizer_path):
1527
+ """Pick where the tokenizer comes from for a Hugging Face checkpoint source.
1528
+
1529
+ A local export may carry only the model (``config.json`` +
1530
+ ``model.safetensors``); when it has no ``tokenizer.pkl`` next to it, reuse
1531
+ the shared tokenizer directory the training pipeline uses.
1532
+ """
1533
+
1534
+ if tokenizer_path:
1535
+ return tokenizer_path
1536
+ source_str = os.fspath(source)
1537
+ if os.path.isdir(source_str):
1538
+ if os.path.exists(os.path.join(source_str, "tokenizer.pkl")):
1539
+ return source
1540
+ from ebfm.common import get_base_dir
1541
+
1542
+ return os.path.join(get_base_dir(), "tokenizer")
1543
+ return source
1544
+
1545
+
1546
+ def _load_huggingface_checkpoint(
1547
+ source: str | os.PathLike[str],
1548
+ *,
1549
+ device,
1550
+ dtype,
1551
+ tokenizer_path: str | os.PathLike[str] | None = None,
1552
+ ):
1553
+ """Load a Transformers model and expose the legacy inference interface."""
1554
+
1555
+ hf_model_class = globals().get("EBFMForMaskedLM")
1556
+ hf_tokenizer_class = globals().get("EBFMTokenizer")
1557
+ if hf_model_class is None or hf_tokenizer_class is None:
1558
+ raise ImportError(
1559
+ "Loading a Hugging Face EBFM model requires the optional dependencies. "
1560
+ "Install them with `uv sync --extra hf --extra gpu` or `--extra cpu`."
1561
+ )
1562
+
1563
+ model = hf_model_class.from_pretrained(os.fspath(source))
1564
+ tokenizer_source = _resolve_hf_tokenizer_source(source, tokenizer_path)
1565
+ tokenizer = hf_tokenizer_class.from_pretrained(os.fspath(tokenizer_source))
1566
+ legacy_tokenizer = tokenizer._encoding
1567
+
1568
+ model = model.to(device=device, dtype=dtype)
1569
+ model.eval()
1570
+ model.ebfm.eval()
1571
+
1572
+ hparams = model.config.to_hparams()
1573
+ hparams["tokenizer_dir"] = os.fspath(tokenizer_source)
1574
+ wrapper = EBFMInferenceWrapper(
1575
+ model.ebfm,
1576
+ EBFMTokenizerWrapper(tokenizer_obj=legacy_tokenizer),
1577
+ device,
1578
+ max_seq_len=int(hparams.get("context_length", 256)),
1579
+ )
1580
+ print(
1581
+ f"[checkpoint] loaded Hugging Face model={hparams.get('model_name', 'ebfm')} "
1582
+ f"size={hparams.get('model_size', 'unknown')} "
1583
+ f"context={hparams.get('context_length', 256)}"
1584
+ )
1585
+ return wrapper, legacy_tokenizer, hparams, device
1586
+
1587
+
1588
+ def _normalize_checkpoint_hparams(hparams, tokenizer):
1589
+ hparams = dict(hparams)
1590
+ if hparams.get("time_embedding") is None:
1591
+ hparams["time_embedding"] = True
1592
+ if not hparams.get("ebfm_norm"):
1593
+ hparams["ebfm_norm"] = "rms"
1594
+ if not hparams.get("ebfm_act_func"):
1595
+ hparams["ebfm_act_func"] = "silu"
1596
+ if not hparams.get("vocab_size"):
1597
+ hparams["vocab_size"] = tokenizer.get_vocab_size()
1598
+ hparams.setdefault("float_precision", "32-true")
1599
+ hparams.setdefault("weight_initialization_method", "xavier")
1600
+ hparams.setdefault("weight_initialization_gain", 1.0)
1601
+ hparams.setdefault("gradient_checkpointing", False)
1602
+ hparams.setdefault("use_sdpa_attention", False)
1603
+ return hparams
1604
+
1605
+
1606
+ def _normalize_legacy_state_dict(state_dict):
1607
+ """Convert Lightning state-dict keys to canonical EBFM module keys."""
1608
+
1609
+ normalized = {}
1610
+ for key, value in state_dict.items():
1611
+ if key.startswith("model."):
1612
+ key = key[len("model.") :]
1613
+ key = key.replace("._orig_mod.", ".")
1614
+ if key.startswith("_orig_mod."):
1615
+ key = key[len("_orig_mod.") :]
1616
+ normalized[key] = value
1617
+
1618
+ eager_prefix = "transformer_eager."
1619
+ transformer_prefix = "transformer."
1620
+ eager_keys = [key for key in normalized if key.startswith(eager_prefix)]
1621
+ if eager_keys:
1622
+ missing = [
1623
+ key
1624
+ for key in eager_keys
1625
+ if transformer_prefix + key[len(eager_prefix) :] not in normalized
1626
+ ]
1627
+ if missing:
1628
+ raise RuntimeError(
1629
+ "Cannot discard transformer_eager keys without canonical counterparts: "
1630
+ + ", ".join(missing[:5])
1631
+ )
1632
+ normalized = {
1633
+ key: value
1634
+ for key, value in normalized.items()
1635
+ if not key.startswith(eager_prefix)
1636
+ }
1637
+ return normalized
1638
+
1639
+
1640
+ def load_checkpoint(
1641
+ checkpoint_path: str | os.PathLike[str],
1642
+ *,
1643
+ device: str | Any = "auto",
1644
+ dtype: str | Any = "auto",
1645
+ tokenizer_path: str | None = None,
1646
+ ):
1647
+ """Load a Lightning checkpoint or Transformers model.
1648
+
1649
+ The return value is ``(model, tokenizer, hparams, device)``. ``model`` is
1650
+ always an :class:`EBFMInferenceWrapper`, so chat and evaluation code can use
1651
+ the same interface for all supported model sources. ``checkpoint_path``
1652
+ may be a Lightning ``.ckpt`` file, a local Transformers directory, or a
1653
+ Hugging Face Hub model ID.
1654
+
1655
+ For a Lightning checkpoint, ``tokenizer_path`` points to the directory
1656
+ containing ``tokenizer.pkl`` and ``token_bytes.pt``. If omitted, a
1657
+ ``tokenizer`` directory next to the checkpoint is used. For a Transformers
1658
+ source, the tokenizer is loaded from the model source unless an explicit
1659
+ ``tokenizer_path`` is provided.
1660
+ """
1661
+
1662
+ import torch
1663
+
1664
+ resolved_device = _resolve_device(torch, device)
1665
+ resolved_dtype = _resolve_dtype(torch, dtype, resolved_device)
1666
+ if _is_huggingface_source(checkpoint_path):
1667
+ return _load_huggingface_checkpoint(
1668
+ checkpoint_path,
1669
+ device=resolved_device,
1670
+ dtype=resolved_dtype,
1671
+ tokenizer_path=tokenizer_path,
1672
+ )
1673
+
1674
+ print(
1675
+ f"[checkpoint] loading {checkpoint_path} on {resolved_device} ({resolved_dtype})"
1676
+ )
1677
+ checkpoint = load_checkpoint_file(checkpoint_path, map_location="cpu")
1678
+ hparams = dict(checkpoint.get("hyper_parameters", {}))
1679
+ tokenizer_dir = resolve_tokenizer_dir(checkpoint_path, tokenizer_path)
1680
+ if tokenizer_dir is not None:
1681
+ hparams["tokenizer_dir"] = str(tokenizer_dir)
1682
+ else:
1683
+ configured_tokenizer_dir = hparams.get("tokenizer_dir")
1684
+ if configured_tokenizer_dir:
1685
+ configured_tokenizer_dir = Path(configured_tokenizer_dir).expanduser()
1686
+ if configured_tokenizer_dir.is_dir():
1687
+ tokenizer_dir = configured_tokenizer_dir
1688
+ if tokenizer_dir is None:
1689
+ raise FileNotFoundError(
1690
+ "No tokenizer assets found. Pass tokenizer_path or place a "
1691
+ "tokenizer directory next to the checkpoint."
1692
+ )
1693
+ hparams["no_wandb"] = True
1694
+ hparams["compile_model"] = False
1695
+
1696
+ tokenizer = get_tokenizer(tokenizer_dir=tokenizer_dir)
1697
+ hparams = _normalize_checkpoint_hparams(hparams, tokenizer)
1698
+ hparams["tokenizer_obj"] = EBFMTokenizerWrapper(tokenizer_obj=tokenizer)
1699
+ if resolved_dtype == torch.bfloat16:
1700
+ hparams["float_precision"] = "bf16-true"
1701
+ elif resolved_dtype == torch.float16:
1702
+ hparams["float_precision"] = "16-mixed"
1703
+ else:
1704
+ hparams["float_precision"] = "32-true"
1705
+ model = EBFM_NLP(hparams)
1706
+ state_dict = checkpoint.get("state_dict", checkpoint)
1707
+ model_state_dict = _normalize_legacy_state_dict(state_dict)
1708
+
1709
+ model.load_state_dict(model_state_dict, strict=True)
1710
+ model.eval()
1711
+ model = model.to(device=resolved_device, dtype=resolved_dtype)
1712
+ model.eval()
1713
+ wrapped = EBFMInferenceWrapper(
1714
+ model,
1715
+ EBFMTokenizerWrapper(tokenizer_obj=tokenizer),
1716
+ resolved_device,
1717
+ max_seq_len=int(hparams.get("context_length", 256)),
1718
+ )
1719
+ print(
1720
+ f"[checkpoint] loaded model={hparams.get('model_name', 'ebfm')} "
1721
+ f"size={hparams.get('model_size', 'unknown')} "
1722
+ f"context={hparams.get('context_length', 256)}"
1723
+ )
1724
+ return wrapped, tokenizer, hparams, resolved_device
1725
+
1726
+
1727
+ class _VocabularyOnlyTokenizer:
1728
+ """Tokenizer substitute used when the HF model receives integer IDs."""
1729
+
1730
+ def __init__(self, vocab_size: int):
1731
+ self.vocab_size = int(vocab_size)
1732
+
1733
+ def get_vocab_size(self):
1734
+ return self.vocab_size
1735
+
1736
+
1737
+ if EBFMConfig is not None and PreTrainedModel is not None and PreTrainedTokenizer is not None:
1738
+
1739
+ class EBFMPreTrainedModel(PreTrainedModel):
1740
+ """Shared Transformers base class for EBFM models."""
1741
+
1742
+ config_class = EBFMConfig
1743
+ base_model_prefix = "ebfm"
1744
+ main_input_name = "input_ids"
1745
+
1746
+ def _init_weights(self, module):
1747
+ del module
1748
+
1749
+
1750
+ class EBFMForMaskedLM(EBFMPreTrainedModel):
1751
+ """Hugging Face wrapper exposing EBFM token logits."""
1752
+
1753
+ def __init__(self, config):
1754
+ super().__init__(config)
1755
+ hparams = config.to_hparams()
1756
+ hparams["tokenizer_obj"] = _VocabularyOnlyTokenizer(config.vocab_size)
1757
+ self.ebfm = EBFM_NLP(hparams)
1758
+ self.post_init()
1759
+
1760
+ def get_input_embeddings(self):
1761
+ return self.ebfm.embeddings
1762
+
1763
+ def set_input_embeddings(self, value):
1764
+ self.ebfm.embeddings = value
1765
+
1766
+ def get_output_embeddings(self):
1767
+ return self.ebfm.tf_head.proj
1768
+
1769
+ def set_output_embeddings(self, value):
1770
+ self.ebfm.tf_head.proj = value
1771
+
1772
+ def forward(
1773
+ self,
1774
+ input_ids=None,
1775
+ attention_mask=None,
1776
+ labels=None,
1777
+ output_hidden_states=None,
1778
+ output_attentions=None,
1779
+ return_dict=None,
1780
+ **kwargs,
1781
+ ):
1782
+ del attention_mask, output_attentions, kwargs
1783
+ if input_ids is None:
1784
+ raise ValueError("input_ids must be provided")
1785
+ if input_ids.ndim != 2:
1786
+ raise ValueError(
1787
+ f"input_ids must have shape [batch, sequence], got {input_ids.shape}"
1788
+ )
1789
+ if return_dict is None:
1790
+ return_dict = self.config.use_return_dict
1791
+
1792
+ parameter = next(self.parameters())
1793
+ use_autocast = parameter.device.type == "cuda" and parameter.dtype in {
1794
+ torch.float16,
1795
+ torch.bfloat16,
1796
+ }
1797
+ autocast_context = (
1798
+ torch.autocast(
1799
+ device_type=parameter.device.type,
1800
+ dtype=parameter.dtype,
1801
+ )
1802
+ if use_autocast
1803
+ else nullcontext()
1804
+ )
1805
+ with autocast_context:
1806
+ _, _, hidden_states = self.ebfm(
1807
+ input_ids,
1808
+ learning=self.training,
1809
+ return_pred_hiddens=True,
1810
+ )
1811
+ hidden = hidden_states[-1]
1812
+ if hidden is None:
1813
+ raise RuntimeError("EBFM did not return the post-update hidden state")
1814
+ logits = self.ebfm.tf_head(hidden, self.ebfm.embeddings(input_ids))
1815
+
1816
+ loss = None
1817
+ if labels is not None:
1818
+ loss = F.cross_entropy(
1819
+ logits.reshape(-1, logits.size(-1)),
1820
+ labels.reshape(-1),
1821
+ ignore_index=-100,
1822
+ )
1823
+
1824
+ if not return_dict:
1825
+ output = (logits,)
1826
+ if output_hidden_states:
1827
+ output = output + ((hidden,),)
1828
+ return ((loss,) + output) if loss is not None else output
1829
+
1830
+ return MaskedLMOutput(
1831
+ loss=loss,
1832
+ logits=logits,
1833
+ hidden_states=(hidden,) if output_hidden_states else None,
1834
+ attentions=None,
1835
+ )
1836
+
1837
+ @classmethod
1838
+ def from_legacy_checkpoint(cls, checkpoint_path, tokenizer_path=None):
1839
+ """Load a Lightning checkpoint into the Transformers wrapper."""
1840
+
1841
+ checkpoint = load_checkpoint_file(checkpoint_path, map_location="cpu")
1842
+ hparams = dict(checkpoint.get("hyper_parameters", {}))
1843
+ tokenizer_dir = resolve_tokenizer_dir(checkpoint_path, tokenizer_path)
1844
+ if tokenizer_dir is None:
1845
+ raise FileNotFoundError(
1846
+ "No tokenizer directory found next to the checkpoint; "
1847
+ "pass tokenizer_path explicitly."
1848
+ )
1849
+ tokenizer = get_tokenizer(tokenizer_dir)
1850
+ hparams = _normalize_checkpoint_hparams(hparams, tokenizer)
1851
+ config = EBFMConfig.from_legacy_hparams(
1852
+ hparams, vocab_size=tokenizer.get_vocab_size()
1853
+ )
1854
+ model = cls(config)
1855
+ canonical_state = _normalize_legacy_state_dict(
1856
+ checkpoint.get("state_dict", checkpoint)
1857
+ )
1858
+ hf_state = {f"ebfm.{key}": value for key, value in canonical_state.items()}
1859
+ incompatible = model.load_state_dict(hf_state, strict=False)
1860
+ if incompatible.missing_keys or incompatible.unexpected_keys:
1861
+ raise RuntimeError(
1862
+ "Legacy checkpoint does not match the EBFM Transformers wrapper: "
1863
+ f"missing={incompatible.missing_keys[:5]}, "
1864
+ f"unexpected={incompatible.unexpected_keys[:5]}"
1865
+ )
1866
+ return model, tokenizer
1867
+
1868
+
1869
+ class EBFMTokenizer(PreTrainedTokenizer):
1870
+ """Transformers tokenizer backed by EBFM's serialized RustBPE data."""
1871
+
1872
+ model_input_names = ["input_ids", "attention_mask"]
1873
+ vocab_files_names = {"tokenizer_file": "tokenizer.pkl"}
1874
+
1875
+ @classmethod
1876
+ def from_pretrained(cls, pretrained_model_name_or_path, *init_inputs, **kwargs):
1877
+ if "tokenizer_file" not in kwargs:
1878
+ model_path = Path(str(pretrained_model_name_or_path)).expanduser()
1879
+ if model_path.is_dir():
1880
+ kwargs["tokenizer_file"] = str(model_path / "tokenizer.pkl")
1881
+ else:
1882
+ # Repo id (or Hub reference): fetch tokenizer.pkl so the pkl is
1883
+ # resolved from the Hub instead of the current directory.
1884
+ from huggingface_hub import hf_hub_download
1885
+
1886
+ hub_kwargs = {
1887
+ key: kwargs[key]
1888
+ for key in (
1889
+ "cache_dir",
1890
+ "revision",
1891
+ "subfolder",
1892
+ "token",
1893
+ "local_files_only",
1894
+ )
1895
+ if key in kwargs
1896
+ }
1897
+ kwargs["tokenizer_file"] = hf_hub_download(
1898
+ repo_id=str(pretrained_model_name_or_path),
1899
+ filename="tokenizer.pkl",
1900
+ **hub_kwargs,
1901
+ )
1902
+ return super().from_pretrained(
1903
+ pretrained_model_name_or_path, *init_inputs, **kwargs
1904
+ )
1905
+
1906
+ def __init__(self, tokenizer_file=None, **kwargs):
1907
+ if tokenizer_file is None:
1908
+ tokenizer_file = "tokenizer.pkl"
1909
+ # absolute() keeps a Hub cache symlink (snapshots/<rev>/tokenizer.pkl)
1910
+ # intact; resolve() would dereference it to blobs/<hash>, hiding the
1911
+ # directory that StandaloneTokenizer.from_directory needs.
1912
+ tokenizer_file = Path(tokenizer_file).expanduser().absolute()
1913
+ self._tokenizer_file = tokenizer_file
1914
+ self._encoding = StandaloneTokenizer.from_directory(tokenizer_file.parent)
1915
+ self._special_ids = {
1916
+ "<|bos|>": self._encoding.bos_token_id,
1917
+ "<|eos|>": self._encoding.eos_token_id,
1918
+ "<|pad|>": self._encoding.pad_token_id,
1919
+ "<|unk|>": 0,
1920
+ }
1921
+ special_tokens = {
1922
+ "bos_token": kwargs.pop("bos_token", "<|bos|>"),
1923
+ "eos_token": kwargs.pop("eos_token", "<|eos|>"),
1924
+ "pad_token": kwargs.pop("pad_token", "<|pad|>"),
1925
+ "unk_token": kwargs.pop("unk_token", "<|unk|>"),
1926
+ }
1927
+ super().__init__(
1928
+ **special_tokens,
1929
+ **kwargs,
1930
+ )
1931
+
1932
+ @property
1933
+ def vocab_size(self):
1934
+ return self._encoding.get_vocab_size()
1935
+
1936
+ def get_vocab(self):
1937
+ vocabulary = {str(index): index for index in range(self.vocab_size)}
1938
+ vocabulary.update(self._special_ids)
1939
+ return vocabulary
1940
+
1941
+ def _tokenize(self, text, **kwargs):
1942
+ del kwargs
1943
+ return [str(index) for index in self._encoding.encode(text)]
1944
+
1945
+ def _convert_token_to_id(self, token):
1946
+ if token in self._special_ids:
1947
+ return self._special_ids[token]
1948
+ return int(token)
1949
+
1950
+ def _convert_id_to_token(self, index):
1951
+ return str(index)
1952
+
1953
+ def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):
1954
+ del token_ids_1
1955
+ return [self.bos_token_id] + list(token_ids_0)
1956
+
1957
+ def _decode(
1958
+ self,
1959
+ token_ids,
1960
+ skip_special_tokens=False,
1961
+ clean_up_tokenization_spaces=True,
1962
+ **kwargs,
1963
+ ):
1964
+ del clean_up_tokenization_spaces, kwargs
1965
+ token_ids = [int(token_id) for token_id in token_ids]
1966
+ if skip_special_tokens:
1967
+ token_ids = [
1968
+ token_id
1969
+ for token_id in token_ids
1970
+ if token_id not in self._special_ids.values()
1971
+ ]
1972
+ return self._encoding.decode(token_ids)
1973
+
1974
+ def save_vocabulary(self, save_directory, filename_prefix=None):
1975
+ del filename_prefix
1976
+ save_directory = Path(save_directory)
1977
+ save_directory.mkdir(parents=True, exist_ok=True)
1978
+ target = save_directory / "tokenizer.pkl"
1979
+ shutil.copyfile(self._tokenizer_file, target)
1980
+ return (str(target),)
1981
+
1982
+ else:
1983
+ EBFMConfig = None
1984
+ EBFMPreTrainedModel = None
1985
+ EBFMForMaskedLM = None
1986
+ EBFMTokenizer = None
1987
+
1988
+
1989
+ __all__ = [
1990
+ "EBFMConfig",
1991
+ "EBFMPreTrainedModel",
1992
+ "EBFMForMaskedLM",
1993
+ "EBFMTokenizer",
1994
+ "EBFMModelArgs",
1995
+ "EBFMInferenceWrapper",
1996
+ "load_checkpoint",
1997
+ "resolve_tokenizer_dir",
1998
+ "load_checkpoint_file",
1999
+ "get_tokenizer",
2000
+ "get_token_bytes",
2001
+ "calculate_bpb_score",
2002
+ "StandaloneTokenizer",
2003
+ "EBFMTokenizerWrapper",
2004
+ "EBFM_NLP",
2005
+ "EBFMTimeConcat",
2006
+ "PRECISION_TO_MCMC_DTYPE",
2007
+ "Attention",
2008
+ "FeedForward",
2009
+ "TransformerBlock",
2010
+ "RMSNorm",
2011
+ "precompute_freqs_cis",
2012
+ "reshape_for_broadcast",
2013
+ "apply_rotary_emb",
2014
+ "repeat_kv",
2015
+ "build_tf_head",
2016
+ "TFDirectUnembedHead",
2017
+ "init_wandb_watch",
2018
+ "init_whole_model_weights",
2019
+ "setup_ebfm",
2020
+ ]
token_bytes.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:72e6403e2fd4ffb9aab2e359c58aea91c6d541521cfb32663b8aae21b598fe0c
3
+ size 132649
tokenizer.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0998abb3c2bad97dce7c461a2dd78017a032809ea216efa297d9261ae5361fbe
3
+ size 412778
tokenizer_config.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "<|unk|>",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "32759": {
12
+ "content": "<|pad|>",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ }
19
+ },
20
+ "auto_map": {
21
+ "AutoTokenizer": [
22
+ "modeling_ebfm.EBFMTokenizer",
23
+ null
24
+ ]
25
+ },
26
+ "backend": "custom",
27
+ "bos_token": "<|bos|>",
28
+ "eos_token": "<|eos|>",
29
+ "model_max_length": 1000000000000000019884624838656,
30
+ "pad_token": "<|pad|>",
31
+ "tokenizer_class": "EBFMTokenizer",
32
+ "unk_token": "<|unk|>"
33
+ }