vladimirbesk commited on
Commit
dfafb0b
·
verified ·
1 Parent(s): 85dd0db

card for v1.3: measured numbers only, jargon row explained

Browse files
Files changed (1) hide show
  1. README.md +226 -140
README.md CHANGED
@@ -22,7 +22,7 @@ base_model: jhu-clsp/mmBERT-base
22
  inference: false
23
  ---
24
 
25
- # Ру-Жев (Ru-Jev) v1.1
26
 
27
  Open, Russian-first typed-decision model. It does not generate text. Given a
28
  context and a set of candidate options, it returns a calibrated probability over
@@ -33,8 +33,8 @@ runs on CPU and can be deployed inside a closed perimeter.
33
  Project page: https://ru-jev.besk.tech
34
  Training and evaluation code: https://github.com/beskvladimir-create/ru-jev
35
 
36
- The previous release is kept on the `v1` branch of this repository. Load it with
37
- `revision="v1"` if you depend on its exact behaviour.
38
 
39
  The inference widget is switched off on purpose. This is a backbone plus a
40
  separate scoring head, not a standard sequence-classification checkpoint, so the
@@ -44,195 +44,281 @@ stock widget cannot load it. Use the snippet below.
44
  trained from scratch on open, human-labelled datasets. The name is a play on the
45
  category, not a claim of compatibility or endorsement.
46
 
47
- ## What changed since v1
48
 
49
- v1 could not route. It was trained on natural language inference, sentiment and
50
- toxicity, and no task in its mix ever presented category names as the options,
51
- so asking it to pick a department from a list left it at roughly chance.
 
 
 
52
 
53
- | Task | v1 | v1.1 |
 
 
 
 
 
54
  | --- | --- | --- |
55
- | Routing, 60 intents | 0.344 | 0.965 |
56
- | Routing, 18 scenarios | 0.305 | 0.954 |
57
- | Reviews, 5 levels | 0.391 | 0.629 |
58
- | Reasoning questions | 0.327 | 0.522 |
59
- | All 13 tasks | 0.663 | 0.825 |
60
-
61
- On the nine tasks v1 was trained for, scored on identical rows, v1.1 is ahead
62
- overall as well, 0.843 against 0.824, with the gains on acceptability and
63
- sentiment and losses on `rcb` and `russe`.
64
-
65
- Calibration moved the other way on those nine tasks: 0.009 for v1 against 0.020
66
- for v1.1, both measured held out. On the full thirteen-task mix the order
67
- reverses, 0.025 for v1 against 0.020 for v1.1, because a single temperature
68
- cannot serve a model that is at chance on half the work. v1.1 is the better
69
- trade: two points of calibration on a narrow slice bought routing that works.
 
 
70
 
71
  ## Results
72
 
73
- Measured on half of the validation split, 13,373 decisions, with the
74
- temperature fitted on the other half.
75
 
76
  | Metric | Value |
77
  | --- | --- |
78
- | Accuracy (micro) | 0.825 |
79
- | ECE, temperature fitted on a disjoint half | 0.0200 |
80
  | ECE, temperature fitted on this same data | 0.0159 |
81
- | Shipped temperature | 2.2754 |
82
 
83
  Two ECE figures, because the number depends on where the temperature came from.
84
  Fitting and scoring on the same rows is in-sample and optimistic. The first
85
  figure is what to expect on new data of the same kind, and it is the one to
86
  quote.
87
 
88
- Per task, full validation split:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- | Task | Accuracy | n |
91
  | --- | --- | --- |
92
- | massive_intent (60 labels) | 0.965 | 3000 |
93
- | toxicity | 0.961 | 3000 |
94
- | massive_scenario (18 labels) | 0.954 | 3000 |
95
- | russe (word sense) | 0.822 | 8505 |
96
- | terra (entailment) | 0.775 | 307 |
97
- | rureviews (sentiment) | 0.772 | 3000 |
98
- | danetqa (yes/no) | 0.742 | 821 |
99
- | parus (choice) | 0.680 | 100 |
100
- | rucola (acceptability) | 0.650 | 1344 |
101
- | georeview (5 levels) | 0.629 | 3000 |
102
- | rwsd (coreference) | 0.564 | 204 |
103
- | mera_qa (reasoning) | 0.522 | 245 |
104
- | rcb (3-way NLI) | 0.450 | 220 |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
  ## Name your options carefully
107
 
108
- The model reads the option text. How you word your categories changes the
109
- answer, and the effect is large. Three hundred routing questions, the same
110
- eighteen categories every time, only the wording differs:
111
 
112
- | Wording | Example | Accuracy | Confident errors |
113
- | --- | --- | --- | --- |
114
- | Russian word | `погода` | 0.973 | 3 |
115
- | English key | `weather` | 0.963 | 4 |
116
- | Descriptive sentence | `человек спрашивает про погоду` | 0.953 | 2 |
117
- | Russian phrase | `прогноз погоды` | 0.890 | 9 |
118
- | Department name | `метеослужба` | 0.870 | 10 |
119
- | Jargon, transliterated | `вэзер` | 0.630 | 26 |
120
-
121
- Write options as ordinary words that describe the category. Avoid borrowed
122
- jargon: `биллинг` costs far more accuracy than `оплата, счета и возвраты`. The
123
- last column counts answers that were wrong while above 0.8 confidence, so on
124
- unfamiliar wording the probability stops being a reliable guide as well. If you
125
- must use in-house jargon, measure it on your own data first with
126
- `scripts/robustness.py` from the code repository.
127
 
128
  ## Withholding an answer
129
 
130
- A calibrated probability is worth something only if the caller acts on it. The
131
- serving code in the repository takes a `threshold` and withholds anything below
132
- it for a person to handle.
133
 
134
- | Share answered automatically | Threshold | Accuracy on those | Errors per 1000 |
135
  | --- | --- | --- | --- |
136
- | 30% | 0.98 | 99.3% | 7 |
137
- | 50% | 0.85 | 97.1% | 29 |
138
- | 60% | 0.77 | 95.1% | 49 |
139
- | 80% | 0.60 | 89.5% | 105 |
140
- | 100% | none | 82.5% | 175 |
 
141
 
142
- Per decision shape at 95% accuracy or better, routing over many options clears
143
- the entire stream, while yes/no questions and small choices clear about half.
144
 
145
- ## What a cheap baseline gets
 
 
 
 
146
 
147
- | Task | Majority class | TF-IDF + logreg | Ru-Jev v1.1 |
148
- | --- | --- | --- | --- |
149
- | massive_scenario | 0.066 | 0.435 | 0.954 |
150
- | massive_intent | 0.063 | 0.820 | 0.965 |
151
- | terra | 0.498 | 0.394 | 0.775 |
152
- | danetqa | 0.502 | 0.568 | 0.742 |
153
- | russe | 0.631 | 0.701 | 0.822 |
154
- | georeview | 0.199 | 0.544 | 0.629 |
155
- | rureviews | 0.324 | 0.720 | 0.772 |
156
- | rucola | 0.587 | 0.609 | 0.650 |
157
- | toxicity | 0.766 | 0.943 | 0.961 |
158
- | rwsd | 0.554 | 0.554 | 0.564 |
159
-
160
- The model earns its place on routing, entailment and yes/no questions. On
161
- toxicity it is 1.8 points over character n-grams and on sentiment 5.2 points,
162
- so for those tasks alone a linear model is cheaper to run and easier to debug.
163
 
164
- ## Limitations
 
 
165
 
166
- - **Coreference is not supported.** On `rwsd` the model matches the majority
167
- class exactly, 0.564 against 0.554. It does not resolve pronouns.
168
- - **Multi-step reasoning is weak.** `rcb` 0.450 and `mera_qa` 0.522. A 307M
169
- encoder does not chain inferences, and no amount of further training on this
170
- architecture will change that.
171
- - **Unfamiliar label wording degrades both the answer and the confidence**, as
172
- the table above shows.
173
- - Russian first. The multilingual base handles other languages, but they were
174
- not measured.
175
 
176
  ## Speed on CPU
177
 
178
- Apple M4, four threads, median of 25 runs after warmup, this checkpoint in
179
- float32.
180
-
181
- | Request | Tokens | Median | p95 |
182
- | --- | --- | --- | --- |
183
- | yes/no, short context | 23 | 24.5 ms | 26.5 ms |
184
- | choice of three | 17 | 31.2 ms | 32.6 ms |
185
- | yes/no, full 256-token context | 315, truncated to 256 | 133.2 ms | 141.1 ms |
186
 
187
- On a single thread the short requests are unchanged and the full context costs
188
- 186 ms. A server CPU will give different figures.
 
 
 
189
 
190
  ## Usage
191
 
192
  ```python
193
- import torch, torch.nn as nn, json
194
- from transformers import AutoTokenizer, AutoModel
195
  from huggingface_hub import hf_hub_download
 
196
 
197
  REPO = "besktech/ru-jev"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  tok = AutoTokenizer.from_pretrained(REPO)
199
- backbone = AutoModel.from_pretrained(REPO).eval()
200
- head = nn.Linear(backbone.config.hidden_size, 1)
201
- head.load_state_dict(torch.load(hf_hub_download(REPO, "head.pt"), map_location="cpu"))
202
- T = json.load(open(hf_hub_download(REPO, "calibration.json")))["temperature"]
203
-
204
- @torch.no_grad()
205
- def decide(context, options):
206
- enc = tok([context] * len(options), [str(o) for o in options],
207
- truncation=True, max_length=256, padding=True, return_tensors="pt")
208
- scores = head(backbone(**enc).last_hidden_state[:, 0]).squeeze(-1)
209
- probs = torch.softmax(scores / T, dim=0)
210
- return list(zip(options, probs.tolist()))
211
-
212
- decide("Клиента списали дважды", ["да", "нет"])
213
- decide("Не приходит счёт", ["оплата, счета и возвраты", "техническая проблема"])
214
- decide("Отличный сервис, всё быстро", ["негативный", "нейтральный", "позитивный"])
215
  ```
216
 
217
- ## Training data
 
218
 
219
- Open datasets only, all permitting commercial use:
220
 
221
- - Russian SuperGLUE (MIT): terra, danetqa, russe, rwsd, parus, rcb
222
- - MASSIVE (CC-BY-4.0): intent and scenario routing
223
- - Georeview (MIT): 5-level reviews
224
- - MERA (MIT): reasoning questions
225
- - RuReviews (Apache-2.0): sentiment
226
- - RuDetoxifier data (MIT): toxicity
227
- - RuCoLA (Apache-2.0 labels): acceptability
228
 
229
- Boolean tasks are trained with five wordings of the same answer, and options are
230
- reordered on every epoch, so the model cannot learn a position or a single
231
- surface form.
232
 
233
  ## License
234
 
235
- MIT.
236
 
237
  Vladimir Beskorovainyi, Enterprise AI Architect & CTO, author of the Amazon
238
  bestseller "Enterprise AI: The KATA Way", founder of Besk Tech (besk.tech)
 
22
  inference: false
23
  ---
24
 
25
+ # Ру-Жев (Ru-Jev) v1.3
26
 
27
  Open, Russian-first typed-decision model. It does not generate text. Given a
28
  context and a set of candidate options, it returns a calibrated probability over
 
33
  Project page: https://ru-jev.besk.tech
34
  Training and evaluation code: https://github.com/beskvladimir-create/ru-jev
35
 
36
+ Earlier releases are kept on branches: `v1.1` and `v1`. Load one with
37
+ `revision="v1.1"` if you depend on its exact behaviour.
38
 
39
  The inference widget is switched off on purpose. This is a backbone plus a
40
  separate scoring head, not a standard sequence-classification checkpoint, so the
 
44
  trained from scratch on open, human-labelled datasets. The name is a play on the
45
  category, not a claim of compatibility or endorsement.
46
 
47
+ ## What changed since v1.1
48
 
49
+ v1.1 read the option text, which is the point of the design, but it read it
50
+ narrowly. It had learned the exact wordings its training data used. Rename a
51
+ category and accuracy fell off a cliff: 0.956 when a scenario was called
52
+ `погода`, 0.633 when the same scenario was called `вэзер`. Worse, on the
53
+ unfamiliar wording it stayed confident while being wrong, so the probability
54
+ stopped carrying information exactly where a caller needed it most.
55
 
56
+ v1.3 ships every fixed option set in several wordings and draws one per example,
57
+ so the model has to read an option instead of recognising it. For the routing
58
+ tasks that includes the English key written in Cyrillic and a clipped form of
59
+ it, generated by rule.
60
+
61
+ | Option wording | v1.1 | v1.3 |
62
  | --- | --- | --- |
63
+ | Russian word, `погода` | 0.956 | 0.960 |
64
+ | English key, `weather` | 0.957 | 0.962 |
65
+ | Descriptive sentence | 0.939 | 0.958 |
66
+ | Russian phrase, `прогноз погоды` | 0.881 | 0.957 |
67
+ | Department name, `метеослужба` | 0.835 | 0.869 |
68
+ | Jargon, `вэзер` | 0.633 | 0.855 |
69
+
70
+ A thousand routing questions per row, the same eighteen categories every time,
71
+ only the wording differs.
72
+
73
+ One caveat on the jargon row, because it is the headline number. An intent key
74
+ begins with its scenario name, so three of the eighteen jargon labels coincide
75
+ with what the transliteration rule produces and reach training as a first word.
76
+ On the fifteen that do not coincide, which is the honest measure of
77
+ generalisation to wording the model has never seen, v1.3 scores 0.838 against 0.624 for v1.1.
78
+ The rules were not tuned against this test vocabulary; `scripts/robustness.py`
79
+ in the code repository reports both figures.
80
 
81
  ## Results
82
 
83
+ v1.3 on its own validation split, 13,373 decisions, with the temperature
84
+ fitted on the other half:
85
 
86
  | Metric | Value |
87
  | --- | --- |
88
+ | Accuracy (micro) | 0.823 |
89
+ | ECE, temperature fitted on a disjoint half | 0.0185 |
90
  | ECE, temperature fitted on this same data | 0.0159 |
91
+ | Shipped temperature | 1.8640 |
92
 
93
  Two ECE figures, because the number depends on where the temperature came from.
94
  Fitting and scoring on the same rows is in-sample and optimistic. The first
95
  figure is what to expect on new data of the same kind, and it is the one to
96
  quote.
97
 
98
+ ### Why most of the comparison with v1.1 had to be thrown away
99
+
100
+ Two things stood between the two releases, and both were found by checking
101
+ rather than by reasoning.
102
+
103
+ The data builder carved each task's validation slice by shuffling that task's
104
+ pool with a module-level random generator, seeded once and consumed by every
105
+ other random call in the file. Adding two label styles to one task changed how
106
+ many bits those draws consumed, the stream diverged from that point, and every
107
+ task below it in the build order received a different slice. Measured: of
108
+ v1.3's validation rows, 90.3% of rucola and 89.8% of mera_qa had been in v1.1's
109
+ training set, along with 47.2% of georeview and 21.7% of toxicity.
110
+
111
+ The second is the point of this release working against its own measurement.
112
+ Where v1.3 ships an option set in several wordings, its validation rows carry
113
+ wordings v1.1 never trained on. Scoring v1.1 there measures how well it handles
114
+ renamed options, which is a real question but not the question a per-task
115
+ accuracy table is asking. Comparing by text alone hides this, so the rows were
116
+ compared by text and option wordings together.
117
 
118
+ | Task | Same rows | Same rows and same option wordings |
119
  | --- | --- | --- |
120
+ | terra, danetqa, russe, rwsd, parus, rcb | 100% | 100% |
121
+ | rureviews | 100% | 19.6% |
122
+ | massive_scenario, massive_intent | 63% | 0% |
123
+ | mera_qa | 10.6% | 10.6% |
124
+ | rucola | 9.7% | 9.7% |
125
+ | georeview | 5.3% | 1.5% |
126
+ | toxicity | 2.4% | 0.4% |
127
+
128
+ What this cost in published claims is worth stating plainly. An early draft of
129
+ this card read rucola 0.909 against 0.647 and mera_qa 0.939 against 0.596, both
130
+ of which were v1.1 being scored on its own training data. It also read toxicity
131
+ 0.722 against 0.954 as a gain in capability, when v1.1 scores 0.961 on toxicity
132
+ against the wordings it trained on; 0.722 is that same model with the labels
133
+ renamed. None of those three numbers belonged in a per-task accuracy table.
134
+
135
+ The builder now seeds each carve from the task name, so a wording change in one
136
+ task can no longer move the split in another.
137
+
138
+ ### Six tasks measured on identical rows with identical option wordings
139
+
140
+ | Metric, six comparable tasks | v1.1 | v1.3 |
141
+ | --- | --- | --- |
142
+ | Accuracy (micro) | 0.7948 | 0.7920 |
143
+ | ECE, temperature fitted on the other half | 0.0172 | 0.0160 |
144
+
145
+ Scored on 5,078 decisions, half of the six comparable tasks, with each model's temperature fitted on the other half. The accuracy difference is -0.0028, which is 0.3 standard errors and therefore nothing.
146
+
147
+ | Task | v1.1 | v1.3 | n |
148
+ | --- | --- | --- | --- |
149
+ | russe (word sense) | 0.822 | 0.824 | 8505 |
150
+ | danetqa (yes/no) | 0.742 | 0.753 | 821 |
151
+ | terra (entailment) | 0.775 | 0.733 | 307 |
152
+ | parus (choice) | 0.680 | 0.590 | 100 |
153
+ | rcb (3-way NLI) | 0.450 | 0.509 | 220 |
154
+ | rwsd (coreference) | 0.564 | 0.480 | 204 |
155
+
156
+ On the only ground where the two releases can be compared directly, they are
157
+ the same model for accuracy. v1.3 is not more accurate than v1.1 and this
158
+ release does not claim to be. What it is, is far less dependent on how the
159
+ options are worded, and slightly better calibrated with a temperature of
160
+ 1.8640 against 2.2754, meaning less of the confidence has to be flattened
161
+ away after the fact.
162
+
163
+ ### The remaining seven tasks, each model on its own held-out slice
164
+
165
+ No shared rows exist for these, so each column is that model's own validation
166
+ slice scored with its own option wordings. That is an honest measurement of
167
+ each model separately and a weak comparison between them.
168
+
169
+ | Task | v1.1, own slice | v1.3, own slice | n |
170
+ | --- | --- | --- | --- |
171
+ | massive_intent (60 labels) | not measured | 0.960 | 3000 |
172
+ | massive_scenario (18 labels) | not measured | 0.955 | 3000 |
173
+ | toxicity | 0.961 | 0.954 | 3000 |
174
+ | rureviews (sentiment) | not measured | 0.774 | 3000 |
175
+ | rucola (acceptability) | 0.650 | 0.647 | 1344 |
176
+ | georeview (5 levels) | 0.629 | 0.631 | 3000 |
177
+ | mera_qa (reasoning) | 0.522 | 0.596 | 245 |
178
+
179
+ ## Four epochs were trained and the third was shipped
180
+
181
+ The run went four epochs and each one was kept. The fourth overfits: training
182
+ loss fell to 0.085 while validation loss rose from 0.483 to 0.820, and a
183
+ temperature fitted on it comes out at 3.92, which is what overconfidence looks
184
+ like as a number.
185
+
186
+ | Epoch | Training loss | Validation loss | Validation accuracy |
187
+ | --- | --- | --- | --- |
188
+ | 1 | 0.6029 | 0.5054 | 0.7536 |
189
+ | 2 | 0.4336 | 0.4576 | 0.7920 |
190
+ | 3 | 0.2714 | 0.4831 | 0.8181 |
191
+ | 4 | 0.0849 | 0.8204 | 0.8139 |
192
+
193
+ The shipped weights are the third epoch. Had only the last checkpoint been kept,
194
+ the released model would have been the worse one and nothing would have shown it.
195
 
196
  ## Name your options carefully
197
 
198
+ The model reads the option text, so wording still matters, just much less than
199
+ it did.
 
200
 
201
+ | Option wording | Accuracy | Confident errors |
202
+ | --- | --- | --- |
203
+ | English key, `weather` | 0.962 | 26 |
204
+ | Russian word, `погода` | 0.960 | 19 |
205
+ | Russian phrase, `прогноз погоды` | 0.957 | 14 |
206
+ | Jargon, `вэзер` | 0.855 | 44 |
207
+ | Department name, `метеослужба` | 0.869 | 52 |
208
+ | Descriptive sentence | 0.958 | 16 |
209
+
210
+ The last column counts answers that were wrong while above 0.8 confidence.
211
+ Write options as ordinary words that describe the category. If you use in-house
212
+ jargon, measure it on your own data first with `scripts/robustness.py` from the
213
+ code repository rather than assuming it transfers.
 
 
214
 
215
  ## Withholding an answer
216
 
217
+ A calibrated probability is worth something only if the caller acts on it. Set a
218
+ confidence threshold, answer above it and send the rest to a human. The serving
219
+ API takes a `threshold` and returns the withheld decisions in a review list.
220
 
221
+ | Share answered | Confidence threshold | Accuracy on those | Errors per 1000 |
222
  | --- | --- | --- | --- |
223
+ | 50% | 0.85 | 0.967 | 33 |
224
+ | 60% | 0.77 | 0.947 | 53 |
225
+ | 70% | 0.69 | 0.921 | 79 |
226
+ | 80% | 0.61 | 0.892 | 108 |
227
+ | 90% | 0.54 | 0.859 | 141 |
228
+ | 100% | 0.23 | 0.823 | 177 |
229
 
230
+ ## Limitations
 
231
 
232
+ Hard reasoning tasks with small training sets remain weak and are not fixed by
233
+ anything in this release: `rwsd` coreference, `rcb` three-way inference and
234
+ `parus` causal choice all train on fewer than a thousand rows each. For a 307M
235
+ encoder that is a size limit, not a data-wording limit. A larger backbone was
236
+ tried once, xlm-roberta-large at 560M, and lost to v1.
237
 
238
+ Those same tasks are where the two releases differ most in the comparable
239
+ table, and in both directions: v1.3 is 0.059 above v1.1 on `rcb` and 0.084 and
240
+ 0.090 below it on `rwsd` and `parus`. Their validation slices hold 220, 204 and
241
+ 100 rows, so one standard error is roughly 0.03 to 0.05 and none of those three
242
+ gaps is far outside it. Read them as scatter, not as a trend, and do not plan
243
+ around either direction.
 
 
 
 
 
 
 
 
 
 
244
 
245
+ The model answers only within the option set it is given. It cannot say that
246
+ none of the options fit, so include an explicit "none of these" option when that
247
+ outcome is possible.
248
 
249
+ Probabilities are calibrated in aggregate, not per task. A single temperature
250
+ serves all thirteen tasks, so a task far from the mix average can be
251
+ systematically over or under confident.
 
 
 
 
 
 
252
 
253
  ## Speed on CPU
254
 
255
+ Apple M4, 4 threads, float32, median of 25 runs:
 
 
 
 
 
 
 
256
 
257
+ | Decision | Latency |
258
+ | --- | --- |
259
+ | Short yes/no | 25 ms |
260
+ | Choice from three options | 31 ms |
261
+ | Full 256-token context | 131 ms |
262
 
263
  ## Usage
264
 
265
  ```python
266
+ import json, torch, torch.nn as nn
 
267
  from huggingface_hub import hf_hub_download
268
+ from transformers import AutoModel, AutoTokenizer
269
 
270
  REPO = "besktech/ru-jev"
271
+
272
+
273
+ class Scorer(nn.Module):
274
+ def __init__(self, repo):
275
+ super().__init__()
276
+ self.backbone = AutoModel.from_pretrained(repo)
277
+ self.head = nn.Linear(self.backbone.config.hidden_size, 1)
278
+ self.head.load_state_dict(
279
+ torch.load(hf_hub_download(repo, "head.pt"), map_location="cpu"))
280
+
281
+ def forward(self, ids, mask):
282
+ cls = self.backbone(input_ids=ids, attention_mask=mask).last_hidden_state[:, 0]
283
+ return self.head(cls).squeeze(-1)
284
+
285
+
286
+ calib = json.load(open(hf_hub_download(REPO, "calibration.json")))
287
+ T = calib["temperature"]
288
  tok = AutoTokenizer.from_pretrained(REPO)
289
+ model = Scorer(REPO).eval()
290
+
291
+ context = "Приложение падает при входе после обновления."
292
+ options = ["техническая проблема", "оплата, счета и возвраты", "продажи"]
293
+
294
+ with torch.no_grad():
295
+ enc = tok([context] * len(options), options, truncation=True,
296
+ max_length=calib["max_len"], padding=True, return_tensors="pt")
297
+ probs = torch.softmax(model(enc["input_ids"], enc["attention_mask"]) / T, dim=0)
298
+
299
+ for option, p in zip(options, probs.tolist()):
300
+ print(f"{p:.3f} {option}")
 
 
 
 
301
  ```
302
 
303
+ Divide by the shipped temperature before the softmax. Without it the
304
+ probabilities are overconfident and the threshold logic above does not hold.
305
 
306
+ ## Training data
307
 
308
+ Thirteen tasks from open Russian datasets, 143,991 training and 26,746
309
+ validation decisions. Russian SuperGLUE (`terra`, `danetqa`, `russe`, `rwsd`,
310
+ `parus`, `rcb`), RuCoLA, MERA (`ruopenbookqa`, `ruworldtree`), MTEB Russian
311
+ (`RuReviewsClassification`, `GeoreviewClassification`, `amazon_massive_intent`,
312
+ `amazon_massive_scenario`) and `rudetoxifier_data`. Each source keeps its own
313
+ licence; see `DATASETS.md` in the code repository.
 
314
 
315
+ Every fixed option set ships in several wordings. The builder verifies that all
316
+ thirteen tasks are present and refuses to write the data if any is short, which
317
+ exists because an earlier run silently trained on eleven.
318
 
319
  ## License
320
 
321
+ MIT, weights and code.
322
 
323
  Vladimir Beskorovainyi, Enterprise AI Architect & CTO, author of the Amazon
324
  bestseller "Enterprise AI: The KATA Way", founder of Besk Tech (besk.tech)