Bc-AI commited on
Commit
9df61c9
·
verified ·
1 Parent(s): e7702dd

Add Transformers support (modeling + config code), tokenizer/generation configs and model card

Browse files
README.md ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ pipeline_tag: text-generation
4
+ language:
5
+ - en
6
+ tags:
7
+ - comet
8
+ - openmodel-1
9
+ - causal-lm
10
+ - base-model
11
+ - hybrid-architecture
12
+ - custom_code
13
+ datasets:
14
+ - HuggingFaceFW/fineweb-edu
15
+ - HuggingFaceFW/fineweb
16
+ - HuggingFaceTB/finemath
17
+ - Cyrile/dataset-the-stack-v2-dedup-sub
18
+ ---
19
+
20
+ # Comet-1-Preview
21
+
22
+ Comet-1-Preview is a 7.0B-parameter causal language model from OpenModel-Labs, trained from scratch with the single-file pipeline in [`train.py`](./train.py) (internal name: OpenModel-1). Three out of every four blocks are grouped-query attention; the fourth replaces attention with a gated, dilated causal convolution.
23
+
24
+ **This checkpoint is a base model.** It continues text; it does not follow chat turns. Prompt it with the beginning of the document you want, or with a few-shot pattern. See [Prompting](#prompting).
25
+
26
+ It is a preview release: no benchmark results have been published for it, and no license has been declared for the weights yet.
27
+
28
+ | | |
29
+ |---|---|
30
+ | Parameters | 7,001,436,928 |
31
+ | Weights | `model.safetensors`, float32, 28 GB |
32
+ | Context | trained on 4,096-token sequences; position table covers 8,192 |
33
+ | Tokenizer | GPT-NeoX-20B BPE, 50,277 tokens |
34
+ | Language | English web text, with maths and source code |
35
+
36
+ ## Quick start
37
+
38
+ The architecture is not built into Transformers, so loading needs `trust_remote_code=True`. That runs [`modeling_comet.py`](./modeling_comet.py) and [`configuration_comet.py`](./configuration_comet.py) from this repository; they are short and worth reading first.
39
+
40
+ ```python
41
+ import torch
42
+ from transformers import AutoModelForCausalLM, AutoTokenizer
43
+
44
+ repo = "OpenModel-Labs/Comet-1-Preview"
45
+ tokenizer = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
46
+ model = AutoModelForCausalLM.from_pretrained(
47
+ repo,
48
+ trust_remote_code=True,
49
+ dtype=torch.bfloat16, # on older 4.x releases that reject `dtype`, use torch_dtype=
50
+ device_map="auto", # needs `accelerate`
51
+ )
52
+
53
+ inputs = tokenizer("Photosynthesis is the process by which", return_tensors="pt").to(model.device)
54
+ output = model.generate(
55
+ **inputs,
56
+ max_new_tokens=128,
57
+ do_sample=True,
58
+ temperature=0.7,
59
+ top_p=0.9,
60
+ repetition_penalty=1.1,
61
+ )
62
+ print(tokenizer.decode(output[0], skip_special_tokens=True))
63
+ ```
64
+
65
+ The sampling values above are ordinary starting points, not tuned settings.
66
+
67
+ Memory: the weights take about 14 GB in bfloat16 and 28 GB in float32. Training computed in bfloat16, so bfloat16 inference matches the precision the weights were trained under. The generation cache adds roughly 0.6 GB per sequence at 4,096 tokens in bfloat16.
68
+
69
+ Requirements: a recent PyTorch 2.x and `transformers` 4.46 or newer.
70
+
71
+ ## Prompting
72
+
73
+ The model was trained on plain documents separated by `<|endoftext|>`. Write the start of a document and let it continue.
74
+
75
+ These are greedy continuations (`do_sample=False`, 20 new tokens) from the released weights. `\n` marks a line break.
76
+
77
+ | Prompt | Continuation |
78
+ |---|---|
79
+ | `The capital of Australia is` | ` Canberra.\n\nThe population of Australia is about 23 million.\n\nThe currency of` |
80
+ | `Photosynthesis is the process by which` | ` green plants convert carbon dioxide and water into glucose and oxygen. This process is essential for the survival of` |
81
+ | `def is_prime(n):\n """Return True if n is a prime number."""\n` | ` if n < 2:\n return False\n for i in range(2, int` |
82
+ | `Question: What is 12 + 30?\nAnswer:` | ` 12 + 30 = 42\n\nQuestion: What is 12 + 30 + 42?\nAnswer:` |
83
+
84
+ The last row shows the usual base-model behaviour: after answering, it writes the next question itself. Stop on a string of your choosing (for example `"\nQuestion:"`) or cap `max_new_tokens`.
85
+
86
+ Things to know:
87
+
88
+ - The tokenizer adds no special tokens. `<|endoftext|>` (id 0) is the document separator and the end-of-sequence token for generation. `<|padding|>` (id 1) is the pad token, and batches are padded on the left.
89
+ - No chat template is shipped. `train.py` defines later fine-tuning stages that use a `<|user|>` / `<|assistant|>` turn format, but this checkpoint does not respond to it. Given `<|user|>\nWhat is the capital of France?\n<|endoftext|><|assistant|>\n`, its greedy continuation was `\n# How to Calculate the Area of a Circle\n\nThe area of a circle is the amount`, which is the start of an unrelated document.
90
+ - For question answering or instructions, use a few-shot pattern such as the `Question:` / `Answer:` one above.
91
+
92
+ ## Architecture
93
+
94
+ ```
95
+ tokens -> embedding (50,277 x 4,096)
96
+ -> 36 blocks, repeating [global, global, global, recall]
97
+ -> RMSNorm -> logits through the same embedding matrix (tied)
98
+
99
+ block: x = x + mixer_scale * mixer(RMSNorm(x))
100
+ x = x + mlp_scale * SwiGLU(RMSNorm(x))
101
+ ```
102
+
103
+ | Component | Detail |
104
+ |---|---|
105
+ | Blocks | 36 in total: 27 `global`, 9 `recall` (blocks 3, 7, 11, ..., 35) |
106
+ | Width | 4,096 |
107
+ | `global` mixer | Causal grouped-query attention: 32 query heads, 8 key/value heads, head size 128. Queries and keys pass through a per-head RMSNorm, then RoPE (base 1,000,000). |
108
+ | `recall` mixer | Project 4,096 to a gate and a value of 2,048 channels each; run a depthwise causal convolution over the value (kernel 7, dilation 8); multiply by the sigmoid of the gate; project back to 4,096. |
109
+ | Feed-forward | SwiGLU, inner width 12,288, in every block |
110
+ | Residual scaling | A learned per-channel scale on each branch, initialised to 0.5 |
111
+ | Normalisation | Pre-norm RMSNorm, epsilon 1e-6 |
112
+ | Biases | None |
113
+
114
+ A `recall` block sees seven positions: the current token and the tokens 8, 16, 24, 32, 40 and 48 places back. The config lists `memory_dilations: [1, 2, 4, 8]`, and a block picks its dilation by its index modulo four. Because `recall` blocks always sit at an index that is 3 modulo 4, all nine use dilation 8 in this checkpoint.
115
+
116
+ A `global` block has about 193M parameters and a `recall` block about 176M; the embedding matrix has 206M.
117
+
118
+ ## Training
119
+
120
+ This section describes the settings in `train.py`. It is not a log of the run: the upload does not include the run metadata, so the number of tokens this checkpoint actually saw is not recorded here.
121
+
122
+ **Data.** Pretraining streams FineWeb-Edu and FineWeb, then gradually mixes in maths and code. The mixture moves linearly from the first column to the second over 12B tokens.
123
+
124
+ | Source | Start | After anneal | License note in `train.py` |
125
+ |---|---|---|---|
126
+ | [HuggingFaceFW/fineweb-edu](https://huggingface.co/datasets/HuggingFaceFW/fineweb-edu) (`sample-100BT`) | 70% | 55% | ODC-By, Common Crawl-derived |
127
+ | [HuggingFaceFW/fineweb](https://huggingface.co/datasets/HuggingFaceFW/fineweb) (`sample-100BT`) | 30% | 20% | ODC-By, Common Crawl-derived |
128
+ | [HuggingFaceTB/finemath](https://huggingface.co/datasets/HuggingFaceTB/finemath) (`finemath-4plus`) | 0% | 15% | ODC-By, Common Crawl-derived |
129
+ | [Cyrile/dataset-the-stack-v2-dedup-sub](https://huggingface.co/datasets/Cyrile/dataset-the-stack-v2-dedup-sub) (Python 6%, JavaScript 2%, Java 1%, C++ 0.75%, C 0.25%) | 0% | 10% | per-file license review required |
130
+
131
+ **Optimisation.**
132
+
133
+ | Setting | Value |
134
+ |---|---|
135
+ | Sequence length | 4,096 tokens; documents are packed end to end with the end-of-text token between them |
136
+ | Hardware | 8 GPUs, DDP |
137
+ | Batch | 1 sequence per GPU, 16 accumulation steps: 524,288 tokens per optimizer step |
138
+ | Optimizer | AdamW, betas (0.9, 0.95), weight decay 0.1 (not applied to norm gains or residual scales) |
139
+ | Learning rate | 3e-4 peak, linear warm-up over 600M tokens, cosine decay to 3e-5 |
140
+ | Gradient clipping | 1.0 |
141
+ | Precision | float32 parameters, bfloat16 autocast |
142
+ | Token budget | 22 tokens per parameter (about 154B tokens), with an 11-day wall-clock cap on the stage |
143
+
144
+ `train.py` also contains supervised fine-tuning and SimPO preference-tuning stages. As shown under [Prompting](#prompting), this checkpoint behaves as the output of the pretraining stage.
145
+
146
+ ## Files
147
+
148
+ | File | Purpose |
149
+ |---|---|
150
+ | `model.safetensors` | Weights, exactly as written by `train.py` (flat keys: `embed.weight`, `blocks.N.*`, `norm.weight`) |
151
+ | `config.json` | Architecture hyperparameters and the `auto_map` that points Transformers at the code below |
152
+ | `configuration_comet.py` | `CometConfig` |
153
+ | `modeling_comet.py` | `CometModel` (hidden states) and `CometForCausalLM` (logits, loss, `generate`) |
154
+ | `generation_config.json` | End-of-sequence and padding ids |
155
+ | `tokenizer.json`, `tokenizer_config.json`, `special_tokens_map.json` | Tokenizer (same `tokenizer.json` content as `EleutherAI/gpt-neox-20b`) |
156
+ | `train.py` | The training pipeline, including the reference `OpenModel1` module |
157
+
158
+ ## Implementation notes
159
+
160
+ `modeling_comet.py` keeps the parameter names and shapes of the training module, so the checkpoint loads with no key remapping. It adds what the training loop did not need:
161
+
162
+ - **Generation cache.** `use_cache=True` returns a standard `DynamicCache`. Attention blocks store keys and values in it. `recall` blocks store their convolution input in the key slot, so beam search, batch expansion and cache cropping work on them unchanged.
163
+ - **Padding.** An `attention_mask` hides padded keys in attention, zeroes padded positions in the convolution, and numbers real tokens from position 0. A left-padded batch therefore gives the same logits as running each row alone.
164
+ - **Loss.** Pass `labels=input_ids`; the shift happens inside the model and `-100` is ignored.
165
+ - **Gradient checkpointing.** `model.gradient_checkpointing_enable()`.
166
+
167
+ Not supported: `output_attentions` (attention runs through `scaled_dot_product_attention`), legacy tuple caches, and inputs longer than 8,192 positions, which raise an error.
168
+
169
+ ## Verification
170
+
171
+ What was checked before these files were published:
172
+
173
+ - **Layout.** A full-size `CometForCausalLM` has the same 353 tensor names and shapes as the header of the released `model.safetensors`.
174
+ - **Equivalence.** With identical weights on small random configurations, logits, hidden states and loss match the `OpenModel1` module from `train.py` exactly in float32 (maximum absolute difference 0).
175
+ - **Cache and padding.** Token-by-token and chunked decoding match a full forward pass to about 1e-6. Greedy `generate` with the cache matches recomputing from scratch, beam search matches with and without the cache, and left-padded batches match unbatched runs.
176
+ - **Released weights.** A float32 CPU forward pass over the full checkpoint gave perplexity 4.5 on a 68-token encyclopedic paragraph and 1.7 on a short Python function, put 98.8% on ` Tokyo` after a list of capitals ending in `The capital of Japan is`, and gave perplexity above 400,000 on random tokens. These are smoke tests on a handful of sentences, not benchmarks.
177
+ - **Versions.** The test suite passes on `transformers` 4.46.3, 4.57.3 and 5.22.2 with PyTorch 2.15.
178
+
179
+ Not covered: the full checkpoint has not been run on a GPU or in bfloat16 through this code, and the continuations in the Prompting table were produced on CPU with weights rounded through float16. Multi-GPU `device_map` placement was only exercised on a small model.
180
+
181
+ ## Limitations
182
+
183
+ - It is a base model with no instruction or safety tuning. It can produce incorrect, biased or offensive text, and it will state false things fluently.
184
+ - Training data is English web text, maths and code. Expect weak results in other languages.
185
+ - Sequences longer than 4,096 tokens were never seen in training. Up to 8,192 the model runs, but quality there has not been measured.
186
+ - No evaluation results are available yet.
187
+
188
+ ## Fine-tuning for chat
189
+
190
+ To continue with the fine-tuning stage that `train.py` defines, format each message as
191
+
192
+ ```
193
+ <|{role}|>\n{content}\n<|endoftext|>
194
+ ```
195
+
196
+ and compute the loss on assistant messages only, including their closing `<|endoftext|>`. The role markers are ordinary text, not special tokens. A generation prompt ends with `<|assistant|>\n`.
config.json CHANGED
@@ -1,4 +1,13 @@
1
  {
 
 
 
 
 
 
 
 
 
2
  "variant": "7.0b",
3
  "vocab_size": 50277,
4
  "hidden_size": 4096,
@@ -14,8 +23,15 @@
14
  "layer_pattern": ["global", "global", "global", "recall"],
15
  "layer_scale_init": 0.5,
16
  "qk_norm": true,
17
- "tie_word_embeddings": true,
18
  "fused_projections": true,
19
  "dropout": 0.0,
20
- "gradient_checkpointing": true
21
- }
 
 
 
 
 
 
 
 
1
  {
2
+ "architectures": [
3
+ "CometForCausalLM"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_comet.CometConfig",
7
+ "AutoModel": "modeling_comet.CometModel",
8
+ "AutoModelForCausalLM": "modeling_comet.CometForCausalLM"
9
+ },
10
+ "model_type": "comet",
11
  "variant": "7.0b",
12
  "vocab_size": 50277,
13
  "hidden_size": 4096,
 
23
  "layer_pattern": ["global", "global", "global", "recall"],
24
  "layer_scale_init": 0.5,
25
  "qk_norm": true,
26
+ "rms_norm_eps": 1e-06,
27
  "fused_projections": true,
28
  "dropout": 0.0,
29
+ "initializer_range": 0.02,
30
+ "tie_word_embeddings": true,
31
+ "use_cache": true,
32
+ "bos_token_id": 0,
33
+ "eos_token_id": 0,
34
+ "pad_token_id": 1,
35
+ "tokenizer_class": "PreTrainedTokenizerFast",
36
+ "dtype": "float32"
37
+ }
configuration_comet.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Configuration for Comet-1 (OpenModel-1), a hybrid attention / causal-convolution language model."""
2
+
3
+ from transformers import PretrainedConfig
4
+
5
+
6
+ class CometConfig(PretrainedConfig):
7
+ r"""
8
+ Configuration class for [`CometModel`] and [`CometForCausalLM`].
9
+
10
+ The field names mirror the ``model_config`` block that the OpenModel-1 training
11
+ pipeline (``train.py``) writes next to every checkpoint, so a training-time
12
+ ``config.json`` loads unchanged.
13
+
14
+ Args:
15
+ vocab_size (`int`, defaults to 50277):
16
+ Size of the token embedding table (``len(tokenizer)`` of the GPT-NeoX-20B BPE tokenizer).
17
+ hidden_size (`int`, defaults to 4096):
18
+ Width of the residual stream.
19
+ num_hidden_layers (`int`, defaults to 36):
20
+ Number of blocks.
21
+ num_attention_heads (`int`, defaults to 32):
22
+ Number of query heads in the "global" (attention) blocks.
23
+ num_key_value_heads (`int`, defaults to 8):
24
+ Number of key/value heads (grouped-query attention).
25
+ intermediate_size (`int`, defaults to 12288):
26
+ Inner width of the SwiGLU feed-forward.
27
+ max_position_embeddings (`int`, defaults to 8192):
28
+ Number of positions in the precomputed RoPE table. Inputs longer than this are rejected.
29
+ rope_theta (`float`, defaults to 1000000.0):
30
+ RoPE base frequency.
31
+ memory_width (`int`, defaults to 2048):
32
+ Channel count of the depthwise convolution lane in "recall" blocks.
33
+ memory_kernel_size (`int`, defaults to 7):
34
+ Kernel size of the depthwise causal convolution.
35
+ memory_dilations (`list[int]`, defaults to `[1, 2, 4, 8]`):
36
+ Dilation table. Block ``i`` uses ``memory_dilations[i % len(memory_dilations)]``.
37
+ layer_pattern (`list[str]`, defaults to `["global", "global", "global", "recall"]`):
38
+ Repeating block schedule. Block ``i`` is of kind ``layer_pattern[i % len(layer_pattern)]``;
39
+ ``"global"`` is grouped-query attention, anything else is the causal recall mixer.
40
+ layer_scale_init (`float`, defaults to 0.5):
41
+ Initial value of the learned per-channel residual scales.
42
+ qk_norm (`bool`, defaults to `True`):
43
+ Apply RMSNorm to queries and keys (per head) before RoPE.
44
+ rms_norm_eps (`float`, defaults to 1e-6):
45
+ Epsilon of every RMSNorm.
46
+ fused_projections (`bool`, defaults to `True`):
47
+ Parameter layout. ``True`` stores Q/K/V as one ``qkv_proj`` matrix and the two SwiGLU input
48
+ projections as one ``gate_up`` matrix (the layout of the released checkpoint).
49
+ dropout (`float`, defaults to 0.0):
50
+ Dropout on both residual branches (training only).
51
+ initializer_range (`float`, defaults to 0.02):
52
+ Standard deviation used when initialising new weights.
53
+ tie_word_embeddings (`bool`, defaults to `True`):
54
+ Share the input embedding matrix with the output projection.
55
+ use_cache (`bool`, defaults to `True`):
56
+ Return a key/value + convolution-state cache from the forward pass.
57
+ variant (`str`, *optional*):
58
+ Free-form size label written by the training pipeline (for example ``"7.0b"``).
59
+ """
60
+
61
+ model_type = "comet"
62
+
63
+ def __init__(
64
+ self,
65
+ vocab_size=50277,
66
+ hidden_size=4096,
67
+ num_hidden_layers=36,
68
+ num_attention_heads=32,
69
+ num_key_value_heads=8,
70
+ intermediate_size=12288,
71
+ max_position_embeddings=8192,
72
+ rope_theta=1_000_000.0,
73
+ memory_width=2048,
74
+ memory_kernel_size=7,
75
+ memory_dilations=(1, 2, 4, 8),
76
+ layer_pattern=("global", "global", "global", "recall"),
77
+ layer_scale_init=0.5,
78
+ qk_norm=True,
79
+ rms_norm_eps=1e-6,
80
+ fused_projections=True,
81
+ dropout=0.0,
82
+ initializer_range=0.02,
83
+ tie_word_embeddings=True,
84
+ use_cache=True,
85
+ variant=None,
86
+ bos_token_id=0,
87
+ eos_token_id=0,
88
+ pad_token_id=1,
89
+ **kwargs,
90
+ ):
91
+ # `gradient_checkpointing` is a training-time switch that the training pipeline
92
+ # stores in its config dump. It is not an architecture property; enable it with
93
+ # `model.gradient_checkpointing_enable()` instead.
94
+ kwargs.pop("gradient_checkpointing", None)
95
+
96
+ self.vocab_size = vocab_size
97
+ self.hidden_size = hidden_size
98
+ self.num_hidden_layers = num_hidden_layers
99
+ self.num_attention_heads = num_attention_heads
100
+ self.num_key_value_heads = num_key_value_heads
101
+ self.intermediate_size = intermediate_size
102
+ self.max_position_embeddings = max_position_embeddings
103
+ self.rope_theta = float(rope_theta)
104
+ self.memory_width = memory_width
105
+ self.memory_kernel_size = memory_kernel_size
106
+ self.memory_dilations = list(memory_dilations)
107
+ self.layer_pattern = list(layer_pattern)
108
+ self.layer_scale_init = layer_scale_init
109
+ self.qk_norm = qk_norm
110
+ self.rms_norm_eps = rms_norm_eps
111
+ self.fused_projections = fused_projections
112
+ self.dropout = dropout
113
+ self.initializer_range = initializer_range
114
+ self.use_cache = use_cache
115
+ self.variant = variant
116
+
117
+ if hidden_size % num_attention_heads != 0:
118
+ raise ValueError("hidden_size must be divisible by num_attention_heads")
119
+ if (hidden_size // num_attention_heads) % 2 != 0:
120
+ raise ValueError("the attention head dimension must be even for RoPE")
121
+ if num_attention_heads % num_key_value_heads != 0:
122
+ raise ValueError("num_attention_heads must be divisible by num_key_value_heads")
123
+ if memory_kernel_size < 1:
124
+ raise ValueError("memory_kernel_size must be positive")
125
+ if len(self.layer_pattern) == 0 or len(self.memory_dilations) == 0:
126
+ raise ValueError("layer_pattern and memory_dilations must not be empty")
127
+
128
+ super().__init__(
129
+ bos_token_id=bos_token_id,
130
+ eos_token_id=eos_token_id,
131
+ pad_token_id=pad_token_id,
132
+ tie_word_embeddings=tie_word_embeddings,
133
+ **kwargs,
134
+ )
135
+
136
+ def block_kind(self, index):
137
+ """Kind of block ``index``: ``"global"`` (attention) or ``"recall"`` (causal convolution)."""
138
+ return self.layer_pattern[index % len(self.layer_pattern)]
139
+
140
+ def block_dilation(self, index):
141
+ """Convolution dilation that block ``index`` uses if it is a recall block."""
142
+ return self.memory_dilations[index % len(self.memory_dilations)]
143
+
144
+
145
+ __all__ = ["CometConfig"]
generation_config.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 0,
3
+ "eos_token_id": 0,
4
+ "pad_token_id": 1
5
+ }
modeling_comet.py ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Comet-1 (OpenModel-1) for Hugging Face Transformers.
3
+
4
+ Inference/fine-tuning port of the ``OpenModel1`` module defined in the training
5
+ pipeline (``train.py``). Parameter names and shapes are identical to the training
6
+ checkpoint, so ``model.safetensors`` loads without any key remapping.
7
+
8
+ Architecture
9
+ ------------
10
+ A pre-norm decoder with tied input/output embeddings. Blocks follow a repeating
11
+ schedule (``config.layer_pattern``, by default ``[global, global, global, recall]``):
12
+
13
+ * ``global`` blocks use causal grouped-query attention with per-head RMSNorm on
14
+ queries and keys (QK-norm) followed by rotary position embeddings.
15
+ * ``recall`` blocks replace attention with a gated, depthwise, dilated *causal*
16
+ 1-D convolution over a narrower lane (``config.memory_width``).
17
+
18
+ Every block then applies a SwiGLU feed-forward. Both residual branches are scaled
19
+ by a learned per-channel vector (LayerScale).
20
+
21
+ Compared with ``train.py`` this file adds what generation needs and the training
22
+ loop did not: an incremental cache (attention keys/values *and* convolution
23
+ history), padding-aware attention masks, and the standard Transformers outputs.
24
+ """
25
+
26
+ import math
27
+ from typing import Optional, Tuple, Union
28
+
29
+ import torch
30
+ import torch.nn.functional as F
31
+ from torch import nn
32
+
33
+ from transformers import DynamicCache, GenerationMixin, PreTrainedModel
34
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
35
+
36
+ from .configuration_comet import CometConfig
37
+
38
+
39
+ def _cache_length(cache) -> int:
40
+ """Number of tokens already stored in ``cache`` (0 for ``None`` or an empty cache)."""
41
+ if cache is None:
42
+ return 0
43
+ try:
44
+ return int(cache.get_seq_length())
45
+ except Exception:
46
+ return 0
47
+
48
+
49
+ class CometRMSNorm(nn.Module):
50
+ def __init__(self, size: int, eps: float = 1e-6):
51
+ super().__init__()
52
+ self.weight = nn.Parameter(torch.ones(size))
53
+ self.eps = eps
54
+
55
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
56
+ weight = self.weight.to(x.dtype)
57
+ if hasattr(F, "rms_norm"):
58
+ return F.rms_norm(x, (x.shape[-1],), weight, self.eps)
59
+ variance = x.float().pow(2).mean(dim=-1, keepdim=True)
60
+ return weight * (x * torch.rsqrt(variance + self.eps).to(x.dtype))
61
+
62
+ def extra_repr(self) -> str:
63
+ return f"{self.weight.shape[0]}, eps={self.eps}"
64
+
65
+
66
+ def _apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
67
+ """Rotate interleaved (even, odd) channel pairs. ``x``: [B, H, T, D]; ``cos``/``sin``: [B|1, 1, T, D/2]."""
68
+ even = x[..., ::2]
69
+ odd = x[..., 1::2]
70
+ return torch.stack((even * cos - odd * sin, even * sin + odd * cos), dim=-1).flatten(-2)
71
+
72
+
73
+ class CometAttention(nn.Module):
74
+ """Causal grouped-query attention with QK-norm and RoPE."""
75
+
76
+ def __init__(self, config: CometConfig):
77
+ super().__init__()
78
+ self.heads = config.num_attention_heads
79
+ self.kv_heads = config.num_key_value_heads
80
+ self.head_dim = config.hidden_size // config.num_attention_heads
81
+ self.q_dim = self.heads * self.head_dim
82
+ self.kv_dim = self.kv_heads * self.head_dim
83
+ self.fused_projections = config.fused_projections
84
+ if self.fused_projections:
85
+ self.qkv_proj = nn.Linear(config.hidden_size, self.q_dim + 2 * self.kv_dim, bias=False)
86
+ else:
87
+ self.q_proj = nn.Linear(config.hidden_size, self.q_dim, bias=False)
88
+ self.k_proj = nn.Linear(config.hidden_size, self.kv_dim, bias=False)
89
+ self.v_proj = nn.Linear(config.hidden_size, self.kv_dim, bias=False)
90
+ self.o_proj = nn.Linear(self.q_dim, config.hidden_size, bias=False)
91
+ self.o_proj._comet_residual_out = True
92
+ if config.qk_norm:
93
+ self.q_norm = CometRMSNorm(self.head_dim, eps=config.rms_norm_eps)
94
+ self.k_norm = CometRMSNorm(self.head_dim, eps=config.rms_norm_eps)
95
+ else:
96
+ self.q_norm = nn.Identity()
97
+ self.k_norm = nn.Identity()
98
+
99
+ def forward(
100
+ self,
101
+ x: torch.Tensor,
102
+ cos: torch.Tensor,
103
+ sin: torch.Tensor,
104
+ attn_mask: Optional[torch.Tensor] = None,
105
+ is_causal: bool = False,
106
+ cache=None,
107
+ layer_idx: int = 0,
108
+ ) -> torch.Tensor:
109
+ batch, sequence, _ = x.shape
110
+ if self.fused_projections:
111
+ q, k, v = self.qkv_proj(x).split((self.q_dim, self.kv_dim, self.kv_dim), dim=-1)
112
+ else:
113
+ q, k, v = self.q_proj(x), self.k_proj(x), self.v_proj(x)
114
+ q = q.view(batch, sequence, self.heads, self.head_dim).transpose(1, 2)
115
+ k = k.view(batch, sequence, self.kv_heads, self.head_dim).transpose(1, 2)
116
+ v = v.view(batch, sequence, self.kv_heads, self.head_dim).transpose(1, 2)
117
+
118
+ cos = cos.to(device=q.device, dtype=q.dtype)
119
+ sin = sin.to(device=q.device, dtype=q.dtype)
120
+ q = _apply_rope(self.q_norm(q), cos, sin)
121
+ k = _apply_rope(self.k_norm(k), cos, sin)
122
+
123
+ if cache is not None:
124
+ # Keys are stored already normalised and rotated, at key/value-head granularity.
125
+ k, v = cache.update(k, v, layer_idx)
126
+
127
+ groups = self.heads // self.kv_heads
128
+ if groups > 1:
129
+ # Query head h reads key/value head h // groups (same grouping as `enable_gqa`).
130
+ length = k.shape[-2]
131
+ k = k[:, :, None].expand(batch, self.kv_heads, groups, length, self.head_dim)
132
+ k = k.reshape(batch, self.heads, length, self.head_dim)
133
+ v = v[:, :, None].expand(batch, self.kv_heads, groups, length, self.head_dim)
134
+ v = v.reshape(batch, self.heads, length, self.head_dim)
135
+
136
+ if attn_mask is not None:
137
+ y = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask.to(q.device))
138
+ else:
139
+ y = F.scaled_dot_product_attention(q, k, v, is_causal=is_causal)
140
+ return self.o_proj(y.transpose(1, 2).reshape(batch, sequence, self.q_dim))
141
+
142
+
143
+ class CometRecallMixer(nn.Module):
144
+ """Gated local recall branch: depthwise, dilated, causal convolution over a narrow lane."""
145
+
146
+ def __init__(self, config: CometConfig, dilation: int):
147
+ super().__init__()
148
+ width = config.memory_width
149
+ self.width = width
150
+ self.kernel_size = config.memory_kernel_size
151
+ self.dilation = dilation
152
+ # Number of past frames a single output position can see.
153
+ self.history = (self.kernel_size - 1) * self.dilation
154
+ self.in_proj = nn.Linear(config.hidden_size, 2 * width, bias=False)
155
+ self.depthwise = nn.Conv1d(
156
+ width,
157
+ width,
158
+ kernel_size=self.kernel_size,
159
+ dilation=dilation,
160
+ groups=width,
161
+ bias=False,
162
+ )
163
+ self.out_proj = nn.Linear(width, config.hidden_size, bias=False)
164
+ self.out_proj._comet_residual_out = True
165
+
166
+ def forward(
167
+ self,
168
+ x: torch.Tensor,
169
+ token_mask: Optional[torch.Tensor] = None,
170
+ cache=None,
171
+ layer_idx: int = 0,
172
+ ) -> torch.Tensor:
173
+ batch, sequence, _ = x.shape
174
+ gate, value = self.in_proj(x).chunk(2, dim=-1)
175
+ if token_mask is not None:
176
+ # Padding tokens contribute nothing to the convolution, exactly like the
177
+ # zero padding that precedes the first real token during training.
178
+ value = value * token_mask.to(device=value.device, dtype=value.dtype).unsqueeze(-1)
179
+
180
+ if cache is not None:
181
+ # The convolution input is cached in the "key" slot as [B, 1, T, width] so
182
+ # that every generic cache operation (beam reordering, cropping, batch
183
+ # expansion) applies to it unchanged. The "value" slot is a placeholder.
184
+ frames, _ = cache.update(value.unsqueeze(1), value.new_zeros(batch, 1, sequence, 1), layer_idx)
185
+ context = frames[:, 0, -(self.history + sequence) :]
186
+ else:
187
+ context = value
188
+
189
+ left = self.history + sequence - context.shape[1]
190
+ context = F.pad(context.transpose(1, 2), (left, 0))
191
+ value = self.depthwise(context).transpose(1, 2)
192
+ return self.out_proj(torch.sigmoid(gate) * value)
193
+
194
+
195
+ class CometSwiGLU(nn.Module):
196
+ def __init__(self, config: CometConfig):
197
+ super().__init__()
198
+ self.fused_projections = config.fused_projections
199
+ if self.fused_projections:
200
+ self.gate_up = nn.Linear(config.hidden_size, 2 * config.intermediate_size, bias=False)
201
+ else:
202
+ self.gate = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
203
+ self.up = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
204
+ self.down = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
205
+ self.down._comet_residual_out = True
206
+
207
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
208
+ if self.fused_projections:
209
+ gate, up = self.gate_up(x).chunk(2, dim=-1)
210
+ else:
211
+ gate, up = self.gate(x), self.up(x)
212
+ return self.down(F.silu(gate) * up)
213
+
214
+
215
+ class CometBlock(nn.Module):
216
+ def __init__(self, config: CometConfig, index: int):
217
+ super().__init__()
218
+ self.index = index
219
+ self.kind = config.block_kind(index)
220
+ self.norm1 = CometRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
221
+ self.norm2 = CometRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
222
+ if self.kind == "global":
223
+ self.mixer = CometAttention(config)
224
+ else:
225
+ self.mixer = CometRecallMixer(config, config.block_dilation(index))
226
+ self.mlp = CometSwiGLU(config)
227
+ self.mixer_scale = nn.Parameter(torch.full((config.hidden_size,), float(config.layer_scale_init)))
228
+ self.mlp_scale = nn.Parameter(torch.full((config.hidden_size,), float(config.layer_scale_init)))
229
+ self.dropout = config.dropout
230
+
231
+ def forward(
232
+ self,
233
+ x: torch.Tensor,
234
+ cos: torch.Tensor,
235
+ sin: torch.Tensor,
236
+ attn_mask: Optional[torch.Tensor] = None,
237
+ token_mask: Optional[torch.Tensor] = None,
238
+ is_causal: bool = False,
239
+ cache=None,
240
+ ) -> torch.Tensor:
241
+ h = self.norm1(x)
242
+ if self.kind == "global":
243
+ h = self.mixer(h, cos, sin, attn_mask=attn_mask, is_causal=is_causal, cache=cache, layer_idx=self.index)
244
+ else:
245
+ h = self.mixer(h, token_mask=token_mask, cache=cache, layer_idx=self.index)
246
+ if self.dropout:
247
+ h = F.dropout(h, p=self.dropout, training=self.training)
248
+ x = x + h * self.mixer_scale.to(h.dtype)
249
+ h = self.mlp(self.norm2(x))
250
+ if self.dropout:
251
+ h = F.dropout(h, p=self.dropout, training=self.training)
252
+ return x + h * self.mlp_scale.to(h.dtype)
253
+
254
+
255
+ class CometPreTrainedModel(PreTrainedModel):
256
+ config_class = CometConfig
257
+ base_model_prefix = ""
258
+ supports_gradient_checkpointing = True
259
+ _no_split_modules = ["CometBlock"]
260
+ _skip_keys_device_placement = ["past_key_values"]
261
+ _supports_cache_class = True
262
+
263
+ def _init_weights(self, module: nn.Module) -> None:
264
+ std = self.config.initializer_range
265
+ if isinstance(module, nn.Linear):
266
+ if getattr(module, "_comet_residual_out", False):
267
+ std = std / math.sqrt(2.0 * self.config.num_hidden_layers)
268
+ nn.init.normal_(module.weight, mean=0.0, std=std)
269
+ if module.bias is not None:
270
+ nn.init.zeros_(module.bias)
271
+ elif isinstance(module, (nn.Embedding, nn.Conv1d)):
272
+ nn.init.normal_(module.weight, mean=0.0, std=std)
273
+ elif isinstance(module, CometRMSNorm):
274
+ nn.init.ones_(module.weight)
275
+ elif isinstance(module, CometBlock):
276
+ nn.init.constant_(module.mixer_scale, float(self.config.layer_scale_init))
277
+ nn.init.constant_(module.mlp_scale, float(self.config.layer_scale_init))
278
+
279
+
280
+ class CometModel(CometPreTrainedModel):
281
+ """Bare Comet trunk: token embeddings, blocks and the final norm. Returns hidden states."""
282
+
283
+ def __init__(self, config: CometConfig):
284
+ super().__init__(config)
285
+ self._build_trunk(config)
286
+ self.post_init()
287
+
288
+ def _build_trunk(self, config: CometConfig) -> None:
289
+ self.embed = nn.Embedding(config.vocab_size, config.hidden_size)
290
+ self.blocks = nn.ModuleList(CometBlock(config, index) for index in range(config.num_hidden_layers))
291
+ self.norm = CometRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
292
+ self.gradient_checkpointing = False
293
+
294
+ # RoPE tables are built lazily on first use and kept as plain attributes rather than
295
+ # buffers: they are derived constants, and this keeps them out of every loading path
296
+ # (meta-device instantiation, device maps, dtype casts).
297
+ self._rope_tables = None
298
+
299
+ def _rope(self, device: torch.device) -> Tuple[torch.Tensor, torch.Tensor]:
300
+ tables = self._rope_tables
301
+ if tables is None or tables[0].device != device:
302
+ config = self.config
303
+ head_dim = config.hidden_size // config.num_attention_heads
304
+ exponent = torch.arange(0, head_dim, 2, dtype=torch.float32, device="cpu") / head_dim
305
+ inv_freq = 1.0 / (config.rope_theta**exponent)
306
+ positions = torch.arange(config.max_position_embeddings, dtype=torch.float32, device="cpu")
307
+ frequencies = torch.outer(positions, inv_freq)
308
+ tables = (frequencies.cos().to(device), frequencies.sin().to(device))
309
+ self._rope_tables = tables
310
+ return tables
311
+
312
+ def get_input_embeddings(self) -> nn.Embedding:
313
+ return self.embed
314
+
315
+ def set_input_embeddings(self, value: nn.Embedding) -> None:
316
+ self.embed = value
317
+
318
+ def _run_trunk(
319
+ self,
320
+ input_ids: Optional[torch.LongTensor] = None,
321
+ attention_mask: Optional[torch.Tensor] = None,
322
+ position_ids: Optional[torch.LongTensor] = None,
323
+ past_key_values=None,
324
+ inputs_embeds: Optional[torch.Tensor] = None,
325
+ use_cache: Optional[bool] = None,
326
+ output_hidden_states: Optional[bool] = None,
327
+ ):
328
+ if (input_ids is None) == (inputs_embeds is None):
329
+ raise ValueError("Pass exactly one of `input_ids` or `inputs_embeds`.")
330
+ if inputs_embeds is None:
331
+ inputs_embeds = self.embed(input_ids)
332
+ batch, sequence = inputs_embeds.shape[:2]
333
+ device = inputs_embeds.device
334
+
335
+ use_cache = self.config.use_cache if use_cache is None else use_cache
336
+ if self.training and past_key_values is None:
337
+ use_cache = False
338
+ cache = past_key_values
339
+ if cache is not None and not hasattr(cache, "update"):
340
+ raise TypeError(
341
+ "`past_key_values` must be a `transformers.Cache` instance (for example `DynamicCache()`); "
342
+ "legacy tuple caches are not supported."
343
+ )
344
+ if cache is None and use_cache:
345
+ cache = DynamicCache()
346
+ past = _cache_length(cache)
347
+ total = past + sequence
348
+ if total > self.config.max_position_embeddings:
349
+ raise ValueError(
350
+ f"Sequence length {total} exceeds max_position_embeddings={self.config.max_position_embeddings}."
351
+ )
352
+
353
+ # ---- masks -------------------------------------------------------------
354
+ attn_mask = None # [B|1, 1, T, past + T] boolean, True = may attend
355
+ token_mask = None # [B, T] boolean, True = real token (current chunk only)
356
+ is_causal = False
357
+ keep = None
358
+ if attention_mask is not None:
359
+ if attention_mask.dim() != 2:
360
+ raise ValueError("`attention_mask` must have shape [batch, sequence].")
361
+ keep = attention_mask.to(device=device, dtype=torch.bool)
362
+ if keep.shape[1] == sequence and past > 0:
363
+ # Only the new chunk was described; everything cached is treated as real.
364
+ keep = torch.cat([keep.new_ones(batch, past), keep], dim=1)
365
+ if keep.shape[1] != total:
366
+ raise ValueError(
367
+ f"`attention_mask` has length {attention_mask.shape[1]} but the model sees {total} positions "
368
+ f"({past} cached + {sequence} new)."
369
+ )
370
+ if bool(keep.all()):
371
+ keep = None
372
+
373
+ if keep is not None or (past > 0 and sequence > 1):
374
+ query_positions = past + torch.arange(sequence, device=device)
375
+ key_positions = torch.arange(total, device=device)
376
+ attn_mask = (key_positions[None, :] <= query_positions[:, None])[None, None]
377
+ if keep is not None:
378
+ # Every position may always attend to itself so that padded rows never
379
+ # become fully masked (which would produce NaNs).
380
+ diagonal = (key_positions[None, :] == query_positions[:, None])[None, None]
381
+ attn_mask = (attn_mask & keep[:, None, None, :]) | diagonal
382
+ token_mask = keep[:, past:]
383
+ elif past == 0 and sequence > 1:
384
+ is_causal = True
385
+
386
+ # ---- positions ---------------------------------------------------------
387
+ if position_ids is None:
388
+ if keep is not None:
389
+ # Real tokens are numbered 0, 1, 2, ... regardless of how much padding precedes them.
390
+ position_ids = (keep.long().cumsum(-1) - 1).clamp_min(0)[:, past:]
391
+ else:
392
+ position_ids = torch.arange(past, total, device=device)[None, :]
393
+ else:
394
+ position_ids = position_ids.to(device)
395
+ if position_ids.dim() == 1:
396
+ position_ids = position_ids[None, :]
397
+ if position_ids.shape[-1] != sequence:
398
+ position_ids = position_ids[..., -sequence:]
399
+ if int(position_ids.max()) >= self.config.max_position_embeddings:
400
+ raise ValueError("`position_ids` exceed max_position_embeddings.")
401
+ rope_cos, rope_sin = self._rope(device)
402
+ cos = rope_cos[position_ids].unsqueeze(1)
403
+ sin = rope_sin[position_ids].unsqueeze(1)
404
+
405
+ # ---- blocks ------------------------------------------------------------
406
+ x = inputs_embeds
407
+ all_hidden_states = () if output_hidden_states else None
408
+ for block in self.blocks:
409
+ if output_hidden_states:
410
+ all_hidden_states += (x,)
411
+ if self.gradient_checkpointing and self.training and cache is None:
412
+ x = torch.utils.checkpoint.checkpoint(
413
+ block, x, cos, sin, attn_mask, token_mask, is_causal, use_reentrant=False
414
+ )
415
+ else:
416
+ x = block(x, cos, sin, attn_mask=attn_mask, token_mask=token_mask, is_causal=is_causal, cache=cache)
417
+ x = self.norm(x)
418
+ if output_hidden_states:
419
+ all_hidden_states += (x,)
420
+ return x, (cache if use_cache else None), all_hidden_states
421
+
422
+ def forward(
423
+ self,
424
+ input_ids: Optional[torch.LongTensor] = None,
425
+ attention_mask: Optional[torch.Tensor] = None,
426
+ position_ids: Optional[torch.LongTensor] = None,
427
+ past_key_values=None,
428
+ inputs_embeds: Optional[torch.Tensor] = None,
429
+ use_cache: Optional[bool] = None,
430
+ output_attentions: Optional[bool] = None,
431
+ output_hidden_states: Optional[bool] = None,
432
+ return_dict: Optional[bool] = None,
433
+ **kwargs,
434
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
435
+ if output_hidden_states is None:
436
+ output_hidden_states = bool(getattr(self.config, "output_hidden_states", False))
437
+ if return_dict is None:
438
+ return_dict = bool(getattr(self.config, "return_dict", True))
439
+ hidden, cache, all_hidden_states = self._run_trunk(
440
+ input_ids=input_ids,
441
+ attention_mask=attention_mask,
442
+ position_ids=position_ids,
443
+ past_key_values=past_key_values,
444
+ inputs_embeds=inputs_embeds,
445
+ use_cache=use_cache,
446
+ output_hidden_states=output_hidden_states,
447
+ )
448
+ if not return_dict:
449
+ return tuple(item for item in (hidden, cache, all_hidden_states) if item is not None)
450
+ return BaseModelOutputWithPast(
451
+ last_hidden_state=hidden,
452
+ past_key_values=cache,
453
+ hidden_states=all_hidden_states,
454
+ )
455
+
456
+
457
+ class CometForCausalLM(CometModel, GenerationMixin):
458
+ """
459
+ Comet trunk with the language-modelling head.
460
+
461
+ The state-dict layout is flat and identical to the training checkpoint
462
+ (``embed.weight``, ``blocks.N.*``, ``norm.weight``). With
463
+ ``config.tie_word_embeddings=True`` there is no separate head parameter: logits are
464
+ computed directly against ``embed.weight``.
465
+ """
466
+
467
+ def __init__(self, config: CometConfig):
468
+ CometPreTrainedModel.__init__(self, config)
469
+ self._build_trunk(config)
470
+ if config.tie_word_embeddings:
471
+ self.lm_head = None
472
+ else:
473
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
474
+ self.post_init()
475
+
476
+ def get_output_embeddings(self):
477
+ # `None` when tied: there is nothing for Transformers to tie or resize separately.
478
+ return self.lm_head
479
+
480
+ def set_output_embeddings(self, value) -> None:
481
+ self.lm_head = value
482
+
483
+ def _logits(self, hidden: torch.Tensor) -> torch.Tensor:
484
+ weight = self.embed.weight if self.lm_head is None else self.lm_head.weight
485
+ return F.linear(hidden.to(weight.device), weight)
486
+
487
+ def forward(
488
+ self,
489
+ input_ids: Optional[torch.LongTensor] = None,
490
+ attention_mask: Optional[torch.Tensor] = None,
491
+ position_ids: Optional[torch.LongTensor] = None,
492
+ past_key_values=None,
493
+ inputs_embeds: Optional[torch.Tensor] = None,
494
+ labels: Optional[torch.LongTensor] = None,
495
+ use_cache: Optional[bool] = None,
496
+ output_attentions: Optional[bool] = None,
497
+ output_hidden_states: Optional[bool] = None,
498
+ return_dict: Optional[bool] = None,
499
+ **kwargs,
500
+ ) -> Union[Tuple, CausalLMOutputWithPast]:
501
+ r"""
502
+ labels (`torch.LongTensor` of shape `(batch, sequence)`, *optional*):
503
+ Next-token targets. They are shifted inside the model, so pass ``labels=input_ids``;
504
+ positions set to ``-100`` are ignored.
505
+ """
506
+ if output_hidden_states is None:
507
+ output_hidden_states = bool(getattr(self.config, "output_hidden_states", False))
508
+ if return_dict is None:
509
+ return_dict = bool(getattr(self.config, "return_dict", True))
510
+ hidden, cache, all_hidden_states = self._run_trunk(
511
+ input_ids=input_ids,
512
+ attention_mask=attention_mask,
513
+ position_ids=position_ids,
514
+ past_key_values=past_key_values,
515
+ inputs_embeds=inputs_embeds,
516
+ use_cache=use_cache,
517
+ output_hidden_states=output_hidden_states,
518
+ )
519
+ logits = self._logits(hidden)
520
+
521
+ loss = None
522
+ if labels is not None:
523
+ shift_logits = logits[:, :-1].float()
524
+ shift_labels = labels[:, 1:].to(shift_logits.device)
525
+ total = F.cross_entropy(
526
+ shift_logits.reshape(-1, shift_logits.shape[-1]),
527
+ shift_labels.reshape(-1),
528
+ ignore_index=-100,
529
+ reduction="sum",
530
+ )
531
+ loss = total / shift_labels.ne(-100).sum().clamp_min(1)
532
+
533
+ if not return_dict:
534
+ return tuple(item for item in (loss, logits, cache, all_hidden_states) if item is not None)
535
+ return CausalLMOutputWithPast(
536
+ loss=loss,
537
+ logits=logits,
538
+ past_key_values=cache,
539
+ hidden_states=all_hidden_states,
540
+ )
541
+
542
+ def prepare_inputs_for_generation(
543
+ self,
544
+ input_ids: torch.LongTensor,
545
+ past_key_values=None,
546
+ attention_mask: Optional[torch.Tensor] = None,
547
+ inputs_embeds: Optional[torch.Tensor] = None,
548
+ **kwargs,
549
+ ) -> dict:
550
+ """Feed only the tokens the cache has not seen yet; keep the full-length attention mask."""
551
+ past = _cache_length(past_key_values)
552
+ model_inputs = {}
553
+ if inputs_embeds is not None and past == 0:
554
+ model_inputs["inputs_embeds"] = inputs_embeds
555
+ else:
556
+ seen = past
557
+ if inputs_embeds is not None:
558
+ # Generation started from embeddings: `input_ids` holds only the generated tokens.
559
+ seen = past - inputs_embeds.shape[1]
560
+ unseen = input_ids.shape[1] - seen
561
+ if past > 0:
562
+ input_ids = input_ids[:, -unseen:] if unseen > 0 else input_ids[:, -1:]
563
+ model_inputs["input_ids"] = input_ids.contiguous()
564
+ model_inputs["past_key_values"] = past_key_values
565
+ model_inputs["attention_mask"] = attention_mask
566
+ model_inputs["use_cache"] = kwargs.get("use_cache", True)
567
+ if kwargs.get("output_hidden_states") is not None:
568
+ model_inputs["output_hidden_states"] = kwargs["output_hidden_states"]
569
+ return model_inputs
570
+
571
+
572
+ __all__ = ["CometPreTrainedModel", "CometModel", "CometForCausalLM"]
special_tokens_map.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": "<|endoftext|>",
3
+ "eos_token": "<|endoftext|>",
4
+ "unk_token": "<|endoftext|>",
5
+ "pad_token": "<|padding|>"
6
+ }
tokenizer_config.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tokenizer_class": "PreTrainedTokenizerFast",
3
+ "bos_token": "<|endoftext|>",
4
+ "eos_token": "<|endoftext|>",
5
+ "unk_token": "<|endoftext|>",
6
+ "pad_token": "<|padding|>",
7
+ "add_prefix_space": false,
8
+ "clean_up_tokenization_spaces": false,
9
+ "model_input_names": [
10
+ "input_ids",
11
+ "attention_mask"
12
+ ],
13
+ "model_max_length": 8192,
14
+ "padding_side": "left"
15
+ }