The key idea behind this model is symmetric data augmentation. During training, every sentence
pair (A, B, score) is duplicated as (B, A, score). This forces the model to learn that
similarity is bidirectional: similarity(A, B) = similarity(B, A). This simple technique
produces consistent gains across all historical STS benchmarks.
The model has 22.7 million parameters, runs fast on CPU, and fits comfortably on consumer GPUs.
This is NOT a general-purpose embedding model.
It is built for one job: measuring how similar two sentences are in meaning.
It is not optimized for document retrieval, classification, or multilingual tasks.
2. Model Architecture
Property
Value
Base Architecture
MiniLM (6-layer Transformer encoder)
Total Parameters
22.7 M
Hidden Dimension
384
Output Embedding Dimension
384
Max Sequence Length
256 tokens
Pooling Strategy
Mean Pooling
Normalization
L2-normalized embeddings
Similarity Function
Cosine Similarity
3. Intended Use
Good For
Not Built For
Semantic similarity scoring between two sentences
Document retrieval or search ranking over large corpora
Paraphrase and duplicate question detection
Sentiment analysis or text classification
Small-scale text clustering by meaning
Zero-shot classification
FAQ matching and chatbot intent matching
Multilingual or cross-lingual tasks
Sentence-level deduplication pipelines
Long-document embedding beyond 256 tokens
4. Benchmark Results
All scores are Spearman rank correlation measured locally using the
MTEB library on official test splits.
4.1 STS Results vs Base Model
Task
Base MiniLM-L6-v2
SymSTS-MiniLM-L6 (Ours)
Difference
STS12
0.7237
0.7873
+0.0636
STS13
0.8060
0.8290
+0.0230
STS14
0.7559
0.8186
+0.0627
STS15
0.8539
0.8758
+0.0219
STS16
0.7899
0.8152
+0.0253
STSBenchmark
0.8203
0.8407
+0.0204
SICK-R
0.7758
0.7772
+0.0013
Average
0.7894
0.8205
+0.0312
4.2 Honest Comparison Against Other Models
Scores for external models are approximate, taken from the MTEB leaderboard and published
model cards. Our scores are measured locally. We include models that outperform ours
for full transparency.
Model
Params
STS-B Spearman
STS Avg (approx)
vs SymSTS
SymSTS-MiniLM-L6 (Ours)
22.7M
0.8407
0.8205
--
all-MiniLM-L6-v2
22.7M
0.8203
0.7894
SymSTS wins
all-MiniLM-L12-v2
33M
~0.835
~0.805
SymSTS wins
all-mpnet-base-v2
109M
~0.835
~0.810
SymSTS wins
bge-small-en-v1.5
33M
~0.815
~0.800
SymSTS wins
e5-small-v2
33M
~0.820
~0.805
SymSTS wins
gte-small
33M
~0.840
~0.815
Comparable
bge-base-en-v1.5
110M
~0.855
~0.835
They win
gte-base
110M
~0.855
~0.840
They win
nomic-embed-text-v1.5
137M
~0.865
~0.845
They win
jina-embeddings-v3
570M
~0.870
~0.855
They win
e5-mistral-7b-instruct
7B
~0.880
~0.860
They win
Summary: SymSTS-MiniLM-L6 outperforms all models at or below 33M parameters and several
models up to 109M parameters on STS tasks. It is outperformed by larger base-architecture
models (110M+) and LLM-based embeddings, which is expected given the parameter gap.
5. Training Details
5.1 Loss Function
CosineSimilarityLoss — minimizes the mean squared error between the predicted cosine
similarity and the human-annotated similarity score.
5.2 Symmetric Data Augmentation
For every training pair:
Original: (Sentence_A, Sentence_B, score)
Augmented: (Sentence_B, Sentence_A, score)
Both are included, doubling the effective training set from ~8k to ~16k pairs. Note: because cosine similarity is symmetric by construction (sim(A,B) = sim(B,A) regardless of training), this augmentation's main effect is increasing the volume of training pairs rather than teaching the model a new bidirectional property. The performance gains reported in Section 4 are real and measured directly; the ablation isolating "more data" from "swap specifically" is planned as future work.
5.3 Hyperparameters
Hyperparameter
Value
Learning Rate
1e-5
Batch Size
16
Epochs
2
Warmup
10% of total steps
Weight Decay
0.01
Optimizer
AdamW
Precision
FP16 (mixed precision)
Random Seed
42
Hardware
NVIDIA GeForce GTX 1660 SUPER (6 GB VRAM)
6. Training Data
The model was fine-tuned on the train splits of the following datasets.
STS13 through STS16 and SICK-R were not used during training. They are held-out
evaluation benchmarks only.
Dataset
Source
Original Pairs
After Augmentation
License
STS Benchmark (train)
SemEval-2017 Task 1
5,749
11,498
Research use
STS12 (train)
SemEval-2012 Task 6
2,234
4,468
Research use
Total
7,983
15,966
The raw dataset files are not redistributed in this repository.
Only the fine-tuned model weights are provided.
7. Usage
python
1from sentence_transformers import SentenceTransformer, util
23model = SentenceTransformer("blueprint-ai/SymSTS-MiniLM")45sentences =[6"The cat sits on the mat.",7"A feline is resting on the rug.",8"The stock market crashed today."9]1011embeddings = model.encode(sentences, normalize_embeddings=True)1213sim_01 = util.cos_sim(embeddings[0], embeddings[1]).item()14sim_02 = util.cos_sim(embeddings[0], embeddings[2]).item()1516print(f"Cat vs Feline: {sim_01:.4f}")17print(f"Cat vs Stocks: {sim_02:.4f}")18#8. Full Training Code19This is the exact script used to train SymSTS-MiniLM-L6.20import warnings
21warnings.filterwarnings("ignore")2223import os
24import torch
25from datasets import load_dataset, Dataset
2627from sentence_transformers import(28 SentenceTransformer,29 SentenceTransformerTrainer,30 SentenceTransformerTrainingArguments,31 InputExample,32)33from sentence_transformers.losses import CosineSimilarityLoss
34from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator
353637# =============================================================38# Configuration39# =============================================================40BASE_MODEL ="sentence-transformers/all-MiniLM-L6-v2"41OUTPUT_DIR ="./output/symsts-minilm-l6"42RUNS_DIR ="./output/runs-symsts-minilm-l6"43MAX_SEQ_LEN =25644BATCH_SIZE =1645LEARNING_RATE =1e-546EPOCHS =24748device ="cuda"if torch.cuda.is_available()else"cpu"49print(f"Device: {device}")505152# =============================================================53# Helpers54# =============================================================55defclean_text(text):56if text isNone:57return""58return" ".join(str(text).strip().split())596061defget_main_score(result):62ifisinstance(result,dict):63for key, value in result.items():64if"spearman_cosine"in key:65try:returnfloat(value)66except Exception:pass67for key, value in result.items():68if"spearman"in key:69try:returnfloat(value)70except Exception:pass71for value in result.values():72try:returnfloat(value)73except Exception:pass74try:returnfloat(result)75except Exception:return0.0767778# =============================================================79# 1. Load Base Model80# =============================================================81print("Loading base model...")82student = SentenceTransformer(BASE_MODEL, device=device)83student.max_seq_length = MAX_SEQ_LEN
848586# =============================================================87# 2. Load STS Training Data with Symmetric Augmentation88# =============================================================89print("Loading STS training data...")9091sources =[92("mteb/stsbenchmark-sts","train"),93("mteb/sts12-sts","train"),94]9596sentence1_list =[]97sentence2_list =[]98label_list =[]99seen_pairs =set()100101102defadd_pair(s1, s2, score):103 s1 = clean_text(s1)104 s2 = clean_text(s2)105ifnot s1 ornot s2:106return107108 key =tuple(sorted((s1.lower(), s2.lower())))109if key in seen_pairs:110return111 seen_pairs.add(key)112113# Forward114 sentence1_list.append(s1)115 sentence2_list.append(s2)116 label_list.append(score)117118# Reverse (Symmetric Augmentation)119if s1.lower()!= s2.lower():120 sentence1_list.append(s2)121 sentence2_list.append(s1)122 label_list.append(score)123124125for dataset_name, split in sources:126try:127 ds = load_dataset(dataset_name, split=split)128 count =0129for row in ds:130 s1 = row.get("sentence1")131 s2 = row.get("sentence2")132 score = row.get("score",0.0)133try:134 score =float(score)135except Exception:136continue137if score >1.0:138 score = score /5.0139 score =max(0.0,min(1.0, score))140 add_pair(s1, s2, score)141 count +=1142print(f" Loaded {count} original pairs from {dataset_name}")143except Exception as e:144print(f" Skipping {dataset_name}: {e}")145146print(f"Unique pairs: {len(seen_pairs)}")147print(f"Total augmented rows: {len(sentence1_list)}")148149train_dataset = Dataset.from_dict({150"sentence1": sentence1_list,151"sentence2": sentence2_list,152"label": label_list,153})154155156# =============================================================157# 3. STS-B Test Evaluator158# =============================================================159print("Loading STS-B test evaluator...")160161sts_test = load_dataset("mteb/stsbenchmark-sts", split="test")162eval_examples =[]163for row in sts_test:164 s1 = clean_text(row.get("sentence1"))165 s2 = clean_text(row.get("sentence2"))166 score =float(row.get("score",0.0))/5.0167 eval_examples.append(InputExample(texts=[s1, s2], label=score))168169evaluator = EmbeddingSimilarityEvaluator.from_input_examples(170 eval_examples,171 name="sts-b-test",172)173174base_score = get_main_score(evaluator(student))175print(f"Base model STS-B score: {base_score:.4f}")176177178# =============================================================179# 4. Train180# =============================================================181print("Training SymSTS-MiniLM-L6...")182183train_loss = CosineSimilarityLoss(model=student)184185training_args = SentenceTransformerTrainingArguments(186 output_dir = RUNS_DIR,187 num_train_epochs = EPOCHS,188 per_device_train_batch_size = BATCH_SIZE,189 per_device_eval_batch_size = BATCH_SIZE,190 learning_rate = LEARNING_RATE,191 warmup_steps =0.1,192 weight_decay =0.01,193 fp16 = torch.cuda.is_available(),194 bf16 =False,195 logging_steps =50,196 save_strategy ="no",197 eval_strategy ="epoch",198 dataloader_num_workers =0,199 report_to ="none",200 remove_unused_columns =False,201 seed =42,202)203204trainer = SentenceTransformerTrainer(205 model = student,206 args = training_args,207 train_dataset = train_dataset,208 loss = train_loss,209 evaluator = evaluator,210)211212trainer.train()213214os.makedirs(OUTPUT_DIR, exist_ok=True)215try:216 trainer.save_model(OUTPUT_DIR)217except Exception:218 student.save_pretrained(OUTPUT_DIR)219220print(f"Model saved to: {OUTPUT_DIR}")221222223# =============================================================224# 5. Final Evaluation225# =============================================================226final_score = get_main_score(evaluator(student))227228print("="*50)229print(f"Base model STS-B: {base_score:.4f}")230print(f"SymSTS STS-B: {final_score:.4f}")231print(f"Improvement: {final_score - base_score:+.4f}")232print("="*50)233234#9. Evaluation Code235This is the script used to produce the benchmark tables above.236import warnings
237warnings.filterwarnings("ignore")238239import os, json, glob, mteb
240from sentence_transformers import SentenceTransformer
241242os.environ["TOKENIZERS_PARALLELISM"]="false"243244TASKS =[245"STS12","STS13","STS14","STS15",246"STS16","STSBenchmark","SICK-R",247]248249defget_scores(model_path, out_dir):250 model = SentenceTransformer(model_path)251 tasks = mteb.get_tasks(tasks=TASKS)252 evaluation = mteb.MTEB(tasks=tasks)253 evaluation.run(model, output_folder=out_dir, verbosity=0)254255 scores ={}256for task_name in TASKS:257 files = glob.glob(f"{out_dir}/**/*{task_name}*.json", recursive=True)258if files:259withopen(files[0])as f:260 d = json.load(f)261if"scores"in d and"test"in d["scores"]:262 scores[task_name]= d["scores"]["test"][0].get("main_score",0.0)263return scores
264265base = get_scores("sentence-transformers/all-MiniLM-L6-v2","./mteb_base")266ours = get_scores("./output/symsts-minilm-l6","./mteb_ours")267268print(f"{'Task':<16}{'Base':>8}{'Ours':>8}{'Diff':>8}")269print("-"*44)270for t in TASKS:271 b = base.get(t,0)272 o = ours.get(t,0)273print(f"{t:<16}{b:>8.4f}{o:>8.4f}{o-b:>+8.4f}")
10. Limitations
Trained on a small curated dataset (~16,000 augmented pairs). May not generalize well
to domains far from the STS benchmark distribution (news, forums, headlines, image captions).
Symmetric augmentation assumes similarity is perfectly symmetric. This is generally true
for semantic similarity but may not hold for all retrieval scenarios.
Inherits biases from the base MiniLM architecture and SemEval training data, which is
predominantly English, web-sourced text.
Should not be used as the sole decision-making system in high-stakes applications
without human oversight.
11. License
This fine-tuned model is released under the Apache 2.0 License, consistent with the
license of the base model
sentence-transformers/all-MiniLM-L6-v2.
The training datasets (STS Benchmark, STS12) are released for research and evaluation
purposes by their respective authors. The raw dataset files are not redistributed here.
12. Credits and Citations
Base Model:
Nils Reimers and Iryna Gurevych.
"Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks."
Proceedings of EMNLP 2019.
Model Card
STS Benchmark:
Daniel Cer, Mona Diab, Eneko Agirre, Inigo Lopez-Gazpio, Lucia Specia.
"SemEval-2017 Task 1: Semantic Textual Similarity Multilingual and Crosslingual Focused Evaluation."
Proceedings of SemEval-2017.
STS12:
Eneko Agirre, Daniel Cer, Mona Diab, Inigo Lopez-Gazpio, Lucia Specia.
"SemEval-2012 Task 6: A Pilot on Semantic Textual Similarity."
Proceedings of *SEM 2012.
Evaluation Framework:
MTEB: Massive Text Embedding Benchmark