Feature Extraction
Transformers
Safetensors
modernbert
typed-decisions
decision-index
decision-model
cross-encoder
text-embeddings-inference
sileod commited on
Commit
46e88e0
·
verified ·
1 Parent(s): d68bc9b

v4: same recipe trained for 48,000 steps on 480k requests (Decision Index 0.3 public 30.90); v3 kept at tag v3

Browse files
Files changed (4) hide show
  1. README.md +14 -13
  2. decider.py +25 -6
  3. decider_config.json +6 -4
  4. model.safetensors +2 -2
README.md CHANGED
@@ -8,14 +8,15 @@ datasets:
8
  tags:
9
  - typed-decisions
10
  - decision-index
11
- - cross-encoder
12
  - decision-model
 
13
  ---
14
 
15
  # tasksource-decider-nano
16
 
17
- This repository was `tasksource/tasksource-jev-nano-v1`. Its `main` (tag `v3`) is the joint cross-encoder trained for
18
- 24,000 steps. The same model trained for 12,000 steps (public index 26.77) stays at tag
 
19
  [`v2`](https://huggingface.co/tasksource/tasksource-decider-nano/tree/v2), and the previous multi-vector model at tag
20
  [`v1`](https://huggingface.co/tasksource/tasksource-decider-nano/tree/v1).
21
 
@@ -25,9 +26,9 @@ generate text.
25
 
26
  | | |
27
  |---|---|
28
- | Decision Index 0.3, public index | **29.05** |
29
  | Requests | 140,171 `ok`, 7 `unsupported` (pairs above 8,192 tokens) |
30
- | Latency, 1× NVIDIA A10 23 GB, one request at a time | median 73 ms · mean 96 ms · p80 95 ms |
31
  | Complete results | [runs/tasksource-decider-nano](https://huggingface.co/datasets/tasksource/decision-index-results/tree/main/runs/tasksource-decider-nano) |
32
 
33
  ## Architecture
@@ -67,8 +68,8 @@ python -m decision_index pipeline --engine decision_index_engine:DeciderEngine \
67
 
68
  ## Training
69
 
70
- 24,000 steps, batches of 8 requests, learning rate 2e-5, inputs up to 2,048 tokens, seed 7. The loss is soft
71
- cross-entropy over each question's options. The training mix has 240,000 requests:
72
 
73
  | Share | Source |
74
  |---:|---|
@@ -87,10 +88,10 @@ request.
87
 
88
  | Area | Skill | Raw |
89
  |---|---:|---:|
90
- | Knowledge & Reasoning | 6.6 | 29.8 |
91
- | Language Understanding | 33.2 | 51.5 |
92
- | Retrieval & Classification | 47.1 | 61.3 |
93
- | Tools & Automation | 43.3 | 48.2 |
94
- | Arts & Human Taste | 14.1 | 43.2 |
95
 
96
- Raw index 46.41; kit `9eb2dbe`, edition 0.3, 140,171 requests scored.
 
8
  tags:
9
  - typed-decisions
10
  - decision-index
 
11
  - decision-model
12
+ - cross-encoder
13
  ---
14
 
15
  # tasksource-decider-nano
16
 
17
+ This repository was `tasksource/tasksource-jev-nano-v1`. Its `main` (tag `v4`) is the joint cross-encoder trained for
18
+ 48,000 steps. The same recipe at 24,000 steps (public index 29.05) stays at tag
19
+ [`v3`](https://huggingface.co/tasksource/tasksource-decider-nano/tree/v3), at 12,000 steps (26.77) at tag
20
  [`v2`](https://huggingface.co/tasksource/tasksource-decider-nano/tree/v2), and the previous multi-vector model at tag
21
  [`v1`](https://huggingface.co/tasksource/tasksource-decider-nano/tree/v1).
22
 
 
26
 
27
  | | |
28
  |---|---|
29
+ | Decision Index 0.3, public index | **30.90** |
30
  | Requests | 140,171 `ok`, 7 `unsupported` (pairs above 8,192 tokens) |
31
+ | Latency, 1× NVIDIA A10 23 GB, one request at a time | median 52 ms · mean 65 ms · p80 63 ms |
32
  | Complete results | [runs/tasksource-decider-nano](https://huggingface.co/datasets/tasksource/decision-index-results/tree/main/runs/tasksource-decider-nano) |
33
 
34
  ## Architecture
 
68
 
69
  ## Training
70
 
71
+ 48,000 steps, batches of 8 requests, learning rate 2e-5, inputs up to 2,048 tokens, seed 7. The loss is soft
72
+ cross-entropy over each question's options. The training mix has 480,000 requests:
73
 
74
  | Share | Source |
75
  |---:|---|
 
88
 
89
  | Area | Skill | Raw |
90
  |---|---:|---:|
91
+ | Knowledge & Reasoning | 7.8 | 30.8 |
92
+ | Language Understanding | 37.7 | 54.7 |
93
+ | Retrieval & Classification | 49.4 | 63.3 |
94
+ | Tools & Automation | 43.6 | 48.3 |
95
+ | Arts & Human Taste | 12.8 | 42.7 |
96
 
97
+ Raw index 47.86; kit `9eb2dbe`, edition 0.3, 140,171 requests scored.
decider.py CHANGED
@@ -106,6 +106,7 @@ class Decider:
106
  self.net = net.to(self.device).eval()
107
  self.layout = self.config.get("layout", "pair")
108
  self.pool = self.config.get("pool", "opt")
 
109
  self.window = getattr(cfg, "sliding_window", None)
110
 
111
  @classmethod
@@ -149,19 +150,35 @@ class Decider:
149
  start += len(opts)
150
  return out
151
 
 
 
 
 
 
 
 
152
  def _score_shared(self, items, row_extra: int = 2048) -> List[np.ndarray]:
153
  torch, tok = self.torch, self.tok
154
  rows, oid, prefix = [], 0, {}
155
- for s, q, opts in items: # one row group per distinct state; rows of max(2P, P + row_extra) tokens
 
 
 
 
 
 
 
 
156
  p = prefix.setdefault(s, tok(s)["input_ids"])
 
157
  qi = tok(f"Question: {q}\nOption:", add_special_tokens=False)["input_ids"]
158
  ob = [tok(" " + o, add_special_tokens=False)["input_ids"] + [tok.sep_token_id] for o in opts]
159
  longest = len(p) + len(qi) + max(map(len, ob))
160
  if longest > self.max_length:
161
  raise TooLong(f"a (state, question, option) pair needs {longest} tokens > {self.max_length}")
162
  cap = max(2 * len(p), len(p) + row_extra)
163
- if not rows or rows[-1][0] is not p or rows[-1][2] + len(qi) + len(ob[0]) > cap:
164
- rows.append([p, [], len(p)])
165
  k = 0
166
  while k < len(ob):
167
  row = rows[-1]
@@ -170,7 +187,7 @@ class Decider:
170
  take.append((oid + k + len(take), ob[k + len(take)]))
171
  size += len(take[-1][1])
172
  if row[1] and row[2] + size > cap:
173
- rows.append([p, [], len(p)])
174
  continue
175
  row[1].append((qi, take))
176
  row[2] += size
@@ -193,7 +210,8 @@ class Decider:
193
  qix = torch.full((B, L), -1, dtype=torch.long)
194
  oix = torch.full((B, L), -1, dtype=torch.long)
195
  spans = []
196
- for r, (p, blocks, _) in enumerate(batch):
 
197
  ids[r, :len(p)], pos[r, :len(p)], kind[r, :len(p)] = torch.tensor(p), torch.arange(len(p)), 0
198
  c = len(p)
199
  for b, (qi, take) in enumerate(blocks):
@@ -208,11 +226,12 @@ class Decider:
208
  kind[r, c:c + m], qix[r, c:c + m], oix[r, c:c + m] = 2, b, o
209
  spans.append((o, r, c, m, len(p), (r, len(p) + sum(len(bb[0]) + sum(len(y) for _, y in bb[1]) for bb in blocks[:b])), n))
210
  c += m
211
- ids, pos, kind, qix, oix = (t.to(self.device) for t in (ids, pos, kind, qix, oix))
212
  ki, kj = kind[:, :, None], kind[:, None, :]
213
  allow = (kj == 0) & (ki >= 0)
214
  allow |= (kj == 1) & (ki >= 1) & (qix[:, :, None] == qix[:, None, :])
215
  allow |= (kj == 2) & (ki == 2) & (oix[:, :, None] == oix[:, None, :])
 
216
  allow |= torch.eye(L, dtype=torch.bool, device=self.device)[None]
217
  masks = {"full_attention": allow[:, None]}
218
  if self.window is not None:
 
106
  self.net = net.to(self.device).eval()
107
  self.layout = self.config.get("layout", "pair")
108
  self.pool = self.config.get("pool", "opt")
109
+ self.attn = self.config.get("attn", "cached") # shared layout: "cached" | "full" | "auto"
110
  self.window = getattr(cfg, "sliding_window", None)
111
 
112
  @classmethod
 
150
  start += len(opts)
151
  return out
152
 
153
+ def _full(self, n_questions: int, state_len: int, auto_extra: int = 4096) -> bool:
154
+ """cached: the state reads only itself and is shared by all questions of a request; full: one row per
155
+ question, the state also reads its question; auto: full when that costs <= auto_extra extra tokens."""
156
+ if self.attn == "auto":
157
+ return (n_questions - 1) * state_len <= auto_extra
158
+ return self.attn == "full"
159
+
160
  def _score_shared(self, items, row_extra: int = 2048) -> List[np.ndarray]:
161
  torch, tok = self.torch, self.tok
162
  rows, oid, prefix = [], 0, {}
163
+ runs = [] # questions per request = consecutive items with the same state
164
+ for s, _, _ in items:
165
+ if runs and runs[-1][0] == s:
166
+ runs[-1][1] += 1
167
+ else:
168
+ runs.append([s, 1])
169
+ nq = [n for _, n in runs for _ in range(n)]
170
+ first = [k == 0 for _, n in runs for k in range(n)]
171
+ for (s, q, opts), n_q, new_req in zip(items, nq, first): # rows of max(2P, P + row_extra) tokens
172
  p = prefix.setdefault(s, tok(s)["input_ids"])
173
+ full = self._full(n_q, len(p))
174
  qi = tok(f"Question: {q}\nOption:", add_special_tokens=False)["input_ids"]
175
  ob = [tok(" " + o, add_special_tokens=False)["input_ids"] + [tok.sep_token_id] for o in opts]
176
  longest = len(p) + len(qi) + max(map(len, ob))
177
  if longest > self.max_length:
178
  raise TooLong(f"a (state, question, option) pair needs {longest} tokens > {self.max_length}")
179
  cap = max(2 * len(p), len(p) + row_extra)
180
+ if not rows or rows[-1][0] is not p or new_req or full or rows[-1][2] + len(qi) + len(ob[0]) > cap:
181
+ rows.append([p, [], len(p), full])
182
  k = 0
183
  while k < len(ob):
184
  row = rows[-1]
 
187
  take.append((oid + k + len(take), ob[k + len(take)]))
188
  size += len(take[-1][1])
189
  if row[1] and row[2] + size > cap:
190
+ rows.append([p, [], len(p), full])
191
  continue
192
  row[1].append((qi, take))
193
  row[2] += size
 
210
  qix = torch.full((B, L), -1, dtype=torch.long)
211
  oix = torch.full((B, L), -1, dtype=torch.long)
212
  spans = []
213
+ rowfull = torch.tensor([r[3] for r in batch], dtype=torch.bool)
214
+ for r, (p, blocks, _, _) in enumerate(batch):
215
  ids[r, :len(p)], pos[r, :len(p)], kind[r, :len(p)] = torch.tensor(p), torch.arange(len(p)), 0
216
  c = len(p)
217
  for b, (qi, take) in enumerate(blocks):
 
226
  kind[r, c:c + m], qix[r, c:c + m], oix[r, c:c + m] = 2, b, o
227
  spans.append((o, r, c, m, len(p), (r, len(p) + sum(len(bb[0]) + sum(len(y) for _, y in bb[1]) for bb in blocks[:b])), n))
228
  c += m
229
+ ids, pos, kind, qix, oix, rowfull = (t.to(self.device) for t in (ids, pos, kind, qix, oix, rowfull))
230
  ki, kj = kind[:, :, None], kind[:, None, :]
231
  allow = (kj == 0) & (ki >= 0)
232
  allow |= (kj == 1) & (ki >= 1) & (qix[:, :, None] == qix[:, None, :])
233
  allow |= (kj == 2) & (ki == 2) & (oix[:, :, None] == oix[:, None, :])
234
+ allow |= rowfull[:, None, None] & (ki == 0) & (kj == 1) # full rows: the state reads its question
235
  allow |= torch.eye(L, dtype=torch.bool, device=self.device)[None]
236
  masks = {"full_attention": allow[:, None]}
237
  if self.window is not None:
decider_config.json CHANGED
@@ -4,19 +4,19 @@
4
  "base": "cross-encoder/ettin-reranker-150m-v1",
5
  "train_max_length": 2048,
6
  "inference_max_length": 8192,
7
- "step": 24000,
8
  "train": {
9
  "arch": "joint",
10
  "opt_pool": "marker",
11
  "shuffle_options": false,
12
- "manifest": "runs/manifests/mix_all.jsonl",
13
  "extra_manifest": null,
14
  "extra_frac": 0.0,
15
  "lr": 2e-05,
16
  "batch_requests": 8,
17
  "max_k": 32,
18
  "seed": 7,
19
- "total_steps": 24000,
20
  "max_length": 2048,
21
  "pack": 0,
22
  "init": null,
@@ -28,5 +28,7 @@
28
  "tok_budget": 12288
29
  },
30
  "layout": "shared",
31
- "pool": "opt"
 
 
32
  }
 
4
  "base": "cross-encoder/ettin-reranker-150m-v1",
5
  "train_max_length": 2048,
6
  "inference_max_length": 8192,
7
+ "step": 48000,
8
  "train": {
9
  "arch": "joint",
10
  "opt_pool": "marker",
11
  "shuffle_options": false,
12
+ "manifest": "runs/manifests/mix_all_480k.jsonl",
13
  "extra_manifest": null,
14
  "extra_frac": 0.0,
15
  "lr": 2e-05,
16
  "batch_requests": 8,
17
  "max_k": 32,
18
  "seed": 7,
19
+ "total_steps": 48000,
20
  "max_length": 2048,
21
  "pack": 0,
22
  "init": null,
 
28
  "tok_budget": 12288
29
  },
30
  "layout": "shared",
31
+ "pool": "opt",
32
+ "attn": "cached",
33
+ "weights_dtype": "bf16"
34
  }
model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:ca302526bc031a820677508630f72806c7f757fe25191d5eb1e154f658191717
3
- size 596074556
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e26e29f12339163b473f985270ebb4b9b3b90ee3b7ffd488715d45550842bef4
3
+ size 298044578