Update pipeline tag and add library name for `ettin-decoder-32m`
#1
by
nielsr
HF Staff
- opened
README.md
CHANGED
|
@@ -1,38 +1,34 @@
|
|
| 1 |
---
|
| 2 |
-
license: mit
|
| 3 |
language:
|
| 4 |
- en
|
| 5 |
-
|
|
|
|
|
|
|
| 6 |
---
|
|
|
|
| 7 |
# Ettin: an Open Suite of Paired Encoders and Decoders
|
| 8 |
|
| 9 |
[](https://opensource.org/licenses/MIT)
|
| 10 |
[](https://arxiv.org/abs/2507.11412)
|
| 11 |
-
[](https://huggingface.co/datasets/jhu-clsp)
|
| 13 |
[](https://github.com/jhu-clsp/ettin-encoder-vs-decoder)
|
| 14 |
|
| 15 |
> 🎯 **TL;DR**: State-of-the-art paired encoder and decoder models (17M-1B params) trained identically for fair comparison with open data. Encoders beat ModernBERT. Decoders beat Llama 3.2/SmolLM2.
|
| 16 |
|
| 17 |
-
📄 [Paper](https://arxiv.org/abs/2507.11412) |
|
| 18 |
|
| 19 |
-
This
|
| 20 |
|
| 21 |
## Table of Contents
|
| 22 |
-
- [Performance Highlights](
|
| 23 |
-
- [Quick Start](
|
| 24 |
-
- [Model
|
| 25 |
-
- [
|
| 26 |
-
- [
|
| 27 |
-
|
| 28 |
-
- [Decoder Models](#decoder-models)
|
| 29 |
-
- [Cross-Objective Models](#cross-objective-models)
|
| 30 |
-
- [Accessing Training Checkpoints](#accessing-training-checkpoints)
|
| 31 |
-
- [Research Applications](#research-applications)
|
| 32 |
- [Training Details](#training-details)
|
| 33 |
-
- [
|
| 34 |
-
- [Usage Examples](#usage-examples)
|
| 35 |
-
- [Fine-tuning Examples](#fine-tuning-examples)
|
| 36 |
- [Citation](#citation)
|
| 37 |
|
| 38 |
## 📊 Performance Highlights
|
|
@@ -57,8 +53,7 @@ This model is part of the Ettin suite - the first collection of paired encoder-o
|
|
| 57 |
pip install torch>=1.9.0
|
| 58 |
# until the new pip release, install from main to use decoders (transformers>=4.54.X will contain it)
|
| 59 |
# encoders work with transformers>=4.48.0
|
| 60 |
-
pip install git+https://github.com/huggingface/transformers.git
|
| 61 |
-
```
|
| 62 |
|
| 63 |
### 30-Second Examples
|
| 64 |
|
|
@@ -68,6 +63,11 @@ from transformers import AutoTokenizer, AutoModel
|
|
| 68 |
|
| 69 |
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-encoder-150m")
|
| 70 |
model = AutoModel.from_pretrained("jhu-clsp/ettin-encoder-150m")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
```
|
| 72 |
|
| 73 |
**Decoder for Text Generation:**
|
|
@@ -76,30 +76,14 @@ from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
| 76 |
|
| 77 |
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-decoder-150m")
|
| 78 |
model = AutoModelForCausalLM.from_pretrained("jhu-clsp/ettin-decoder-150m")
|
| 79 |
-
```
|
| 80 |
-
|
| 81 |
-
## Model Description
|
| 82 |
-
|
| 83 |
-
Ettin models are designed to provide a foundation for comparing encoder-only and decoder-only architectures. Unlike previous comparisons that were limited by different training data, architectures, and recipes, Ettin models use:
|
| 84 |
-
|
| 85 |
-
1. **Identical training data** - Same high-quality mixture across all models
|
| 86 |
-
2. **Open Training Data** - Data is available now with batch-level training data for each of the 250+ checkpoints
|
| 87 |
-
3. **Matched architectures** - Only differing in attention patterns (bidirectional vs causal) and training objectives (MLM vs CLM)
|
| 88 |
-
4. **Consistent training recipe** - Three-phase training with 2T tokens
|
| 89 |
-
5. **Multiple scales** - From 17M to 1B parameters
|
| 90 |
-
|
| 91 |
-
This approach allows for true apples-to-apples comparisons between encoder and decoder models, revealing the inherent strengths of each architecture.
|
| 92 |
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
- **Mid-training/Extension Data**: [jhu-clsp/ettin-extension-data](https://huggingface.co/datasets/jhu-clsp/ettin-extension-data) - 250B tokens of higher-quality filtered data
|
| 99 |
-
- **Decay Phase Data**: [jhu-clsp/ettin-decay-data](https://huggingface.co/datasets/jhu-clsp/ettin-decay-data) - 100B tokens of premium data sources
|
| 100 |
-
- **Training Data Order**: [jhu-clsp/ettin-data-order](https://huggingface.co/datasets/jhu-clsp/ettin-data-order) - Batch-level training order (columns: input_ids, step)
|
| 101 |
|
| 102 |
-
## Model Family
|
| 103 |
|
| 104 |
### Encoder Models
|
| 105 |
|
|
@@ -143,35 +127,18 @@ These models demonstrate what happens when you continue training encoders as dec
|
|
| 143 |
**Load as decoders** using `AutoModelForCausalLM`:
|
| 144 |
|
| 145 |
| Size | Model | Parameters | Description | Download |
|
| 146 |
-
|
| 147 |
| XXS | [ettin-decoder-from-encoder-17m](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-17m) | 17M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-17m) |
|
| 148 |
| XS | [ettin-decoder-from-encoder-32m](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-32m) | 32M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-32m) |
|
| 149 |
| Small | [ettin-decoder-from-encoder-68m](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-68m) | 68M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-68m) |
|
| 150 |
-
| Base | [ettin-decoder-from-encoder-150m](https://huggingface.co/jhu-clsp/ettin-decoder-
|
| 151 |
-
| Large | [ettin-decoder-from-encoder-400m](https://huggingface.co/jhu-clsp/ettin-decoder-
|
| 152 |
| XL | [ettin-decoder-from-encoder-1b](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-1b) | 1B | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-1b) |
|
| 153 |
|
| 154 |
-
|
| 155 |
-
```python
|
| 156 |
-
# Encoder-from-decoder: Load as encoder
|
| 157 |
-
from transformers import AutoTokenizer, AutoModel
|
| 158 |
-
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-encoder-from-decoder-150m")
|
| 159 |
-
model = AutoModel.from_pretrained("jhu-clsp/ettin-encoder-from-decoder-150m")
|
| 160 |
|
| 161 |
-
|
| 162 |
-
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 163 |
-
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-decoder-from-encoder-150m")
|
| 164 |
-
model = AutoModelForCausalLM.from_pretrained("jhu-clsp/ettin-decoder-from-encoder-150m")
|
| 165 |
-
```
|
| 166 |
|
| 167 |
-
## Accessing Training Checkpoints
|
| 168 |
-
|
| 169 |
-
Beyond the final models listed above, we provide access to intermediate training checkpoints for research and analysis purposes. These checkpoints allow you to study model behavior and performance throughout the training process. You can get the checkpoints either in HF format or raw for continued pre-training (e.g. Composer format).
|
| 170 |
-
|
| 171 |
-
#### Raw Checkpoints
|
| 172 |
-
All raw training checkpoints are available in the [jhu-clsp/ettin-checkpoints](https://huggingface.co/datasets/jhu-clsp/ettin-checkpoints) dataset.
|
| 173 |
-
|
| 174 |
-
#### HuggingFace Format Checkpoints
|
| 175 |
Each model repository contains multiple tagged versions representing different training stages:
|
| 176 |
|
| 177 |
- **`step{number}`** - Pretraining phase checkpoints (e.g., `step599525`, `step596528`)
|
|
@@ -179,90 +146,30 @@ Each model repository contains multiple tagged versions representing different t
|
|
| 179 |
- **`decay{number}`** - Decay phase checkpoints (e.g., `decay100`, `decay500`)
|
| 180 |
|
| 181 |
```python
|
| 182 |
-
from transformers import
|
| 183 |
|
| 184 |
# Load a specific pretraining checkpoint
|
| 185 |
model = AutoModelForCausalLM.from_pretrained(
|
| 186 |
"jhu-clsp/ettin-decoder-400m",
|
| 187 |
revision="step590532" # Specific checkpoint tag
|
| 188 |
)
|
| 189 |
-
|
| 190 |
-
# Load an extension phase checkpoint
|
| 191 |
-
model = AutoModelForCausalLM.from_pretrained(
|
| 192 |
-
"jhu-clsp/ettin-decoder-400m",
|
| 193 |
-
revision="ext1000"
|
| 194 |
-
)
|
| 195 |
-
|
| 196 |
-
# Load a decay phase checkpoint
|
| 197 |
-
model = AutoModelForCausalLM.from_pretrained(
|
| 198 |
-
"jhu-clsp/ettin-decoder-400m",
|
| 199 |
-
revision="decay100"
|
| 200 |
-
)
|
| 201 |
```
|
| 202 |
|
| 203 |
-
|
| 204 |
|
|
|
|
| 205 |
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
### What Makes Ettin Unique
|
| 209 |
-
|
| 210 |
-
Ettin provides the first **controlled comparison** of encoder vs. decoder architectures:
|
| 211 |
-
|
| 212 |
-
- **Identical Training Data**: Same 2T token mixture across all models
|
| 213 |
-
- **Matched Architectures**: Only attention patterns and objectives differ
|
| 214 |
-
- **Open Everything**: Training data, model weights, and batch-level training order
|
| 215 |
-
- **Multiple Scales**: Fair comparison from 17M to 1B parameters
|
| 216 |
-
- **250+ Checkpoints**: Complete training trajectory analysis
|
| 217 |
-
|
| 218 |
-
### Use Cases for Researchers
|
| 219 |
-
|
| 220 |
-
- **Architecture Studies**: Compare encoder vs decoder capabilities fairly
|
| 221 |
-
- **Training Dynamics**: Analyze 250+ checkpoints with batch-level data ordering
|
| 222 |
-
- **Scaling Laws**: Study how architectural advantages change with scale
|
| 223 |
-
- **Transfer Learning**: Investigate cross-objective training effectiveness
|
| 224 |
-
- **Replication Studies**: First open replication of ModernBERT training recipe
|
| 225 |
-
|
| 226 |
-
### Reproducibility
|
| 227 |
-
|
| 228 |
-
All training artifacts are publicly available:
|
| 229 |
-
- Training data with exact batch ordering
|
| 230 |
-
- Model checkpoints every 8.5B tokens
|
| 231 |
-
- Complete hyperparameter configurations
|
| 232 |
-
- Training code and evaluation scripts
|
| 233 |
-
|
| 234 |
-
## Training Details
|
| 235 |
-
|
| 236 |
-
**Data:** High-quality mixture including DCLM, Dolma v1.7, scientific papers, code, and curated sources totaling 2T+ tokens
|
| 237 |
-
|
| 238 |
-
**Architecture:** Transformer with RoPE, GLU activations, and prenorm layers
|
| 239 |
-
|
| 240 |
-
**Training Phases:**
|
| 241 |
-
- **Pre-training**: 1.7T tokens with diverse data mixture
|
| 242 |
-
- **Mid-training**: 250B tokens with higher-quality filtered data and context extension to 8K
|
| 243 |
-
- **Decay phase**: 100B tokens with premium data sources
|
| 244 |
-
|
| 245 |
-
**Key Features:**
|
| 246 |
-
- Context length: Up to 8K tokens
|
| 247 |
-
- Vocabulary: 50,368 tokens (ModernBERT tokenizer)
|
| 248 |
-
- Deep but efficient architectures following MobileLLM principles
|
| 249 |
-
|
| 250 |
-
## Model Architecture
|
| 251 |
-
|
| 252 |
-
| Parameter | 17M | 32M | 68M | 150M | 400M | 1B |
|
| 253 |
-
|:----------|:----|:----|:----|:-----|:-----|:---|
|
| 254 |
-
| Layers | 7 | 10 | 19 | 22 | 28 | 28 |
|
| 255 |
-
| Hidden Size | 256 | 384 | 512 | 768 | 1024 | 1792 |
|
| 256 |
-
| Intermediate Size | 384 | 576 | 768 | 1152 | 2624 | 3840 |
|
| 257 |
-
| Attention Heads | 4 | 6 | 8 | 12 | 16 | 28 |
|
| 258 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
|
|
|
|
| 260 |
|
| 261 |
-
## Usage Examples
|
| 262 |
-
|
| 263 |
-
### Encoder: Masked Language Modeling
|
| 264 |
<details>
|
| 265 |
-
<summary>
|
| 266 |
|
| 267 |
```python
|
| 268 |
from transformers import AutoTokenizer, AutoModelForMaskedLM
|
|
@@ -293,10 +200,8 @@ print(f"Predictions: {predictions}")
|
|
| 293 |
|
| 294 |
</details>
|
| 295 |
|
| 296 |
-
### Decoder: Text Generation
|
| 297 |
-
|
| 298 |
<details>
|
| 299 |
-
<summary
|
| 300 |
|
| 301 |
```python
|
| 302 |
from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
@@ -333,614 +238,158 @@ print(generated)
|
|
| 333 |
|
| 334 |
</details>
|
| 335 |
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
### Encoders
|
| 340 |
-
<details><summary>Click to see how to finetune this into a dense embedding model using Sentence Transformers</summary>
|
| 341 |
|
| 342 |
```python
|
| 343 |
-
|
|
|
|
|
|
|
|
|
|
| 344 |
|
| 345 |
-
from
|
| 346 |
-
from
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
SentenceTransformerTrainingArguments,
|
| 350 |
-
)
|
| 351 |
-
from sentence_transformers.evaluation import TripletEvaluator
|
| 352 |
-
from sentence_transformers.losses import CachedMultipleNegativesRankingLoss
|
| 353 |
-
from sentence_transformers.training_args import BatchSamplers
|
| 354 |
-
|
| 355 |
-
def main():
|
| 356 |
-
# parse the lr & model name
|
| 357 |
-
parser = argparse.ArgumentParser()
|
| 358 |
-
parser.add_argument("--lr", type=float, default=8e-5)
|
| 359 |
-
parser.add_argument("--model_name", type=str, default="jhu-clsp/ettin-encoder-150m")
|
| 360 |
-
args = parser.parse_args()
|
| 361 |
-
lr = args.lr
|
| 362 |
-
model_name = args.model_name
|
| 363 |
-
model_shortname = model_name.split("/")[-1]
|
| 364 |
-
|
| 365 |
-
# 1. Load a model to finetune
|
| 366 |
-
model = SentenceTransformer(model_name)
|
| 367 |
-
|
| 368 |
-
# 2. Load a dataset to finetune on
|
| 369 |
-
dataset = load_dataset(
|
| 370 |
-
"sentence-transformers/msmarco-co-condenser-margin-mse-sym-mnrl-mean-v1",
|
| 371 |
-
"triplet-hard",
|
| 372 |
-
split="train",
|
| 373 |
-
)
|
| 374 |
-
dataset_dict = dataset.train_test_split(test_size=1_000, seed=12)
|
| 375 |
-
train_dataset = dataset_dict["train"].select(range(1_250_000))
|
| 376 |
-
eval_dataset = dataset_dict["test"]
|
| 377 |
-
|
| 378 |
-
# 3. Define a loss function
|
| 379 |
-
loss = CachedMultipleNegativesRankingLoss(model, mini_batch_size=16) # Increase mini_batch_size if you have enough VRAM
|
| 380 |
-
|
| 381 |
-
run_name = f"{model_shortname}-DPR-{lr}"
|
| 382 |
-
# 4. (Optional) Specify training arguments
|
| 383 |
-
args = SentenceTransformerTrainingArguments(
|
| 384 |
-
# Required parameter:
|
| 385 |
-
output_dir=f"output/{model_shortname}/{run_name}",
|
| 386 |
-
# Optional training parameters:
|
| 387 |
-
num_train_epochs=1,
|
| 388 |
-
per_device_train_batch_size=512,
|
| 389 |
-
per_device_eval_batch_size=512,
|
| 390 |
-
warmup_ratio=0.05,
|
| 391 |
-
fp16=False, # Set to False if GPU can't handle FP16
|
| 392 |
-
bf16=True, # Set to True if GPU supports BF16
|
| 393 |
-
batch_sampler=BatchSamplers.NO_DUPLICATES, # (Cached)MultipleNegativesRankingLoss benefits from no duplicates
|
| 394 |
-
learning_rate=lr,
|
| 395 |
-
# Optional tracking/debugging parameters:
|
| 396 |
-
save_strategy="steps",
|
| 397 |
-
save_steps=500,
|
| 398 |
-
save_total_limit=2,
|
| 399 |
-
logging_steps=500,
|
| 400 |
-
run_name=run_name, # Used in `wandb`, `tensorboard`, `neptune`, etc. if installed
|
| 401 |
-
)
|
| 402 |
-
|
| 403 |
-
# 5. (Optional) Create an evaluator & evaluate the base model
|
| 404 |
-
dev_evaluator = TripletEvaluator(
|
| 405 |
-
anchors=eval_dataset["query"],
|
| 406 |
-
positives=eval_dataset["positive"],
|
| 407 |
-
negatives=eval_dataset["negative"],
|
| 408 |
-
name="msmarco-co-condenser-dev",
|
| 409 |
-
)
|
| 410 |
-
dev_evaluator(model)
|
| 411 |
-
|
| 412 |
-
# 6. Create a trainer & train
|
| 413 |
-
trainer = SentenceTransformerTrainer(
|
| 414 |
-
model=model,
|
| 415 |
-
args=args,
|
| 416 |
-
train_dataset=train_dataset,
|
| 417 |
-
eval_dataset=eval_dataset,
|
| 418 |
-
loss=loss,
|
| 419 |
-
evaluator=dev_evaluator,
|
| 420 |
-
)
|
| 421 |
-
trainer.train()
|
| 422 |
-
|
| 423 |
-
# 7. (Optional) Evaluate the trained model on the evaluator after training
|
| 424 |
-
dev_evaluator(model)
|
| 425 |
-
|
| 426 |
-
# 8. Save the model
|
| 427 |
-
model.save_pretrained(f"output/{model_shortname}/{run_name}/final")
|
| 428 |
-
|
| 429 |
-
# 9. (Optional) Push it to the Hugging Face Hub
|
| 430 |
-
model.push_to_hub(run_name, private=False)
|
| 431 |
-
|
| 432 |
-
if __name__ == "__main__":
|
| 433 |
-
main()
|
| 434 |
```
|
|
|
|
| 435 |
</details>
|
| 436 |
|
|
|
|
| 437 |
|
| 438 |
-
|
|
|
|
|
|
|
| 439 |
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
|
| 448 |
-
|
| 449 |
-
# Load the datasets required for knowledge distillation (train, queries, documents)
|
| 450 |
-
train = load_dataset(
|
| 451 |
-
path="lightonai/ms-marco-en-bge",
|
| 452 |
-
name="train",
|
| 453 |
-
)
|
| 454 |
-
|
| 455 |
-
queries = load_dataset(
|
| 456 |
-
path="lightonai/ms-marco-en-bge",
|
| 457 |
-
name="queries",
|
| 458 |
-
)
|
| 459 |
-
|
| 460 |
-
documents = load_dataset(
|
| 461 |
-
path="lightonai/ms-marco-en-bge",
|
| 462 |
-
name="documents",
|
| 463 |
-
)
|
| 464 |
-
|
| 465 |
-
# Set the transformation to load the documents/queries texts using the corresponding ids on the fly
|
| 466 |
-
train.set_transform(
|
| 467 |
-
utils.KDProcessing(queries=queries, documents=documents).transform,
|
| 468 |
-
)
|
| 469 |
-
|
| 470 |
-
# Define the base model, training parameters, and output directory
|
| 471 |
-
num_train_epochs = 1
|
| 472 |
-
lr = 8e-5
|
| 473 |
-
batch_size = 16
|
| 474 |
-
accum_steps = 1
|
| 475 |
-
model_name = "jhu-clsp/ettin-encoder-150m"
|
| 476 |
-
model_shortname = model_name.split("/")[-1]
|
| 477 |
-
|
| 478 |
-
# Set the run name for logging and output directory
|
| 479 |
-
run_name = f"{model_shortname}-colbert-KD-{lr}"
|
| 480 |
-
output_dir = f"output/{model_shortname}/{run_name}"
|
| 481 |
-
|
| 482 |
-
# Initialize the ColBERT model from the base model
|
| 483 |
-
model = models.ColBERT(model_name_or_path=model_name)
|
| 484 |
-
|
| 485 |
-
# Configure the training arguments (e.g., epochs, batch size, learning rate)
|
| 486 |
-
args = SentenceTransformerTrainingArguments(
|
| 487 |
-
output_dir=output_dir,
|
| 488 |
-
num_train_epochs=num_train_epochs,
|
| 489 |
-
per_device_train_batch_size=batch_size,
|
| 490 |
-
fp16=False, # Set to False if you get an error that your GPU can't run on FP16
|
| 491 |
-
bf16=True, # Set to True if you have a GPU that supports BF16
|
| 492 |
-
run_name=run_name,
|
| 493 |
-
logging_steps=10,
|
| 494 |
-
learning_rate=lr,
|
| 495 |
-
gradient_accumulation_steps=accum_steps,
|
| 496 |
-
warmup_ratio=0.05,
|
| 497 |
-
)
|
| 498 |
-
|
| 499 |
-
# Use the Distillation loss function for training
|
| 500 |
-
train_loss = losses.Distillation(model=model)
|
| 501 |
-
|
| 502 |
-
# Initialize the trainer
|
| 503 |
-
trainer = SentenceTransformerTrainer(
|
| 504 |
-
model=model,
|
| 505 |
-
args=args,
|
| 506 |
-
train_dataset=train,
|
| 507 |
-
loss=train_loss,
|
| 508 |
-
data_collator=utils.ColBERTCollator(tokenize_fn=model.tokenize),
|
| 509 |
-
)
|
| 510 |
-
|
| 511 |
-
# Start the training process
|
| 512 |
-
trainer.train()
|
| 513 |
-
|
| 514 |
-
model.save_pretrained(f"{output_dir}/final")
|
| 515 |
-
|
| 516 |
-
if __name__ == "__main__":
|
| 517 |
-
main()
|
| 518 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 519 |
```
|
| 520 |
-
</details>
|
| 521 |
|
| 522 |
-
|
| 523 |
|
| 524 |
-
|
| 525 |
-
import logging
|
| 526 |
|
| 527 |
-
|
| 528 |
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
)
|
| 535 |
-
from sentence_transformers.sparse_encoder.evaluation import SparseNanoBEIREvaluator
|
| 536 |
-
from sentence_transformers.sparse_encoder.losses import SparseMultipleNegativesRankingLoss, SpladeLoss
|
| 537 |
-
from sentence_transformers.training_args import BatchSamplers
|
| 538 |
-
|
| 539 |
-
logging.basicConfig(format="%(asctime)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO)
|
| 540 |
-
|
| 541 |
-
# 1. Load a model to finetune with 2. (Optional) model card data
|
| 542 |
-
model = SparseEncoder(
|
| 543 |
-
"jhu-clsp/ettin-encoder-150m",
|
| 544 |
-
model_card_data=SparseEncoderModelCardData(
|
| 545 |
-
language="en",
|
| 546 |
-
license="apache-2.0",
|
| 547 |
-
)
|
| 548 |
-
)
|
| 549 |
|
| 550 |
-
|
| 551 |
-
full_dataset = load_dataset("sentence-transformers/natural-questions", split="train").select(range(100_000))
|
| 552 |
-
dataset_dict = full_dataset.train_test_split(test_size=1_000, seed=12)
|
| 553 |
-
train_dataset = dataset_dict["train"]
|
| 554 |
-
eval_dataset = dataset_dict["test"]
|
| 555 |
-
|
| 556 |
-
# 4. Define a loss function
|
| 557 |
-
loss = SpladeLoss(
|
| 558 |
-
model=model,
|
| 559 |
-
loss=SparseMultipleNegativesRankingLoss(model=model),
|
| 560 |
-
query_regularizer_weight=5e-5,
|
| 561 |
-
document_regularizer_weight=3e-5,
|
| 562 |
-
)
|
| 563 |
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
output_dir=f"models/{run_name}",
|
| 569 |
-
# Optional training parameters:
|
| 570 |
-
num_train_epochs=1,
|
| 571 |
-
per_device_train_batch_size=16,
|
| 572 |
-
per_device_eval_batch_size=16,
|
| 573 |
-
learning_rate=2e-5,
|
| 574 |
-
warmup_ratio=0.1,
|
| 575 |
-
fp16=True, # Set to False if you get an error that your GPU can't run on FP16
|
| 576 |
-
bf16=False, # Set to True if you have a GPU that supports BF16
|
| 577 |
-
batch_sampler=BatchSamplers.NO_DUPLICATES, # MultipleNegativesRankingLoss benefits from no duplicate samples in a batch
|
| 578 |
-
# Optional tracking/debugging parameters:
|
| 579 |
-
eval_strategy="steps",
|
| 580 |
-
eval_steps=1000,
|
| 581 |
-
save_strategy="steps",
|
| 582 |
-
save_steps=1000,
|
| 583 |
-
save_total_limit=2,
|
| 584 |
-
logging_steps=200,
|
| 585 |
-
run_name=run_name, # Will be used in W&B if `wandb` is installed
|
| 586 |
-
)
|
| 587 |
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
trainer = SparseEncoderTrainer(
|
| 593 |
-
model=model,
|
| 594 |
-
args=args,
|
| 595 |
-
train_dataset=train_dataset,
|
| 596 |
-
eval_dataset=eval_dataset,
|
| 597 |
-
loss=loss,
|
| 598 |
-
evaluator=dev_evaluator,
|
| 599 |
-
)
|
| 600 |
-
trainer.train()
|
| 601 |
|
| 602 |
-
|
| 603 |
-
|
|
|
|
| 604 |
|
| 605 |
-
|
| 606 |
-
model.save_pretrained(f"models/{run_name}/final")
|
| 607 |
|
| 608 |
-
|
| 609 |
-
|
|
|
|
|
|
|
|
|
|
| 610 |
|
| 611 |
-
|
| 612 |
-
</details>
|
| 613 |
|
| 614 |
-
|
| 615 |
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
|
|
|
|
|
|
|
|
|
| 619 |
|
| 620 |
-
|
| 621 |
-
from datasets import load_dataset
|
| 622 |
-
|
| 623 |
-
from sentence_transformers import SentenceTransformer
|
| 624 |
-
from sentence_transformers.cross_encoder import (
|
| 625 |
-
CrossEncoder,
|
| 626 |
-
CrossEncoderModelCardData,
|
| 627 |
-
CrossEncoderTrainer,
|
| 628 |
-
CrossEncoderTrainingArguments,
|
| 629 |
-
)
|
| 630 |
-
from sentence_transformers.cross_encoder.evaluation import (
|
| 631 |
-
CrossEncoderNanoBEIREvaluator,
|
| 632 |
-
CrossEncoderRerankingEvaluator,
|
| 633 |
-
)
|
| 634 |
-
from sentence_transformers.cross_encoder.losses import BinaryCrossEntropyLoss
|
| 635 |
-
from sentence_transformers.evaluation import SequentialEvaluator
|
| 636 |
-
from sentence_transformers.util import mine_hard_negatives
|
| 637 |
-
|
| 638 |
-
# Set the log level to INFO to get more information
|
| 639 |
-
logging.basicConfig(format="%(asctime)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO)
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
def main():
|
| 643 |
-
model_name = "jhu-clsp/ettin-encoder-150m"
|
| 644 |
-
|
| 645 |
-
train_batch_size = 64
|
| 646 |
-
num_epochs = 1
|
| 647 |
-
num_hard_negatives = 5 # How many hard negatives should be mined for each question-answer pair
|
| 648 |
-
|
| 649 |
-
# 1a. Load a model to finetune with 1b. (Optional) model card data
|
| 650 |
-
model = CrossEncoder(
|
| 651 |
-
model_name,
|
| 652 |
-
model_card_data=CrossEncoderModelCardData(
|
| 653 |
-
language="en",
|
| 654 |
-
license="apache-2.0",
|
| 655 |
-
),
|
| 656 |
-
)
|
| 657 |
-
print("Model max length:", model.max_length)
|
| 658 |
-
print("Model num labels:", model.num_labels)
|
| 659 |
-
|
| 660 |
-
# 2a. Load the GooAQ dataset: https://huggingface.co/datasets/sentence-transformers/gooaq
|
| 661 |
-
logging.info("Read the gooaq training dataset")
|
| 662 |
-
full_dataset = load_dataset("sentence-transformers/gooaq", split="train").select(range(100_000))
|
| 663 |
-
dataset_dict = full_dataset.train_test_split(test_size=1_000, seed=12)
|
| 664 |
-
train_dataset = dataset_dict["train"]
|
| 665 |
-
eval_dataset = dataset_dict["test"]
|
| 666 |
-
logging.info(train_dataset)
|
| 667 |
-
logging.info(eval_dataset)
|
| 668 |
-
|
| 669 |
-
# 2b. Modify our training dataset to include hard negatives using a very efficient embedding model
|
| 670 |
-
embedding_model = SentenceTransformer("sentence-transformers/static-retrieval-mrl-en-v1", device="cpu")
|
| 671 |
-
hard_train_dataset = mine_hard_negatives(
|
| 672 |
-
train_dataset,
|
| 673 |
-
embedding_model,
|
| 674 |
-
num_negatives=num_hard_negatives, # How many negatives per question-answer pair
|
| 675 |
-
margin=0, # Similarity between query and negative samples should be x lower than query-positive similarity
|
| 676 |
-
range_min=0, # Skip the x most similar samples
|
| 677 |
-
range_max=100, # Consider only the x most similar samples
|
| 678 |
-
sampling_strategy="top", # Sample the top negatives from the range
|
| 679 |
-
batch_size=4096, # Use a batch size of 4096 for the embedding model
|
| 680 |
-
output_format="labeled-pair", # The output format is (query, passage, label), as required by BinaryCrossEntropyLoss
|
| 681 |
-
use_faiss=True,
|
| 682 |
-
)
|
| 683 |
-
logging.info(hard_train_dataset)
|
| 684 |
-
|
| 685 |
-
# 2c. (Optionally) Save the hard training dataset to disk
|
| 686 |
-
# hard_train_dataset.save_to_disk("gooaq-hard-train")
|
| 687 |
-
# Load again with:
|
| 688 |
-
# hard_train_dataset = load_from_disk("gooaq-hard-train")
|
| 689 |
-
|
| 690 |
-
# 3. Define our training loss.
|
| 691 |
-
# pos_weight is recommended to be set as the ratio between positives to negatives, a.k.a. `num_hard_negatives`
|
| 692 |
-
loss = BinaryCrossEntropyLoss(model=model, pos_weight=torch.tensor(num_hard_negatives))
|
| 693 |
-
|
| 694 |
-
# 4a. Define evaluators. We use the CrossEncoderNanoBEIREvaluator, which is a light-weight evaluator for English reranking
|
| 695 |
-
nano_beir_evaluator = CrossEncoderNanoBEIREvaluator(
|
| 696 |
-
dataset_names=["msmarco", "nfcorpus", "nq"],
|
| 697 |
-
batch_size=train_batch_size,
|
| 698 |
-
)
|
| 699 |
-
|
| 700 |
-
# 4b. Define a reranking evaluator by mining hard negatives given query-answer pairs
|
| 701 |
-
# We include the positive answer in the list of negatives, so the evaluator can use the performance of the
|
| 702 |
-
# embedding model as a baseline.
|
| 703 |
-
hard_eval_dataset = mine_hard_negatives(
|
| 704 |
-
eval_dataset,
|
| 705 |
-
embedding_model,
|
| 706 |
-
corpus=full_dataset["answer"], # Use the full dataset as the corpus
|
| 707 |
-
num_negatives=30, # How many documents to rerank
|
| 708 |
-
batch_size=4096,
|
| 709 |
-
include_positives=True,
|
| 710 |
-
output_format="n-tuple",
|
| 711 |
-
use_faiss=True,
|
| 712 |
-
)
|
| 713 |
-
logging.info(hard_eval_dataset)
|
| 714 |
-
reranking_evaluator = CrossEncoderRerankingEvaluator(
|
| 715 |
-
samples=[
|
| 716 |
-
{
|
| 717 |
-
"query": sample["question"],
|
| 718 |
-
"positive": [sample["answer"]],
|
| 719 |
-
"documents": [sample[column_name] for column_name in hard_eval_dataset.column_names[2:]],
|
| 720 |
-
}
|
| 721 |
-
for sample in hard_eval_dataset
|
| 722 |
-
],
|
| 723 |
-
batch_size=train_batch_size,
|
| 724 |
-
name="gooaq-dev",
|
| 725 |
-
# Realistic setting: only rerank the positives that the retriever found
|
| 726 |
-
# Set to True to rerank *all* positives
|
| 727 |
-
always_rerank_positives=False,
|
| 728 |
-
)
|
| 729 |
-
|
| 730 |
-
# 4c. Combine the evaluators & run the base model on them
|
| 731 |
-
evaluator = SequentialEvaluator([reranking_evaluator, nano_beir_evaluator])
|
| 732 |
-
evaluator(model)
|
| 733 |
-
|
| 734 |
-
# 5. Define the training arguments
|
| 735 |
-
short_model_name = model_name if "/" not in model_name else model_name.split("/")[-1]
|
| 736 |
-
run_name = f"reranker-{short_model_name}-gooaq-bce"
|
| 737 |
-
args = CrossEncoderTrainingArguments(
|
| 738 |
-
# Required parameter:
|
| 739 |
-
output_dir=f"models/{run_name}",
|
| 740 |
-
# Optional training parameters:
|
| 741 |
-
num_train_epochs=num_epochs,
|
| 742 |
-
per_device_train_batch_size=train_batch_size,
|
| 743 |
-
per_device_eval_batch_size=train_batch_size,
|
| 744 |
-
learning_rate=2e-5,
|
| 745 |
-
warmup_ratio=0.1,
|
| 746 |
-
fp16=False, # Set to False if you get an error that your GPU can't run on FP16
|
| 747 |
-
bf16=True, # Set to True if you have a GPU that supports BF16
|
| 748 |
-
dataloader_num_workers=4,
|
| 749 |
-
load_best_model_at_end=True,
|
| 750 |
-
metric_for_best_model="eval_gooaq-dev_ndcg@10",
|
| 751 |
-
# Optional tracking/debugging parameters:
|
| 752 |
-
eval_strategy="steps",
|
| 753 |
-
eval_steps=1000,
|
| 754 |
-
save_strategy="steps",
|
| 755 |
-
save_steps=1000,
|
| 756 |
-
save_total_limit=2,
|
| 757 |
-
logging_steps=200,
|
| 758 |
-
logging_first_step=True,
|
| 759 |
-
run_name=run_name, # Will be used in W&B if `wandb` is installed
|
| 760 |
-
seed=12,
|
| 761 |
-
)
|
| 762 |
-
|
| 763 |
-
# 6. Create the trainer & start training
|
| 764 |
-
trainer = CrossEncoderTrainer(
|
| 765 |
-
model=model,
|
| 766 |
-
args=args,
|
| 767 |
-
train_dataset=hard_train_dataset,
|
| 768 |
-
loss=loss,
|
| 769 |
-
evaluator=evaluator,
|
| 770 |
-
)
|
| 771 |
-
trainer.train()
|
| 772 |
-
|
| 773 |
-
# 7. Evaluate the final model, useful to include these in the model card
|
| 774 |
-
evaluator(model)
|
| 775 |
-
|
| 776 |
-
# 8. Save the final model
|
| 777 |
-
final_output_dir = f"models/{run_name}/final"
|
| 778 |
-
model.save_pretrained(final_output_dir)
|
| 779 |
-
|
| 780 |
-
# 9. (Optional) save the model to the Hugging Face Hub!
|
| 781 |
-
# It is recommended to run `huggingface-cli login` to log into your Hugging Face account first
|
| 782 |
-
try:
|
| 783 |
-
model.push_to_hub(run_name)
|
| 784 |
-
except Exception:
|
| 785 |
-
logging.error(
|
| 786 |
-
f"Error uploading model to the Hugging Face Hub:\n{traceback.format_exc()}To upload it manually, you can run "
|
| 787 |
-
f"`huggingface-cli login`, followed by loading the model using `model = CrossEncoder({final_output_dir!r})` "
|
| 788 |
-
f"and saving it using `model.push_to_hub('{run_name}')`."
|
| 789 |
-
)
|
| 790 |
|
|
|
|
| 791 |
|
| 792 |
-
|
| 793 |
-
|
|
|
|
|
|
|
|
|
|
| 794 |
|
| 795 |
-
|
| 796 |
-
|
|
|
|
|
|
|
| 797 |
|
| 798 |
-
|
| 799 |
|
| 800 |
-
|
| 801 |
-
<summary>Click to expand decoder training code</summary>
|
| 802 |
|
| 803 |
-
|
|
|
|
| 804 |
```bash
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
--dataset_name trl-lib/Capybara \
|
| 808 |
-
--learning_rate 2.0e-5 \
|
| 809 |
-
--num_train_epochs 1 \
|
| 810 |
-
--packing \
|
| 811 |
-
--per_device_train_batch_size 2 \
|
| 812 |
-
--gradient_accumulation_steps 8 \
|
| 813 |
-
--gradient_checkpointing \
|
| 814 |
-
--eos_token '<|im_end|>' \
|
| 815 |
-
--eval_strategy steps \
|
| 816 |
-
--eval_steps 100 \
|
| 817 |
-
--output_dir ettin-decoder-17m \
|
| 818 |
-
--push_to_hub
|
| 819 |
```
|
| 820 |
|
| 821 |
-
|
| 822 |
-
|
| 823 |
-
|
| 824 |
-
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
--num_train_epochs 1 \
|
| 828 |
-
--packing \
|
| 829 |
-
--per_device_train_batch_size 2 \
|
| 830 |
-
--gradient_accumulation_steps 8 \
|
| 831 |
-
--gradient_checkpointing \
|
| 832 |
-
--eos_token '<|im_end|>' \
|
| 833 |
-
--eval_strategy steps \
|
| 834 |
-
--eval_steps 100 \
|
| 835 |
-
--use_peft \
|
| 836 |
-
--lora_r 32 \
|
| 837 |
-
--lora_alpha 16 \
|
| 838 |
-
--output_dir ettin-decoder-17m \
|
| 839 |
-
--push_to_hub
|
| 840 |
-
```
|
| 841 |
|
| 842 |
-
|
|
|
|
| 843 |
```python
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
from datasets import load_dataset
|
| 847 |
-
from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
|
| 848 |
-
from transformers.models.auto.modeling_auto import MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES
|
| 849 |
-
|
| 850 |
-
from trl import (
|
| 851 |
-
ModelConfig,
|
| 852 |
-
ScriptArguments,
|
| 853 |
-
SFTConfig,
|
| 854 |
-
SFTTrainer,
|
| 855 |
-
TrlParser,
|
| 856 |
-
clone_chat_template,
|
| 857 |
-
get_kbit_device_map,
|
| 858 |
-
get_peft_config,
|
| 859 |
-
get_quantization_config,
|
| 860 |
-
)
|
| 861 |
|
|
|
|
|
|
|
| 862 |
|
| 863 |
-
|
| 864 |
-
|
| 865 |
-
# Model init kwargs & Tokenizer
|
| 866 |
-
################
|
| 867 |
-
quantization_config = get_quantization_config(model_args)
|
| 868 |
-
model_kwargs = dict(
|
| 869 |
-
revision=model_args.model_revision,
|
| 870 |
-
trust_remote_code=model_args.trust_remote_code,
|
| 871 |
-
attn_implementation=model_args.attn_implementation,
|
| 872 |
-
torch_dtype=model_args.torch_dtype,
|
| 873 |
-
use_cache=False if training_args.gradient_checkpointing else True,
|
| 874 |
-
device_map=get_kbit_device_map() if quantization_config is not None else None,
|
| 875 |
-
quantization_config=quantization_config,
|
| 876 |
-
)
|
| 877 |
-
|
| 878 |
-
# Create model
|
| 879 |
-
config = AutoConfig.from_pretrained(model_args.model_name_or_path)
|
| 880 |
-
valid_image_text_architectures = MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES.values()
|
| 881 |
-
|
| 882 |
-
if config.architectures and any(arch in valid_image_text_architectures for arch in config.architectures):
|
| 883 |
-
from transformers import AutoModelForImageTextToText
|
| 884 |
-
|
| 885 |
-
model_kwargs.pop("use_cache", None) # Image models do not support cache
|
| 886 |
-
model = AutoModelForImageTextToText.from_pretrained(model_args.model_name_or_path, **model_kwargs)
|
| 887 |
-
else:
|
| 888 |
-
model = AutoModelForCausalLM.from_pretrained(model_args.model_name_or_path, **model_kwargs)
|
| 889 |
-
|
| 890 |
-
# Create tokenizer
|
| 891 |
-
tokenizer = AutoTokenizer.from_pretrained(
|
| 892 |
-
model_args.model_name_or_path, trust_remote_code=model_args.trust_remote_code, use_fast=True
|
| 893 |
-
)
|
| 894 |
-
|
| 895 |
-
# Set default chat template if needed
|
| 896 |
-
if tokenizer.chat_template is None:
|
| 897 |
-
# TODO: source should be passed as an argument
|
| 898 |
-
model, tokenizer = clone_chat_template(model, tokenizer, "Qwen/Qwen3-0.6B")
|
| 899 |
-
|
| 900 |
-
################
|
| 901 |
-
# Dataset
|
| 902 |
-
################
|
| 903 |
-
dataset = load_dataset(script_args.dataset_name, name=script_args.dataset_config)
|
| 904 |
-
|
| 905 |
-
################
|
| 906 |
-
# Training
|
| 907 |
-
################
|
| 908 |
-
trainer = SFTTrainer(
|
| 909 |
-
model=model,
|
| 910 |
-
args=training_args,
|
| 911 |
-
train_dataset=dataset[script_args.dataset_train_split],
|
| 912 |
-
eval_dataset=dataset[script_args.dataset_test_split] if training_args.eval_strategy != "no" else None,
|
| 913 |
-
processing_class=tokenizer,
|
| 914 |
-
peft_config=get_peft_config(model_args),
|
| 915 |
-
)
|
| 916 |
-
|
| 917 |
-
trainer.train()
|
| 918 |
-
|
| 919 |
-
# Save and push to hub
|
| 920 |
-
trainer.save_model(training_args.output_dir)
|
| 921 |
-
if training_args.push_to_hub:
|
| 922 |
-
trainer.push_to_hub(dataset_name=script_args.dataset_name)
|
| 923 |
-
|
| 924 |
-
|
| 925 |
-
def make_parser(subparsers: argparse._SubParsersAction = None):
|
| 926 |
-
dataclass_types = (ScriptArguments, SFTConfig, ModelConfig)
|
| 927 |
-
if subparsers is not None:
|
| 928 |
-
parser = subparsers.add_parser("sft", help="Run the SFT training script", dataclass_types=dataclass_types)
|
| 929 |
-
else:
|
| 930 |
-
parser = TrlParser(dataclass_types)
|
| 931 |
-
return parser
|
| 932 |
-
|
| 933 |
-
|
| 934 |
-
if __name__ == "__main__":
|
| 935 |
-
parser = make_parser()
|
| 936 |
-
# When using the trl cli, this script may be run with additional arguments, corresponding accelerate arguments.
|
| 937 |
-
# To ensure that their parsing does not interfere with the script arguments, parse the arguments with
|
| 938 |
-
# `return_remaining_strings=True`, then ignore the remaining strings.
|
| 939 |
-
script_args, training_args, model_args, _ = parser.parse_args_and_config(return_remaining_strings=True)
|
| 940 |
-
main(script_args, training_args, model_args)
|
| 941 |
|
| 942 |
-
|
| 943 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 944 |
|
| 945 |
## Citation
|
| 946 |
|
|
@@ -956,4 +405,12 @@ If you use Ettin models in your research, please cite our work:
|
|
| 956 |
primaryClass={cs.CL},
|
| 957 |
url={https://arxiv.org/abs/2507.11412},
|
| 958 |
}
|
| 959 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
|
|
|
| 2 |
language:
|
| 3 |
- en
|
| 4 |
+
license: mit
|
| 5 |
+
pipeline_tag: text-generation
|
| 6 |
+
library_name: transformers
|
| 7 |
---
|
| 8 |
+
|
| 9 |
# Ettin: an Open Suite of Paired Encoders and Decoders
|
| 10 |
|
| 11 |
[](https://opensource.org/licenses/MIT)
|
| 12 |
[](https://arxiv.org/abs/2507.11412)
|
| 13 |
+
[](https://huggingface.co/jhu-clsp)
|
| 14 |
[](https://huggingface.co/datasets/jhu-clsp)
|
| 15 |
[](https://github.com/jhu-clsp/ettin-encoder-vs-decoder)
|
| 16 |
|
| 17 |
> 🎯 **TL;DR**: State-of-the-art paired encoder and decoder models (17M-1B params) trained identically for fair comparison with open data. Encoders beat ModernBERT. Decoders beat Llama 3.2/SmolLM2.
|
| 18 |
|
| 19 |
+
📄 [Paper](https://arxiv.org/abs/2507.11412) | 🤗 [Model Collection](https://huggingface.co/jhu-clsp) | 📊 [Training Data](https://huggingface.co/datasets/jhu-clsp)
|
| 20 |
|
| 21 |
+
This repository contains the first collection of paired encoder-only and decoder-only models trained with identical data, architecture, and training recipes. Ettin enables fair comparisons between encoder and decoder architectures across multiple scales, providing state-of-the-art performance for open-data models in their respective size categories.
|
| 22 |
|
| 23 |
## Table of Contents
|
| 24 |
+
- [Performance Highlights](#-performance-highlights)
|
| 25 |
+
- [Quick Start](#-quick-start)
|
| 26 |
+
- [Model Family](#-model-family)
|
| 27 |
+
- [Getting Started](#-getting-started)
|
| 28 |
+
- [Training and Evaluation](#-training-and-evaluation)
|
| 29 |
+
- [Research Applications](#-research-applications)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
- [Training Details](#training-details)
|
| 31 |
+
- [FAQ](#-faq)
|
|
|
|
|
|
|
| 32 |
- [Citation](#citation)
|
| 33 |
|
| 34 |
## 📊 Performance Highlights
|
|
|
|
| 53 |
pip install torch>=1.9.0
|
| 54 |
# until the new pip release, install from main to use decoders (transformers>=4.54.X will contain it)
|
| 55 |
# encoders work with transformers>=4.48.0
|
| 56 |
+
pip install git+https://github.com/huggingface/transformers.git```
|
|
|
|
| 57 |
|
| 58 |
### 30-Second Examples
|
| 59 |
|
|
|
|
| 63 |
|
| 64 |
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-encoder-150m")
|
| 65 |
model = AutoModel.from_pretrained("jhu-clsp/ettin-encoder-150m")
|
| 66 |
+
|
| 67 |
+
# Example: Get embeddings
|
| 68 |
+
inputs = tokenizer("Hello world!", return_tensors="pt")
|
| 69 |
+
outputs = model(**inputs)
|
| 70 |
+
embeddings = outputs.last_hidden_state.mean(dim=1)
|
| 71 |
```
|
| 72 |
|
| 73 |
**Decoder for Text Generation:**
|
|
|
|
| 76 |
|
| 77 |
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-decoder-150m")
|
| 78 |
model = AutoModelForCausalLM.from_pretrained("jhu-clsp/ettin-decoder-150m")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
+
# Example: Generate text
|
| 81 |
+
inputs = tokenizer("The future of AI is", return_tensors="pt")
|
| 82 |
+
outputs = model.generate(**inputs, max_length=50, do_sample=True)
|
| 83 |
+
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
|
| 84 |
+
```
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
+
## 🤖 Model Family
|
| 87 |
|
| 88 |
### Encoder Models
|
| 89 |
|
|
|
|
| 127 |
**Load as decoders** using `AutoModelForCausalLM`:
|
| 128 |
|
| 129 |
| Size | Model | Parameters | Description | Download |
|
| 130 |
+
|:-----|:------|:-----------|:---------|:---------|
|
| 131 |
| XXS | [ettin-decoder-from-encoder-17m](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-17m) | 17M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-17m) |
|
| 132 |
| XS | [ettin-decoder-from-encoder-32m](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-32m) | 32M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-32m) |
|
| 133 |
| Small | [ettin-decoder-from-encoder-68m](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-68m) | 68M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-68m) |
|
| 134 |
+
| Base | [ettin-decoder-from-encoder-150m](https://huggingface.co/jhu-clsp/ettin-decoder-150m) | 150M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-150m) |
|
| 135 |
+
| Large | [ettin-decoder-from-encoder-400m](https://huggingface.co/jhu-clsp/ettin-decoder-400m) | 400M | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-400m) |
|
| 136 |
| XL | [ettin-decoder-from-encoder-1b](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-1b) | 1B | Encoder → CLM continued training | [](https://huggingface.co/jhu-clsp/ettin-decoder-from-encoder-1b) |
|
| 137 |
|
| 138 |
+
### Accessing Training Checkpoints
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
+
Beyond the final models, we provide access to intermediate training checkpoints for research and analysis purposes. All raw training checkpoints are available in the [jhu-clsp/ettin-checkpoints](https://huggingface.co/datasets/jhu-clsp/ettin-checkpoints) dataset.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
Each model repository contains multiple tagged versions representing different training stages:
|
| 143 |
|
| 144 |
- **`step{number}`** - Pretraining phase checkpoints (e.g., `step599525`, `step596528`)
|
|
|
|
| 146 |
- **`decay{number}`** - Decay phase checkpoints (e.g., `decay100`, `decay500`)
|
| 147 |
|
| 148 |
```python
|
| 149 |
+
from transformers import AutoModelForCausalLM
|
| 150 |
|
| 151 |
# Load a specific pretraining checkpoint
|
| 152 |
model = AutoModelForCausalLM.from_pretrained(
|
| 153 |
"jhu-clsp/ettin-decoder-400m",
|
| 154 |
revision="step590532" # Specific checkpoint tag
|
| 155 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
```
|
| 157 |
|
| 158 |
+
## Getting Started
|
| 159 |
|
| 160 |
+
### Training Data
|
| 161 |
|
| 162 |
+
The complete training dataset is publicly available:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
+
- **Pre-training Data**: [jhu-clsp/ettin-pretraining-data](https://huggingface.co/datasets/jhu-clsp/ettin-pretraining-data) - 1.7T tokens
|
| 165 |
+
- **Mid-training Data**: [jhu-clsp/ettin-extension-data](https://huggingface.co/datasets/jhu-clsp/ettin-extension-data) - 250B tokens
|
| 166 |
+
- **Decay Phase Data**: [jhu-clsp/ettin-decay-data](https://huggingface.co/datasets/jhu-clsp/ettin-decay-data) - 100B tokens
|
| 167 |
+
- **Training Order**: [jhu-clsp/ettin-data-order](https://huggingface.co/datasets/jhu-clsp/ettin-data-order) - Batch-level training order
|
| 168 |
|
| 169 |
+
### Usage Examples
|
| 170 |
|
|
|
|
|
|
|
|
|
|
| 171 |
<details>
|
| 172 |
+
<summary><strong>Encoder: Masked Language Modeling</strong></summary>
|
| 173 |
|
| 174 |
```python
|
| 175 |
from transformers import AutoTokenizer, AutoModelForMaskedLM
|
|
|
|
| 200 |
|
| 201 |
</details>
|
| 202 |
|
|
|
|
|
|
|
| 203 |
<details>
|
| 204 |
+
<summary><strong>Decoder: Text Generation</strong></summary>
|
| 205 |
|
| 206 |
```python
|
| 207 |
from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
|
|
| 238 |
|
| 239 |
</details>
|
| 240 |
|
| 241 |
+
<details>
|
| 242 |
+
<summary><strong>Cross-Objective Models</strong></summary>
|
|
|
|
|
|
|
|
|
|
| 243 |
|
| 244 |
```python
|
| 245 |
+
# Encoder-from-decoder: Load as encoder
|
| 246 |
+
from transformers import AutoTokenizer, AutoModel
|
| 247 |
+
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-encoder-from-decoder-150m")
|
| 248 |
+
model = AutoModel.from_pretrained("jhu-clsp/ettin-encoder-from-decoder-150m")
|
| 249 |
|
| 250 |
+
# Decoder-from-encoder: Load as decoder
|
| 251 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 252 |
+
tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/ettin-decoder-from-encoder-150m")
|
| 253 |
+
model = AutoModelForCausalLM.from_pretrained("jhu-clsp/ettin-decoder-from-encoder-150m")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
```
|
| 255 |
+
|
| 256 |
</details>
|
| 257 |
|
| 258 |
+
## 📋 Training and Evaluation
|
| 259 |
|
| 260 |
+
### Pre-training
|
| 261 |
+
For details on model pre-training, data preparation, and training recipes:
|
| 262 |
+
- **📖 [Pre-training Guide](pretraining/README.md)** - Complete training setup, data mixture, and ModernBERT recipe adaptation
|
| 263 |
|
| 264 |
+
### Evaluation
|
| 265 |
+
|
| 266 |
+
#### Encoder Evaluation
|
| 267 |
+
- **📊 [Encoder on Generative Tasks](docs/encoder-generative-eval.md)** - Evaluating encoders on language modeling tasks using our lm-evaluation-harness fork
|
| 268 |
+
- **🔍 [Encoder Retrieval Training](docs/retrieval.md)** - Fine-tuning on MS MARCO and evaluation on MTEB v2 English
|
| 269 |
+
- **🎯 [GLUE Evaluation](glue_evaluation/README.md)** - Comprehensive GLUE benchmark evaluation with fine-tuning scripts
|
| 270 |
+
|
| 271 |
+
#### Decoder Evaluation
|
| 272 |
+
- **🎯 [Decoder on Generative Tasks](docs/decoder-eval.md)** - Using EleutherAI evaluation harness (commit `867413f8677f00f6a817262727cbb041bf36192a`) for comprehensive generative task evaluation
|
| 273 |
+
|
| 274 |
+
#### Bias Evaluation
|
| 275 |
+
- **⚖️ [Gender Bias Evaluation](bias_eval/README.md)** - Comprehensive gender bias testing using Winogender dataset gotcha examples. Tests how well models handle counter-stereotypical pronouns in occupational contexts. Supports both encoder (MLM) and decoder (perplexity) evaluation methods.
|
| 276 |
|
| 277 |
+
### Quick Decoder Evaluation Example
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
|
| 279 |
+
```bash
|
| 280 |
+
# Clone the specific commit of lm-evaluation-harness
|
| 281 |
+
git clone https://github.com/EleutherAI/lm-evaluation-harness.git
|
| 282 |
+
cd lm-evaluation-harness
|
| 283 |
+
git checkout 867413f8677f00f6a817262727cbb041bf36192a
|
| 284 |
+
pip install -e .
|
| 285 |
+
|
| 286 |
+
# Run evaluation on Ettin decoder
|
| 287 |
+
lm_eval --model hf \
|
| 288 |
+
--model_args pretrained=jhu-clsp/ettin-decoder-150m \
|
| 289 |
+
--tasks hellaswag,arc_easy,arc_challenge,winogrande \
|
| 290 |
+
--device cuda:0 \
|
| 291 |
+
--batch_size 8
|
| 292 |
```
|
|
|
|
| 293 |
|
| 294 |
+
## 🔬 Research Applications
|
| 295 |
|
| 296 |
+
### What Makes Ettin Unique
|
|
|
|
| 297 |
|
| 298 |
+
Ettin provides the first **controlled comparison** of encoder vs. decoder architectures:
|
| 299 |
|
| 300 |
+
- **Identical Training Data**: Same 2T token mixture across all models
|
| 301 |
+
- **Matched Architectures**: Only attention patterns and objectives differ
|
| 302 |
+
- **Open Everything**: Training data, model weights, and batch-level training order
|
| 303 |
+
- **Multiple Scales**: Fair comparison from 17M to 1B parameters
|
| 304 |
+
- **250+ Checkpoints**: Complete training trajectory analysis
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
|
| 306 |
+
### Key Research Findings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
|
| 308 |
+
1. **Architecture Specialization Persists**:
|
| 309 |
+
- Encoders excel at classification/retrieval even vs. larger decoders
|
| 310 |
+
- Decoders excel at generation even vs. larger encoders
|
| 311 |
+
- A 400M encoder beats a 1B decoder on MNLI (89.2 vs 88.2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
|
| 313 |
+
2. **Cross-Training Limitations**:
|
| 314 |
+
- Converting decoder→encoder or encoder→decoder underperforms
|
| 315 |
+
- 50B tokens of continued training insufficient to close gaps
|
| 316 |
+
- Native training objective remains superior
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
|
| 318 |
+
3. **Scaling Insights**:
|
| 319 |
+
- Performance gaps between architectures widen with size
|
| 320 |
+
- Decoder-from-encoder adaptation scales particularly poorly
|
| 321 |
|
| 322 |
+
### Use Cases for Researchers
|
|
|
|
| 323 |
|
| 324 |
+
- **Architecture Studies**: Compare encoder vs decoder capabilities fairly
|
| 325 |
+
- **Training Dynamics**: Analyze 250+ checkpoints with batch-level data ordering
|
| 326 |
+
- **Scaling Laws**: Study how architectural advantages change with scale
|
| 327 |
+
- **Transfer Learning**: Investigate cross-objective training effectiveness
|
| 328 |
+
- **Replication Studies**: First open replication of ModernBERT training recipe
|
| 329 |
|
| 330 |
+
## Training Details
|
|
|
|
| 331 |
|
| 332 |
+
### Model Architecture
|
| 333 |
|
| 334 |
+
| Parameter | 17M | 32M | 68M | 150M | 400M | 1B |
|
| 335 |
+
|:----------|:----|:----|:----|:-----|:-----|:---|
|
| 336 |
+
| Layers | 7 | 10 | 19 | 22 | 28 | 28 |
|
| 337 |
+
| Hidden Size | 256 | 384 | 512 | 768 | 1024 | 1792 |
|
| 338 |
+
| Intermediate Size | 384 | 576 | 768 | 1152 | 2624 | 3840 |
|
| 339 |
+
| Attention Heads | 4 | 6 | 8 | 12 | 16 | 28 |
|
| 340 |
|
| 341 |
+
### Training Configuration
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
|
| 343 |
+
**Data:** High-quality mixture including DCLM, Dolma v1.7, scientific papers, code, and curated sources totaling 2T+ tokens
|
| 344 |
|
| 345 |
+
**Architecture Features:**
|
| 346 |
+
- Transformer with RoPE, GLU activations, and prenorm layers
|
| 347 |
+
- Context length: Up to 8K tokens
|
| 348 |
+
- Vocabulary: 50,368 tokens (ModernBERT tokenizer)
|
| 349 |
+
- Deep but efficient architectures following MobileLLM principles
|
| 350 |
|
| 351 |
+
**Training Phases:**
|
| 352 |
+
- **Pre-training**: 1.7T tokens with diverse data mixture
|
| 353 |
+
- **Mid-training**: 250B tokens with higher-quality filtered data and context extension to 8K
|
| 354 |
+
- **Decay phase**: 100B tokens with premium data sources
|
| 355 |
|
| 356 |
+
## ❓ FAQ
|
| 357 |
|
| 358 |
+
### Model Loading Issues
|
|
|
|
| 359 |
|
| 360 |
+
**Q: I'm getting an error that ModernBERT-decoder isn't found.**
|
| 361 |
+
**A:** Make sure you have the latest version of transformers installed:
|
| 362 |
```bash
|
| 363 |
+
# for the latest version until the official pypi release:
|
| 364 |
+
pip install git+https://github.com/huggingface/transformers.git
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
```
|
| 366 |
|
| 367 |
+
**Q: Which model should I choose for my task?**
|
| 368 |
+
**A:**
|
| 369 |
+
- **Classification/Retrieval/Understanding**: Use encoder models
|
| 370 |
+
- **Text Generation/Chat/Completion**: Use decoder models
|
| 371 |
+
- **Research on cross-training**: Use cross-objective models
|
| 372 |
+
- **Size selection**: Start with 150M for experimentation, scale up to 400M or 1B for production
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
|
| 374 |
+
**Q: How do I access training checkpoints?**
|
| 375 |
+
**A:** Each model has multiple git tags for different training stages. Use the `revision` parameter:
|
| 376 |
```python
|
| 377 |
+
model = AutoModel.from_pretrained("jhu-clsp/ettin-encoder-150m", revision="step500000")
|
| 378 |
+
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
|
| 380 |
+
**Q: Can I continue training these models?**
|
| 381 |
+
**A:** Yes! We provide raw checkpoints in the [jhu-clsp/ettin-checkpoints](https://huggingface.co/datasets/jhu-clsp/ettin-checkpoints) dataset that can be loaded into training frameworks.
|
| 382 |
|
| 383 |
+
**Q: What's the difference between cross-objective models and regular models?**
|
| 384 |
+
**A:** Cross-objective models started as one architecture (e.g., decoder) and were continued with a different objective (e.g., MLM). They demonstrate the limitations of cross-training and generally underperform native models.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 385 |
|
| 386 |
+
**Q: How do I reproduce the paper results?**
|
| 387 |
+
**A:** See our evaluation guides:
|
| 388 |
+
- [Encoder Generative Eval](docs/encoder-generative-eval.md)
|
| 389 |
+
- [Retrieval Eval](docs/retrieval.md)
|
| 390 |
+
- [GLUE Eval](glue_evaluation/README.md)
|
| 391 |
+
- [Decoder Eval](docs/decoder-eval.md)
|
| 392 |
+
- [Pre-training](pretraining/README.md)
|
| 393 |
|
| 394 |
## Citation
|
| 395 |
|
|
|
|
| 405 |
primaryClass={cs.CL},
|
| 406 |
url={https://arxiv.org/abs/2507.11412},
|
| 407 |
}
|
| 408 |
+
```
|
| 409 |
+
|
| 410 |
+
## License
|
| 411 |
+
|
| 412 |
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
| 413 |
+
|
| 414 |
+
---
|
| 415 |
+
|
| 416 |
+
**Contact**: For questions about the models or research, please open an issue or contact the authors.
|