TL;DR: A state-of-the-art multilingual encoder trained on 3T+ tokens across 1800+ languages, introducing novel techniques for learning low-resource languages during the decay phase.
1pip install torch>=1.9.0
2pip install transformers>=4.21.01from transformers import AutoTokenizer, AutoModel
2
3tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/mmBERT-base")
4model = AutoModel.from_pretrained("jhu-clsp/mmBERT-base")
5
6inputs = tokenizer("Hello world", return_tensors="pt")
7outputs = model(**inputs)| Model | Total Params | Non-embed Params | Languages | Download |
|---|---|---|---|---|
| mmBERT-small | 140M | 42M | 1800+ | |
| mmBERT-base | 307M | 110M | 1800+ |
| Phase | Dataset | Tokens | Description |
|---|---|---|---|
| Pre-training P1 | mmbert-pretrain-p1 | 2.3T | 60 languages, foundational training |
| Pre-training P2 | mmbert-pretrain-p2 | - | Extension data for pre-training phase |
| Pre-training P3 | mmbert-pretrain-p3 | - | Final pre-training data |
| Mid-training | mmbert-midtraining | 600B | 110 languages, context extension to 8K |
| Decay Phase | mmbert-decay | 100B | 1833 languages, premium quality |
| Parameter | mmBERT-small | mmBERT-base |
|---|---|---|
| Layers | 22 | 22 |
| Hidden Size | 384 | 768 |
| Intermediate Size | 1152 | 1152 |
| Attention Heads | 6 | 12 |
| Total Parameters | 140M | 307M |
| Non-embedding Parameters | 42M | 110M |
| Max Sequence Length | 8192 | 8192 |
| Vocabulary Size | 256,000 | 256,000 |
| Tokenizer | Gemma 2 | Gemma 2 |
1from transformers import AutoTokenizer, AutoModelForMaskedLM
2import torch
3
4tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/mmBERT-base")
5model = AutoModelForMaskedLM.from_pretrained("jhu-clsp/mmBERT-base")
6
7def predict_masked_token(text):
8 inputs = tokenizer(text, return_tensors="pt")
9 with torch.no_grad():
10 outputs = model(**inputs)
11
12 mask_indices = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)
13 predictions = outputs.logits[mask_indices]
14 top_tokens = torch.topk(predictions, 5, dim=-1)
15
16 return [tokenizer.decode(token) for token in top_tokens.indices[0]]
17
18# Works across languages
19texts = [
20 "The capital of France is <mask>.",
21 "La capital de España es <mask>.",
22 "Die Hauptstadt von Deutschland ist <mask>."
23]
24
25for text in texts:
26 predictions = predict_masked_token(text)
27 print(f"Text: {text}")
28 print(f"Predictions: {predictions}")1from transformers import AutoTokenizer, AutoModel
2import torch
3from sklearn.metrics.pairwise import cosine_similarity
4
5tokenizer = AutoTokenizer.from_pretrained("jhu-clsp/mmBERT-base")
6model = AutoModel.from_pretrained("jhu-clsp/mmBERT-base")
7
8def get_embeddings(texts):
9 inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
10
11 with torch.no_grad():
12 outputs = model(**inputs)
13 embeddings = outputs.last_hidden_state.mean(dim=1)
14
15 return embeddings.numpy()
16
17multilingual_texts = [
18 "Artificial intelligence is transforming technology",
19 "La inteligencia artificial está transformando la tecnología",
20 "L'intelligence artificielle transforme la technologie",
21 "人工智能正在改变技术"
22]
23
24embeddings = get_embeddings(multilingual_texts)
25similarities = cosine_similarity(embeddings)
26print("Cross-lingual similarity matrix:")
27print(similarities)1import argparse
2from datasets import load_dataset
3from sentence_transformers import (
4 SentenceTransformer,
5 SentenceTransformerTrainer,
6 SentenceTransformerTrainingArguments,
7)
8from sentence_transformers.evaluation import TripletEvaluator
9from sentence_transformers.losses import CachedMultipleNegativesRankingLoss
10from sentence_transformers.training_args import BatchSamplers
11
12def main():
13 parser = argparse.ArgumentParser()
14 parser.add_argument("--lr", type=float, default=8e-5)
15 parser.add_argument("--model_name", type=str, default="jhu-clsp/mmBERT-base")
16 args = parser.parse_args()
17
18 lr = args.lr
19 model_name = args.model_name
20 model_shortname = model_name.split("/")[-1]
21
22 model = SentenceTransformer(model_name)
23
24 dataset = load_dataset(
25 "sentence-transformers/msmarco-co-condenser-margin-mse-sym-mnrl-mean-v1",
26 "triplet-hard",
27 split="train",
28 )
29 dataset_dict = dataset.train_test_split(test_size=1_000, seed=12)
30 train_dataset = dataset_dict["train"].select(range(1_250_000))
31 eval_dataset = dataset_dict["test"]
32
33 loss = CachedMultipleNegativesRankingLoss(model, mini_batch_size=16)
34 run_name = f"{model_shortname}-DPR-{lr}"
35
36 training_args = SentenceTransformerTrainingArguments(
37 output_dir=f"output/{model_shortname}/{run_name}",
38 num_train_epochs=1,
39 per_device_train_batch_size=512,
40 per_device_eval_batch_size=512,
41 warmup_ratio=0.05,
42 fp16=False,
43 bf16=True,
44 batch_sampler=BatchSamplers.NO_DUPLICATES,
45 learning_rate=lr,
46 save_strategy="steps",
47 save_steps=500,
48 save_total_limit=2,
49 logging_steps=500,
50 run_name=run_name,
51 )
52
53 dev_evaluator = TripletEvaluator(
54 anchors=eval_dataset["query"],
55 positives=eval_dataset["positive"],
56 negatives=eval_dataset["negative"],
57 name="msmarco-co-condenser-dev",
58 )
59 dev_evaluator(model)
60
61 trainer = SentenceTransformerTrainer(
62 model=model,
63 args=training_args,
64 train_dataset=train_dataset,
65 eval_dataset=eval_dataset,
66 loss=loss,
67 evaluator=dev_evaluator,
68 )
69 trainer.train()
70
71 model.save_pretrained(f"output/{model_shortname}/{run_name}/final")
72 model.push_to_hub(run_name, private=False)
73
74if __name__ == "__main__":
75 main()1from transformers import (
2 AutoTokenizer,
3 AutoModelForSequenceClassification,
4 TrainingArguments,
5 Trainer
6)
7from datasets import load_dataset
8import numpy as np
9from sklearn.metrics import accuracy_score, f1_score
10
11def compute_metrics(eval_pred):
12 predictions, labels = eval_pred
13 predictions = np.argmax(predictions, axis=1)
14 return {
15 'accuracy': accuracy_score(labels, predictions),
16 'f1': f1_score(labels, predictions, average='weighted')
17 }
18
19def main():
20 model_name = "jhu-clsp/mmBERT-base"
21
22 tokenizer = AutoTokenizer.from_pretrained(model_name)
23 model = AutoModelForSequenceClassification.from_pretrained(
24 model_name,
25 num_labels=3
26 )
27
28 dataset = load_dataset("xnli", "all_languages")
29
30 def tokenize_function(examples):
31 texts = [f"{p} {tokenizer.sep_token} {h}"
32 for p, h in zip(examples["premise"], examples["hypothesis"])]
33
34 return tokenizer(
35 texts,
36 truncation=True,
37 padding=True,
38 max_length=512
39 )
40
41 train_dataset = dataset["train"].map(tokenize_function, batched=True)
42 eval_dataset = dataset["validation"].map(tokenize_function, batched=True)
43
44 training_args = TrainingArguments(
45 output_dir="./mmbert-xnli",
46 learning_rate=3e-5,
47 per_device_train_batch_size=32,
48 per_device_eval_batch_size=32,
49 num_train_epochs=3,
50 weight_decay=0.01,
51 evaluation_strategy="epoch",
52 save_strategy="epoch",
53 load_best_model_at_end=True,
54 metric_for_best_model="f1",
55 greater_is_better=True,
56 )
57
58 trainer = Trainer(
59 model=model,
60 args=training_args,
61 train_dataset=train_dataset,
62 eval_dataset=eval_dataset,
63 compute_metrics=compute_metrics,
64 )
65
66 trainer.train()
67
68if __name__ == "__main__":
69 main()1import logging
2from datasets import load_dataset
3from sentence_transformers.cross_encoder import (
4 CrossEncoder,
5 CrossEncoderModelCardData,
6 CrossEncoderTrainer,
7 CrossEncoderTrainingArguments,
8)
9from sentence_transformers.cross_encoder.evaluation import CrossEncoderNanoBEIREvaluator
10from sentence_transformers.cross_encoder.losses import BinaryCrossEntropyLoss
11from sentence_transformers.util import mine_hard_negatives
12from sentence_transformers import SentenceTransformer
13import torch
14
15def main():
16 model_name = "jhu-clsp/mmBERT-base"
17 train_batch_size = 32
18 num_epochs = 2
19 num_hard_negatives = 7
20
21 model = CrossEncoder(
22 model_name,
23 model_card_data=CrossEncoderModelCardData(
24 language="multilingual",
25 license="mit",
26 ),
27 )
28
29 full_dataset = load_dataset("sentence-transformers/gooaq", split="train").select(range(50_000))
30 dataset_dict = full_dataset.train_test_split(test_size=1_000, seed=42)
31 train_dataset = dataset_dict["train"]
32 eval_dataset = dataset_dict["test"]
33
34 embedding_model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2", device="cpu")
35 hard_train_dataset = mine_hard_negatives(
36 train_dataset,
37 embedding_model,
38 num_negatives=num_hard_negatives,
39 margin=0,
40 range_min=0,
41 range_max=100,
42 sampling_strategy="top",
43 batch_size=2048,
44 output_format="labeled-pair",
45 use_faiss=True,
46 )
47
48 loss = BinaryCrossEntropyLoss(model=model, pos_weight=torch.tensor(num_hard_negatives))
49
50 nano_beir_evaluator = CrossEncoderNanoBEIREvaluator(
51 dataset_names=["msmarco", "nfcorpus", "nq"],
52 batch_size=train_batch_size,
53 )
54
55 args = CrossEncoderTrainingArguments(
56 output_dir="./mmbert-reranker",
57 num_train_epochs=num_epochs,
58 per_device_train_batch_size=train_batch_size,
59 per_device_eval_batch_size=train_batch_size,
60 learning_rate=2e-5,
61 warmup_ratio=0.1,
62 fp16=False,
63 bf16=True,
64 dataloader_num_workers=4,
65 load_best_model_at_end=True,
66 metric_for_best_model="eval_msmarco_ndcg@10",
67 eval_strategy="steps",
68 eval_steps=1000,
69 save_strategy="steps",
70 save_steps=1000,
71 save_total_limit=2,
72 logging_steps=200,
73 seed=42,
74 )
75
76 trainer = CrossEncoderTrainer(
77 model=model,
78 args=args,
79 train_dataset=hard_train_dataset,
80 loss=loss,
81 evaluator=nano_beir_evaluator,
82 )
83 trainer.train()
84
85 model.save_pretrained("./mmbert-reranker/final")
86
87if __name__ == "__main__":
88 main()| Phase | Dataset | Description |
|---|---|---|
| Pre-training P1 | 2.3T tokens | 60 languages, diverse data mixture |
| Pre-training P2 | - | Extension data for pre-training |
| Pre-training P3 | - | Final pre-training data |
| Mid-training | 600B tokens | 110 languages, context extension |
| Decay Phase | 100B tokens | 1833 languages, premium quality |
1@misc{marone2025mmbertmodernmultilingualencoder,
2 title={mmBERT: A Modern Multilingual Encoder with Annealed Language Learning},
3 author={Marc Marone and Orion Weller and William Fleshman and Eugene Yang and Dawn Lawrie and Benjamin Van Durme},
4 year={2025},
5 eprint={2509.06888},
6 archivePrefix={arXiv},
7 primaryClass={cs.CL},
8 url={https://arxiv.org/abs/2509.06888},
9}