Instructions to use foudil/lens-emotion-encoder with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use foudil/lens-emotion-encoder with sentence-transformers:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("foudil/lens-emotion-encoder") sentences = [ "That is a happy person", "That is a happy dog", "That is a very happy person", "Today is a sunny day" ] embeddings = model.encode(sentences) similarities = model.similarity(embeddings, embeddings) print(similarities.shape) # [4, 4] - Notebooks
- Google Colab
- Kaggle
tags:
- sentence-transformers
- sentence-similarity
- feature-extraction
- emotion
- contrastive-learning
- multi-label
pipeline_tag: sentence-similarity
library_name: sentence-transformers
language:
- en
license: apache-2.0
base_model: SamLowe/roberta-base-go_emotions
datasets:
- go_emotions
EmotionEncoder
EmotionEncoder is a RoBERTa-base encoder fine-tuned with a multi-label supervised contrastive objective on GoEmotions. It maps English text into a 128-dimensional space where cosine similarity = emotional similarity: texts that share emotional content sit close together, texts that don't are pushed apart.
It is not a classifier. There are no output labels, no softmax, no categories to pick from. You get a vector, and that vector's geometry is the model's understanding of emotion.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("foudil/lens-emotion-encoder")
sentences = [
"I can't believe how proud I am of everything she's achieved.",
"Watching her succeed fills me with such joy and admiration.",
"The quarterly report has been filed with the relevant authorities.",
]
embeddings = model.encode(sentences)
similarities = model.similarity(embeddings, embeddings)
# sentences[0] ↔ sentences[1]: high similarity (pride/joy/admiration overlap)
# sentences[0] ↔ sentences[2]: low similarity (emotional vs. neutral)
Why this model exists
Existing approaches to emotion in text fall into two camps, and both have a geometric problem.
Classification models (e.g. roberta-base-go_emotions) are trained with cross-entropy to separate emotion categories. They discriminate well — but cross-entropy optimises decision boundaries, not distances. The resulting embedding space isn't designed to answer "how emotionally similar are these two texts?"
VAD regression models map text to valence–arousal–dominance coordinates. They're continuous by construction, but three scalars cannot resolve categorical distinctions that share VAD coordinates. Fear and anger both sit in the high-arousal, negative-valence quadrant; they are not the same emotion, and downstream systems that need to tell them apart will not get that signal from a VAD scalar.
EmotionEncoder is trained with a multi-label supervised contrastive objective that directly shapes the embedding geometry: texts that share emotional content are pulled together, texts that don't are pushed apart, and the strength of attraction is proportional to how much emotional overlap they have. The result is a space that is simultaneously discriminative and calibrated — something neither of the above achieves alone.
Model details
| Base model | SamLowe/roberta-base-go_emotions |
| Architecture | RoBERTa-base + masked mean pooling + 2-layer MLP projection head |
| Output dimension | 128 |
| Similarity function | Cosine similarity |
| Max sequence length | 100 tokens |
| Training data | GoEmotions (~54k Reddit comments, 28 emotion labels, multi-label) |
| Training objective | Multi-label SupCon with any-overlap pair weighting |
| Language | English |
The projection head maps the 768-dimensional pooled representation to 128 dimensions via two linear layers (768→512 with ReLU, 512→128), followed by LayerNorm and ℓ₂ normalisation. All embeddings live on the unit hypersphere, so cosine similarity is the natural distance.
Performance
Evaluated on three datasets with varying label schemas against two baselines:
all-mpnet-base-v2— strong general-purpose semantic similarity encoder; well-calibrated, no emotion-specific signalSamLowe/roberta-base-go_emotions— the backbone before contrastive fine-tuning; strong discriminator, poorly calibrated for graded similarity
GoEmotions (in-domain, 28 labels)
| Model | Spearman ρ ↑ | ROC-AUC ↑ | Cosine gap ↑ | Brier ↓ | ECE ↓ |
|---|---|---|---|---|---|
| EmotionEncoder | +0.234 | 0.711 | 0.227 | 0.277 | 0.421 |
| all-mpnet-base-v2 | +0.026 | 0.523 | 0.008 | 0.276 | 0.416 |
| roberta-go_emotions | +0.240 | 0.716 | 0.167 | 0.360 | 0.515 |
dair-ai/emotion (out-of-domain, 6 labels)
| Model | Spearman ρ ↑ | ROC-AUC ↑ | Cosine gap ↑ | Brier ↓ | ECE ↓ |
|---|---|---|---|---|---|
| EmotionEncoder | +0.271 | 0.683 | 0.208 | 0.296 | 0.351 |
| all-mpnet-base-v2 | +0.079 | 0.553 | 0.023 | 0.293 | 0.332 |
| roberta-go_emotions | +0.254 | 0.671 | 0.120 | 0.344 | 0.415 |
tweet_eval/emotion (out-of-domain, 4 labels)
| Model | Spearman ρ ↑ | ROC-AUC ↑ | Cosine gap ↑ | Brier ↓ | ECE ↓ |
|---|---|---|---|---|---|
| EmotionEncoder | +0.363 | 0.730 | 0.246 | 0.255 | 0.273 |
| all-mpnet-base-v2 | +0.147 | 0.593 | 0.037 | 0.281 | 0.277 |
| roberta-go_emotions | +0.295 | 0.686 | 0.131 | 0.313 | 0.348 |
EmotionEncoder exceeds the backbone on both out-of-domain datasets on every metric — the label schemas there (4–6 labels) are completely different from GoEmotions (28 labels), so this is genuine transfer, not overfitting to the training distribution. On Brier score it matches all-mpnet-base-v2 in-domain (within 0.001) and beats it on tweet_eval, while reducing the backbone's Brier by 23% on GoEmotions.
What the OOD transfer does and does not show
The OOD numbers support the claim that the learned geometry captures something more general than the GoEmotions label schema. The dair-ai/emotion (6 labels, Twitter) and tweet_eval/emotion (4 labels, Twitter) schemas were not seen during training, and the encoder's geometry recovers their structure better than either baseline.
They do not show that the model has learned a domain-invariant or theory-grounded emotion representation. Both OOD datasets remain English social-media text, sharing register and surface conventions with the GoEmotions training distribution. Performance on emotionally-loaded text in a clinical interview, a legal document, a literary passage, or a non-English-translated source is not established by these probes.
The intended interpretation is conservative: the encoder generalises across emotion label schemas of varying granularity within an English social-media-adjacent register, and should be validated on any domain that departs meaningfully from that.
Usage
Semantic search over emotional content
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("foudil/lens-emotion-encoder")
corpus = [
"I'm devastated. Everything we worked for is gone.",
"She finally got the promotion she deserved — couldn't be happier for her.",
"The meeting has been rescheduled to Thursday.",
"There's something deeply unsettling about the way this unfolded.",
"I feel so grateful for everyone who showed up today.",
]
query = "I'm overwhelmed with gratitude and happiness."
corpus_emb = model.encode(corpus, convert_to_tensor=True)
query_emb = model.encode(query, convert_to_tensor=True)
scores = model.similarity(query_emb, corpus_emb)[0]
for score, sentence in sorted(zip(scores, corpus), reverse=True):
print(f"{score:.3f} {sentence}")
Pairwise similarity scoring
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("foudil/lens-emotion-encoder")
pairs = [
("I'm furious. This is completely unacceptable.", "She makes me so angry I can't think straight."),
("I'm furious. This is completely unacceptable.", "What a wonderful surprise — I'm so touched."),
]
for a, b in pairs:
ea, eb = model.encode([a, b])
print(f"{model.similarity(ea, eb).item():.3f} | {a[:50]}...")
Intended use
- Semantic search and retrieval over emotionally-indexed content
- Emotional similarity scoring between text pairs
- Soft signal for emotion-aware ranking or filtering
- Feature input for downstream models that need a graded emotion representation
Out-of-scope use
- Hard emotion classification. This model produces similarity scores, not category labels. If you need
{"joy": 0.9, "sadness": 0.1}, use a dedicated classification model. - Non-English text. The model has not been evaluated on other languages.
- Clinical or diagnostic use. Emotion similarity scores are not a substitute for clinical emotion assessment.
Limitations
English Reddit register. Training data is drawn from Reddit. Register generalisation beyond English social media is not established and should be validated on your domain before deployment.
GoEmotions label noise. Inter-annotator agreement varies across emotion categories. Categories with low agreement or few examples yield less reliable geometry.
Calibration residual. Boundary ECE on GoEmotions is 0.421 vs. 0.416 for the mpnet calibration reference. Post-hoc temperature or Platt scaling can close this if precise probability estimates matter.
No valence axis. The model encodes full emotional profiles, not sentiment polarity. It is not optimised as a sentiment analyser.
Citation
If you use this model, please cite the GoEmotions corpus and the SupCon objective on which it is based:
@inproceedings{demszky-etal-2020-goemotions,
title = {{GoEmotions}: A Dataset of Fine-Grained Emotions},
author = {Demszky, Dorottya and Movshovitz-Attias, Dana and Ko, Jeongwoo and Cowen, Alan and Nemade, Gaurav and Ravi, Sujith},
booktitle = {Proceedings of the 58th Annual Meeting of the Association for Computational Linguistics},
year = {2020},
publisher = {Association for Computational Linguistics},
pages = {4040--4054},
doi = {10.18653/v1/2020.acl-main.372},
url = {https://aclanthology.org/2020.acl-main.372/}
}
@inproceedings{khosla-etal-2020-supcon,
title = {Supervised Contrastive Learning},
author = {Khosla, Prannay and Teterwak, Piotr and Wang, Chen and Sarna, Aaron and Tian, Yonglong and Isola, Phillip and Maschinot, Aaron and Liu, Ce and Krishnan, Dilip},
booktitle = {Advances in Neural Information Processing Systems},
volume = {33},
year = {2020},
pages = {18661--18673},
url = {https://proceedings.neurips.cc/paper/2020/hash/d89a66c7c80a29b1bdbab0f2a1a94af8-Abstract.html}
}
Framework versions
- Python 3.9.25 · sentence-transformers 5.1.2 · transformers 4.57.6 · PyTorch 2.8.0