File size: 5,811 Bytes
e93b079
 
142ebfc
 
 
 
 
 
 
 
 
 
 
 
 
 
e93b079
142ebfc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c161bc5
 
142ebfc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c161bc5
142ebfc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
---
license: apache-2.0
base_model: distilbert-base-uncased
tags:
  - text-classification
  - multi-label-classification
  - emotion-detection
  - distilbert
  - pytorch
datasets:
  - go_emotions
language:
  - en
metrics:
  - f1
pipeline_tag: text-classification
---

# EmotiCare — Multi-Label Emotion Classifier

EmotiCare is a fine-tuned [DistilBERT](https://huggingface.co/distilbert-base-uncased) model for **multi-label emotion detection** in English text. Given a sentence, it predicts one or more emotions from 28 categories drawn from the [GoEmotions](https://huggingface.co/datasets/go_emotions) dataset.

It is designed for use in applications that need nuanced, fine-grained emotion understanding — such as mental health tools, sentiment dashboards, chatbots, and content moderation systems.

## Emotions

The model classifies text into 28 emotions:

`admiration` · `amusement` · `anger` · `annoyance` · `approval` · `caring` · `confusion` · `curiosity` · `desire` · `disappointment` · `disapproval` · `disgust` · `embarrassment` · `excitement` · `fear` · `gratitude` · `grief` · `joy` · `love` · `nervousness` · `optimism` · `pride` · `realization` · `relief` · `remorse` · `sadness` · `surprise` · `neutral`

## Model Details

| Property | Value |
|---|---|
| Base model | `distilbert-base-uncased` |
| Architecture | DistilBertForSequenceClassification |
| Task | Multi-label text classification |
| Dataset | GoEmotions (simplified, 43,410 train samples) |
| Training epochs | 3 |
| Max sequence length | 512 tokens |
| Framework | PyTorch + 🤗 Transformers |

## Evaluation Results

Evaluated on the GoEmotions test set (5,427 examples):

| Metric | Score |
|---|---|
| F1 Macro | **0.4019** |
| F1 Micro | **0.5702** |
| Eval Loss | 0.0843 |

> Note: Multi-label emotion classification on GoEmotions is a challenging task due to class imbalance and overlapping emotions. F1 Micro of ~0.57 is competitive with similar fine-tuned DistilBERT baselines.

## Inference

### Using the 🤗 `pipeline` (recommended)

```python
from transformers import pipeline
import torch

classifier = pipeline(
    "text-classification",
    model="BruceIC/emoticare",  # replace with your HF repo path
    tokenizer="BruceIC/emoticare",
    top_k=None,                        # return scores for all labels
    device=0 if torch.cuda.is_available() else -1,
)

text = "I can't believe how thoughtful that was, I'm so touched."
results = classifier(text)

# Filter to emotions above a confidence threshold
threshold = 0.3
detected = [r for r in results[0] if r["score"] > threshold]
for emotion in sorted(detected, key=lambda x: -x["score"]):
    print(f"{emotion['label']:<20} {emotion['score']:.3f}")
```

**Example output:**
```
gratitude            0.847
admiration           0.612
love                 0.431
```

---

### Manual inference (more control)

```python
import torch
import torch.nn.functional as F
from transformers import DistilBertTokenizer, DistilBertForSequenceClassification

model_name = "BruceIC/emoticare"  # replace with your HF repo path

tokenizer = DistilBertTokenizer.from_pretrained(model_name)
model = DistilBertForSequenceClassification.from_pretrained(model_name)
model.eval()

def predict_emotions(text: str, threshold: float = 0.3):
    inputs = tokenizer(
        text,
        return_tensors="pt",
        truncation=True,
        max_length=512,
        padding=True,
    )
    with torch.no_grad():
        logits = model(**inputs).logits
    probs = torch.sigmoid(logits).squeeze()  # sigmoid for multi-label

    emotions = model.config.id2label
    results = [
        {"label": emotions[i], "score": float(probs[i])}
        for i in range(len(emotions))
        if float(probs[i]) > threshold
    ]
    return sorted(results, key=lambda x: -x["score"])

# Example
print(predict_emotions("I'm so proud of everything we've built together!"))
```

---

### Batch inference

```python
texts = [
    "I'm terrified of what might happen next.",
    "This is the best day of my life!",
    "I don't really feel anything about it.",
]

inputs = tokenizer(
    texts,
    return_tensors="pt",
    truncation=True,
    max_length=512,
    padding=True,
)

with torch.no_grad():
    logits = model(**inputs).logits

probs = torch.sigmoid(logits)  # shape: (batch_size, 28)
threshold = 0.3

for i, text in enumerate(texts):
    detected = [
        model.config.id2label[j]
        for j in range(28)
        if probs[i][j] > threshold
    ]
    print(f"Text: {text}")
    print(f"Emotions: {', '.join(detected) or 'none above threshold'}\n")
```

## Training Details

- **Base model:** `distilbert-base-uncased`
- **Dataset:** [go_emotions](https://huggingface.co/datasets/go_emotions) (simplified config)
- **Loss function:** Binary Cross-Entropy (multi-label)
- **Optimizer:** AdamW with linear warmup + decay
- **Learning rate:** 2e-5 (peak)
- **Batch size:** 16
- **Epochs:** 3
- **Best checkpoint:** step 8142 (epoch 3)

## Limitations

- Trained on Reddit comments — performance may degrade on formal text, non-native English, or very short inputs.
- Some rare emotions (grief, pride, relief) have limited training examples and lower per-class F1.
- Outputs are probabilities; the optimal threshold (default 0.3) may need tuning for your use case.

## Citation

If you use this model, please cite the GoEmotions dataset:

```bibtex
@inproceedings{demszky-etal-2020-goemotions,
  title     = {{GoEmotions}: A Dataset of Fine-Grained Emotions},
  author    = {Demszky, Dorottya and Movshovitz-Attias, Dana and Ko, Jeongwook
               and Cowen, Alan and Nemade, Gaurav and Ravi, Sujith},
  booktitle = {Proceedings of the 58th Annual Meeting of the Association for
               Computational Linguistics},
  year      = {2020},
}
```