Upload README.md with huggingface_hub
Browse files
README.md
CHANGED
|
@@ -6,7 +6,6 @@
|
|
| 6 |
- question-classification
|
| 7 |
- text-classification
|
| 8 |
- onnx
|
| 9 |
-
- model2vec
|
| 10 |
- english
|
| 11 |
- eat
|
| 12 |
- calibrated
|
|
@@ -16,178 +15,71 @@
|
|
| 16 |
|
| 17 |
# eat-classifiers
|
| 18 |
|
| 19 |
-
English question-type classifiers trained on the
|
| 20 |
-
[EAT
|
| 21 |
-
|
| 22 |
|
| 23 |
-
Two
|
| 24 |
|
| 25 |
-
|
| 26 |
-
|--------|----------|----------------|--------------------------|
|
| 27 |
-
| **ONNX** (LinearSVC, calibrated) | `models/eat/` | `onnxruntime` only | **0.934** (two-stage svm_cal) |
|
| 28 |
-
| **Model2Vec** (potion-base + LinearSVC) | `models/eat_m2v/` | `model2vec`, `scikit-learn` | **0.921** (32M+tfidf) |
|
| 29 |
-
|
| 30 |
-
---
|
| 31 |
|
| 32 |
## Label taxonomy
|
| 33 |
|
| 34 |
-
7 main categories
|
| 35 |
|
| 36 |
| Main | Sub-types |
|
| 37 |
|------|-----------|
|
| 38 |
-
| `ABBR` |
|
| 39 |
-
| `BOOL` |
|
| 40 |
-
| `DESC` |
|
| 41 |
-
| `ENTY` |
|
| 42 |
-
| `HUM` |
|
| 43 |
-
| `LOC` |
|
| 44 |
-
| `NUM` |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
| `
|
| 59 |
-
| `
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
| _(two-stage svm)_ | 53 | svm | decision scores | 0.934 | 0.933 |
|
| 66 |
-
| _(two-stage svm_cal)_ | 53 | svm_cal | calibrated proba | 0.934 | 0.934 |
|
| 67 |
-
|
| 68 |
-
### Two-stage inference
|
| 69 |
-
|
| 70 |
-
Run `eat7_svm_cal` first to predict the main category, then run
|
| 71 |
-
`eat53_svm_cal`, zero out all labels outside that category, and
|
| 72 |
-
renormalise the surviving probabilities. This is the highest-accuracy
|
| 73 |
-
configuration (macro F1 = 0.934 on the held-out test set).
|
| 74 |
|
| 75 |
```python
|
| 76 |
-
import onnxruntime as rt
|
| 77 |
-
import numpy as np, json
|
| 78 |
|
| 79 |
-
sess7 = rt.InferenceSession("
|
| 80 |
-
sess53 = rt.InferenceSession("
|
| 81 |
classes7 = json.loads(sess7.get_modelmeta().custom_metadata_map["classes"])
|
| 82 |
classes53 = json.loads(sess53.get_modelmeta().custom_metadata_map["classes"])
|
| 83 |
main_of_53 = [c.split(":")[0] for c in classes53]
|
| 84 |
|
| 85 |
-
def classify(text
|
| 86 |
inp = np.array([text], dtype=object)
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
row = probs53[0].copy()
|
| 91 |
for j, m in enumerate(main_of_53):
|
| 92 |
if m != main:
|
| 93 |
row[j] = 0.0
|
| 94 |
row /= row.sum()
|
| 95 |
-
|
| 96 |
-
return classes53[best], float(row[best])
|
| 97 |
|
| 98 |
-
|
| 99 |
-
print(label, confidence) # HUM:ind 0.96
|
| 100 |
```
|
| 101 |
|
| 102 |
-
### Single-model ONNX inference
|
| 103 |
-
|
| 104 |
-
```python
|
| 105 |
-
import onnxruntime as rt
|
| 106 |
-
import numpy as np, json
|
| 107 |
-
|
| 108 |
-
sess = rt.InferenceSession("models/eat/eat53_svm_cal_EN_0.9.0.onnx")
|
| 109 |
-
classes = json.loads(sess.get_modelmeta().custom_metadata_map["classes"])
|
| 110 |
-
|
| 111 |
-
inp = np.array(["Who invented the telephone?"], dtype=object)
|
| 112 |
-
label_idx, probs = sess.run(None, {"input": inp})
|
| 113 |
-
label = classes[int(label_idx[0])]
|
| 114 |
-
confidence = float(probs[0].max())
|
| 115 |
-
print(label, confidence) # HUM:ind 0.94
|
| 116 |
-
```
|
| 117 |
-
|
| 118 |
-
---
|
| 119 |
-
|
| 120 |
-
## Model2Vec models
|
| 121 |
-
|
| 122 |
-
Static [model2vec](https://github.com/MinishLab/model2vec) embeddings
|
| 123 |
-
(potion-base 2M / 8M / 32M) with a LinearSVC classifier on top.
|
| 124 |
-
The `+tfidf` variants concatenate TF-IDF word (1,2)-gram features with
|
| 125 |
-
the embeddings before fitting — this consistently adds 5–30 pp macro F1
|
| 126 |
-
over embeddings alone.
|
| 127 |
-
|
| 128 |
-
| Directory | Classes | Backbone | TF-IDF fusion | Accuracy | Macro F1 |
|
| 129 |
-
|-----------|---------|----------|---------------|----------|----------|
|
| 130 |
-
| `m2v-potion-base-2M+tfidf-en-53c` | 53 | potion-base-2M | ✓ | 0.916 | 0.914 |
|
| 131 |
-
| `m2v-potion-base-2M+tfidf-en-7c` | 7 | potion-base-2M | ✓ | 0.953 | 0.908 |
|
| 132 |
-
| `m2v-potion-base-2M-en-53c` | 53 | potion-base-2M | — | 0.631 | 0.607 |
|
| 133 |
-
| `m2v-potion-base-2M-en-7c` | 7 | potion-base-2M | — | 0.700 | 0.562 |
|
| 134 |
-
| `m2v-potion-base-32M+tfidf-en-53c` | 53 | potion-base-32M | ✓ | 0.922 | 0.920 |
|
| 135 |
-
| `m2v-potion-base-32M+tfidf-en-7c` | 7 | potion-base-32M | ✓ | 0.956 | 0.922 |
|
| 136 |
-
| `m2v-potion-base-32M-en-53c` | 53 | potion-base-32M | — | 0.857 | 0.854 |
|
| 137 |
-
| `m2v-potion-base-32M-en-7c` | 7 | potion-base-32M | — | 0.895 | 0.843 |
|
| 138 |
-
| `m2v-potion-base-8M+tfidf-en-53c` | 53 | potion-base-8M | ✓ | 0.918 | 0.917 |
|
| 139 |
-
| `m2v-potion-base-8M+tfidf-en-7c` | 7 | potion-base-8M | ✓ | 0.952 | 0.913 |
|
| 140 |
-
| `m2v-potion-base-8M-en-53c` | 53 | potion-base-8M | — | 0.818 | 0.811 |
|
| 141 |
-
| `m2v-potion-base-8M-en-7c` | 7 | potion-base-8M | — | 0.843 | 0.763 |
|
| 142 |
-
|
| 143 |
-
### Usage (Model2Vec)
|
| 144 |
-
|
| 145 |
-
```python
|
| 146 |
-
from huggingface_hub import snapshot_download
|
| 147 |
-
from train.classifiers import Model2VecClassifier
|
| 148 |
-
|
| 149 |
-
local = snapshot_download("TigreGotico/eat-classifiers")
|
| 150 |
-
clf = Model2VecClassifier.load(f"{local}/models/eat_m2v/m2v-potion-base-8M+tfidf-en-53c")
|
| 151 |
-
print(clf.predict(["Who invented the telephone?"]))
|
| 152 |
-
# ["HUM:ind"]
|
| 153 |
-
```
|
| 154 |
-
|
| 155 |
-
---
|
| 156 |
-
|
| 157 |
-
## Integration with little_questions
|
| 158 |
-
|
| 159 |
-
When installed as part of [little_questions](https://github.com/TigreGotico/little_questions), the
|
| 160 |
-
calibrated ONNX model (`eat53_svm_cal`) is loaded automatically and
|
| 161 |
-
exposed through the `Sentence` API:
|
| 162 |
-
|
| 163 |
-
```python
|
| 164 |
-
from little_questions import Sentence
|
| 165 |
-
|
| 166 |
-
s = Sentence("Who invented the telephone?")
|
| 167 |
-
print(s.classification) # "HUM:ind"
|
| 168 |
-
print(s.main_label) # "HUM"
|
| 169 |
-
print(s.secondary_label) # "ind"
|
| 170 |
-
print(s.confidence) # 0.94
|
| 171 |
-
print(s.pretty_label) # "individual (Human)"
|
| 172 |
-
# Full probability distribution over all 53 labels:
|
| 173 |
-
print(s.classification_scores)
|
| 174 |
-
```
|
| 175 |
-
|
| 176 |
-
---
|
| 177 |
-
|
| 178 |
-
## Datasets
|
| 179 |
-
|
| 180 |
-
Both datasets were built specifically for this project:
|
| 181 |
-
|
| 182 |
-
| Dataset | Task | Size | Labels |
|
| 183 |
-
|---------|------|------|--------|
|
| 184 |
-
| [TigreGotico/EAT](https://huggingface.co/datasets/TigreGotico/EAT) | Question answer-type | 30K EN | 53 fine-grained / 7 main |
|
| 185 |
-
| [TigreGotico/sentence-types-multilingual](https://huggingface.co/datasets/TigreGotico/sentence-types-multilingual) | Sentence type | 80K multilingual | 5 types |
|
| 186 |
-
|
| 187 |
-
---
|
| 188 |
-
|
| 189 |
## Benchmarks
|
| 190 |
|
| 191 |
-
Full results
|
| 192 |
-
|
| 193 |
-

|
|
|
|
| 6 |
- question-classification
|
| 7 |
- text-classification
|
| 8 |
- onnx
|
|
|
|
| 9 |
- english
|
| 10 |
- eat
|
| 11 |
- calibrated
|
|
|
|
| 15 |
|
| 16 |
# eat-classifiers
|
| 17 |
|
| 18 |
+
English question answer-type (EAT) classifiers trained on the
|
| 19 |
+
[TigreGotico/EAT](https://huggingface.co/datasets/TigreGotico/EAT) dataset
|
| 20 |
+
(30,017 questions, 53 fine-grained labels across 7 TREC categories).
|
| 21 |
|
| 22 |
+
Two-stage inference (eat7 gates eat53) achieves **93.4% macro F1** on the test set.
|
| 23 |
|
| 24 |
+
Used by [little_questions](https://github.com/OpenJarbas/little_questions).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
## Label taxonomy
|
| 27 |
|
| 28 |
+
7 main categories, 53 sub-types:
|
| 29 |
|
| 30 |
| Main | Sub-types |
|
| 31 |
|------|-----------|
|
| 32 |
+
| `ABBR` | abb, exp |
|
| 33 |
+
| `BOOL` | yesno |
|
| 34 |
+
| `DESC` | def, desc, manner, reason |
|
| 35 |
+
| `ENTY` | animal, body, color, cremat, currency, dismed, event, food, instru, lang, letter, other, plant, product, religion, sport, substance, symbol, techmeth, termeq, veh, word |
|
| 36 |
+
| `HUM` | desc, gr, ind, title |
|
| 37 |
+
| `LOC` | city, country, landmass, mount, other, state, water |
|
| 38 |
+
| `NUM` | code, count, date, dist, money, ord, other, perc, period, speed, temp, volsize, weight |
|
| 39 |
+
|
| 40 |
+
## Models
|
| 41 |
+
|
| 42 |
+
| File | Input variant | Output[1] |
|
| 43 |
+
|------|---------------|-----------|
|
| 44 |
+
| `eat53_logreg_EN_0.9.0.onnx` | punctuated (written) | decision score |
|
| 45 |
+
| `eat53_sgd_EN_0.9.0.onnx` | punctuated (written) | decision score |
|
| 46 |
+
| `eat53_svm_EN_0.9.0.onnx` | punctuated (written) | decision score |
|
| 47 |
+
| `eat53_svm_cal_EN_0.9.0.onnx` | punctuated (written) | calibrated probability |
|
| 48 |
+
| `eat53_svm_cal_unpunct_EN_0.9.0.onnx` | unpunctuated (ASR/voice) | calibrated probability |
|
| 49 |
+
| `eat7_logreg_EN_0.9.0.onnx` | punctuated (written) | decision score |
|
| 50 |
+
| `eat7_sgd_EN_0.9.0.onnx` | punctuated (written) | decision score |
|
| 51 |
+
| `eat7_svm_EN_0.9.0.onnx` | punctuated (written) | decision score |
|
| 52 |
+
| `eat7_svm_cal_EN_0.9.0.onnx` | punctuated (written) | calibrated probability |
|
| 53 |
+
| `eat7_svm_cal_unpunct_EN_0.9.0.onnx` | unpunctuated (ASR/voice) | calibrated probability |
|
| 54 |
+
|
| 55 |
+
Both punctuated and unpunctuated variants are provided.
|
| 56 |
+
Use the unpunctuated (`_unpunct`) model for ASR / voice assistant input.
|
| 57 |
+
|
| 58 |
+
## Two-stage inference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
```python
|
| 61 |
+
import onnxruntime as rt, numpy as np, json
|
|
|
|
| 62 |
|
| 63 |
+
sess7 = rt.InferenceSession("eat7_svm_cal_EN_0.9.0.onnx")
|
| 64 |
+
sess53 = rt.InferenceSession("eat53_svm_cal_EN_0.9.0.onnx")
|
| 65 |
classes7 = json.loads(sess7.get_modelmeta().custom_metadata_map["classes"])
|
| 66 |
classes53 = json.loads(sess53.get_modelmeta().custom_metadata_map["classes"])
|
| 67 |
main_of_53 = [c.split(":")[0] for c in classes53]
|
| 68 |
|
| 69 |
+
def classify(text):
|
| 70 |
inp = np.array([text], dtype=object)
|
| 71 |
+
main = classes7[int(sess7.run(None, {"input": inp})[0][0])]
|
| 72 |
+
_, probs = sess53.run(None, {"input": inp})
|
| 73 |
+
row = probs[0].copy()
|
|
|
|
| 74 |
for j, m in enumerate(main_of_53):
|
| 75 |
if m != main:
|
| 76 |
row[j] = 0.0
|
| 77 |
row /= row.sum()
|
| 78 |
+
return classes53[int(np.argmax(row))], float(row.max())
|
|
|
|
| 79 |
|
| 80 |
+
print(classify("Who invented the telephone?")) # ('HUM:ind', 0.96)
|
|
|
|
| 81 |
```
|
| 82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
## Benchmarks
|
| 84 |
|
| 85 |
+
Full results: [BENCHMARKS.md](BENCHMARKS.md)
|
|
|
|
|
|