cfahlgren1 HF Staff commited on
Commit
dc714b2
·
verified ·
1 Parent(s): 4dece7f

tinynext-v2-3b: 12.5M params, 56 min on GPU

Browse files
v2/experiments/tinynext-v2-3b/config.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "vocab": 8192,
3
+ "dim": 320,
4
+ "layers": 8,
5
+ "heads": 5,
6
+ "ffn": 864,
7
+ "parameters": 12539200,
8
+ "seq": 512,
9
+ "special_tokens": {
10
+ "<|endoftext|>": 0,
11
+ "<|pre|>": 1,
12
+ "<|suf|>": 2,
13
+ "<|mid|>": 3
14
+ },
15
+ "prompt": "No text after the cursor: the text before it. Otherwise: <|suf|> after <|pre|> before <|mid|>."
16
+ }
v2/experiments/tinynext-v2-3b/eval.py ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.11,<3.14"
3
+ # dependencies = [
4
+ # "torch>=2.6,<2.8",
5
+ # "datasets>=3.0",
6
+ # "numpy<3",
7
+ # "onnxruntime>=1.20",
8
+ # "transformers>=4.46",
9
+ # "tokenizers>=0.20",
10
+ # ]
11
+ # ///
12
+ """Scores autocomplete models the way people use them: type real text, pause, and see if the gray suggestion is right.
13
+
14
+ Every model sees the same pauses in the same text and is decoded with the same rule as offline.page:
15
+ follow the model's top pick while the running product of probabilities stays above a cutoff, and show
16
+ the text up to the last finished word. A suggestion is right when the text really continues that way.
17
+
18
+ Two scenarios:
19
+ typing the cursor is at the end of what's written so far.
20
+ editing the cursor is mid-sentence and real text follows a gap of 2-6 words. Only fill-in-the-middle
21
+ models are given that text; the rest see only what's before the cursor.
22
+
23
+ Usage:
24
+ uv run eval.py --model tinynext-v1 --onnx model_quint8.onnx
25
+ uv run eval.py --model smollm2-135m --hf HuggingFaceTB/SmolLM2-135M
26
+ uv run eval.py --table # compare every metrics/*.json
27
+ """
28
+
29
+ import argparse
30
+ import hashlib
31
+ import json
32
+ import random
33
+ import re
34
+ import statistics
35
+ import time
36
+ from collections.abc import Callable
37
+ from dataclasses import dataclass, field
38
+ from pathlib import Path
39
+
40
+ import numpy as np
41
+
42
+ HERE = Path(__file__).parent
43
+ EVAL_SET = HERE / "eval_set.json"
44
+ METRICS = HERE / "metrics"
45
+
46
+ CUTOFFS = (0.3, 0.4, 0.5, 0.6, 0.7)
47
+ MAIN_CUTOFF = 0.5 # what offline.page uses
48
+ PAUSE_EVERY = 12 # characters between pauses
49
+ MIN_SHOWN_CHARS = 4 # offline.page hides shorter suggestions
50
+ MAX_WORDS = 6
51
+ MAX_CHARS = 48
52
+ DOMAINS = ("articles", "messages", "email")
53
+ SCENARIOS = ("typing", "editing")
54
+ PUNCTUATION = ".,!?;:"
55
+
56
+ # ---- Eval text -------------------------------------------------------------------------------------------------
57
+ # Kept apart from training: Wikipedia isn't in either model's data, OpenAssistant validation isn't in SmolTalk's
58
+ # training split, and the emails come from the senders the original enron-autocomplete held out for its test set.
59
+
60
+ CUT_REPLY = re.compile(
61
+ r"(?im)^\s*(?:-+\s*original message|-+\s*forwarded by|-+\s*inline attachment|from:\s|to:\s|sent:\s"
62
+ r"|subject:\s|cc:\s|.*\bwrote:\s*$|_{5,}|-{5,}|={5,}|\*{5,}|\d{1,2}/\d{1,2}/\d{2,4}\s+\d{1,2}:\d{2})"
63
+ )
64
+ SENSITIVE = re.compile(r"(?i)https?://|www\.|\S+@\S+|\(?\b\d{3}\)?[-. ]\d{3}[-. ]\d{4}\b|\b\d{3}-\d{2}-\d{4}\b")
65
+ BULK_SENDER = re.compile(r"(?i)announce|newsletter|mailer|outlook\.team|^all[._]|general\.|chairman|enron\.(?:on|net)")
66
+ SMART_CHARS = str.maketrans({"‘": "'", "’": "'", "“": '"', "”": '"', "–": "-", "—": "-", "\t": " "})
67
+
68
+
69
+ def clean_email(body: str) -> list[str]:
70
+ """The same cleaning enron-autocomplete was trained with."""
71
+ text = body.replace("\r\n", "\n").replace("=20", " ").replace("=\n", "").translate(SMART_CHARS)
72
+ text = "\n".join(line for line in text.split("\n") if not line.lstrip().startswith(">"))
73
+ cut = CUT_REPLY.search(text)
74
+ if cut:
75
+ text = text[: cut.start()]
76
+ text = re.sub(r"(?m)^-- *\n[\s\S]*", "", text)
77
+ paragraphs = []
78
+ for block in re.split(r"\n\s*\n", text):
79
+ lines = [re.sub(r" +", " ", line).strip() for line in block.split("\n") if line.strip()]
80
+ if not lines:
81
+ continue
82
+ joined = lines[0]
83
+ for previous, line in zip(lines, lines[1:]):
84
+ joined += (" " if len(previous) >= 50 else "\n") + line
85
+ if SENSITIVE.search(joined) or sum(c.isalpha() for c in joined) < 0.6 * len(joined):
86
+ continue
87
+ paragraphs.append(joined)
88
+ return paragraphs
89
+
90
+
91
+ def is_held_out_sender(sender: str) -> bool:
92
+ """enron-autocomplete put senders whose hash bucket is under 5 in its test split."""
93
+ sender = sender.lower()
94
+ if "@enron.com" not in sender or BULK_SENDER.search(sender):
95
+ return False
96
+ return int(hashlib.sha1(sender.encode()).hexdigest(), 16) % 100 < 5
97
+
98
+
99
+ def ascii_paragraphs(text: str, min_chars: int = 40) -> list[str]:
100
+ text = text.translate(SMART_CHARS).encode("ascii", "ignore").decode()
101
+ return [p.strip() for p in re.split(r"\n\s*\n|\n", text) if len(p.strip()) >= min_chars]
102
+
103
+
104
+ def build_eval_set(docs_per_domain: int = 80) -> dict[str, list[list[str]]]:
105
+ from datasets import load_dataset
106
+
107
+ sets: dict[str, list[list[str]]] = {domain: [] for domain in DOMAINS}
108
+ wiki = load_dataset(
109
+ "wikimedia/wikipedia", data_files="20231101.en/train-00040-of-00041.parquet", split="train", streaming=True
110
+ )
111
+ for row in wiki:
112
+ paragraphs = [p for p in ascii_paragraphs(row["text"], 80) if not p.endswith(":")][:6]
113
+ if len(paragraphs) >= 3:
114
+ sets["articles"].append(paragraphs)
115
+ if len(sets["articles"]) >= docs_per_domain:
116
+ break
117
+
118
+ for row in load_dataset("OpenAssistant/oasst1", split="validation"):
119
+ if row["lang"] == "en" and "```" not in row["text"]:
120
+ paragraphs = ascii_paragraphs(row["text"])[:6]
121
+ if paragraphs:
122
+ sets["messages"].append(paragraphs)
123
+ if len(sets["messages"]) >= docs_per_domain:
124
+ break
125
+
126
+ for row in load_dataset("corbt/enron-emails", split="train", streaming=True):
127
+ if is_held_out_sender(row["from"] or ""):
128
+ paragraphs = [p for p in clean_email(row["body"] or "") if len(p) >= 40][:6]
129
+ if paragraphs:
130
+ sets["email"].append(paragraphs)
131
+ if len(sets["email"]) >= docs_per_domain:
132
+ break
133
+ return sets
134
+
135
+
136
+ def load_eval_set() -> dict[str, list[list[str]]]:
137
+ if not EVAL_SET.exists():
138
+ EVAL_SET.write_text(json.dumps(build_eval_set(), indent=1))
139
+ return json.loads(EVAL_SET.read_text())
140
+
141
+
142
+ @dataclass
143
+ class Pause:
144
+ domain: str
145
+ scenario: str
146
+ before: str # the document up to the cursor
147
+ after: str # text after the cursor that a model may see (empty when typing)
148
+ truth: str # what the person actually typed next
149
+
150
+
151
+ def pauses(eval_set: dict[str, list[list[str]]], max_per_domain: int) -> list[Pause]:
152
+ rng = random.Random(0)
153
+ found: list[Pause] = []
154
+ for domain in DOMAINS:
155
+ in_domain: list[Pause] = []
156
+ for paragraphs in eval_set[domain]:
157
+ for index, paragraph in enumerate(paragraphs):
158
+ earlier = "\n".join(paragraphs[:index])
159
+ for at in range(PAUSE_EVERY, len(paragraph) - 8, PAUSE_EVERY):
160
+ before = (earlier + "\n" if earlier else "") + paragraph[:at]
161
+ truth = paragraph[at:]
162
+ in_domain.append(Pause(domain, "typing", before, "", truth))
163
+ # Editing: a gap of 2-6 words, then the rest of the paragraph as it was written.
164
+ gap_words = rng.randint(2, 6)
165
+ gap = re.match(r"(\S*\s+){%d}" % gap_words, truth)
166
+ if gap and len(truth) > gap.end() + 10:
167
+ in_domain.append(Pause(domain, "editing", before, truth[gap.end():], truth))
168
+ rng.shuffle(in_domain)
169
+ found += [p for p in in_domain if p.scenario == "typing"][:max_per_domain]
170
+ found += [p for p in in_domain if p.scenario == "editing"][:max_per_domain]
171
+ return found
172
+
173
+
174
+ # ---- Decoding -------------------------------------------------------------------------------------------------
175
+
176
+
177
+ @dataclass
178
+ class Trajectory:
179
+ """Suggestions that end at a word boundary, with the model's confidence in each. Greedy decoding gives the
180
+ same path for every cutoff, so one decode answers all of them."""
181
+
182
+ candidates: list[tuple[str, float]] = field(default_factory=list)
183
+ milliseconds: float = 0.0
184
+
185
+ def at(self, cutoff: float) -> str:
186
+ best = ""
187
+ for text, confidence in self.candidates:
188
+ if confidence >= cutoff:
189
+ best = text
190
+ return best.rstrip()
191
+
192
+
193
+ class Model:
194
+ name: str
195
+ sees_after: bool = False
196
+
197
+ def suggest(self, before: str, after: str) -> Trajectory:
198
+ raise NotImplementedError
199
+
200
+
201
+ class CharGRU(Model):
202
+ """enron-autocomplete / tinynext v1: one character per step, reading only the current paragraph."""
203
+
204
+ HIDDEN, END, NEWLINE, FIRST, OFFSET = 512, 1, 2, 3, 29
205
+
206
+ def __init__(self, name: str, onnx_path: str):
207
+ import onnxruntime as ort
208
+
209
+ self.name = name
210
+ self.session = ort.InferenceSession(onnx_path, providers=["CPUExecutionProvider"])
211
+ self.read_text: str | None = None
212
+ self.reading = None
213
+
214
+ def step(self, char_id: int, state):
215
+ logits, state = self.session.run(None, {"input_id": np.array([char_id], np.int64), "state": state})
216
+ return logits[0], state
217
+
218
+ def encode(self, char: str) -> int | None:
219
+ if char == "\n":
220
+ return self.NEWLINE
221
+ code = ord(char)
222
+ return code - self.OFFSET if 32 <= code <= 126 else None
223
+
224
+ def read(self, text: str):
225
+ if self.read_text is None or not text.startswith(self.read_text):
226
+ self.reading = self.step(self.END, np.zeros((1, self.HIDDEN), np.float32))
227
+ self.read_text = ""
228
+ for char in text[len(self.read_text):]:
229
+ char_id = self.encode(char)
230
+ if char_id is not None:
231
+ self.reading = self.step(char_id, self.reading[1])
232
+ self.read_text = text
233
+ return self.reading
234
+
235
+ def suggest(self, before: str, after: str) -> Trajectory:
236
+ began = time.perf_counter()
237
+ logits, state = self.read(before.split("\n")[-1])
238
+ trajectory, run, confidence, words = Trajectory(), "", 1.0, 0
239
+ while len(run) < MAX_CHARS and confidence >= min(CUTOFFS):
240
+ probabilities = np.exp(logits - logits.max())
241
+ probabilities /= probabilities.sum()
242
+ char_id = int(probabilities.argmax())
243
+ if char_id < self.FIRST:
244
+ if run:
245
+ trajectory.candidates.append((run, confidence))
246
+ break
247
+ char = chr(char_id + self.OFFSET)
248
+ if char == " ":
249
+ trajectory.candidates.append((run, confidence))
250
+ words += 1
251
+ if words >= MAX_WORDS:
252
+ break
253
+ confidence *= float(probabilities[char_id])
254
+ run += char
255
+ if char in PUNCTUATION:
256
+ trajectory.candidates.append((run, confidence))
257
+ logits, state = self.step(char_id, state)
258
+ trajectory.milliseconds = (time.perf_counter() - began) * 1000
259
+ return trajectory
260
+
261
+
262
+ class TokenLM(Model):
263
+ """A word-piece model. `next_logits(ids, cache)` runs one forward pass and returns (last logits, cache).
264
+
265
+ The word being typed is "healed": the prompt stops before it, and the first generated pieces must spell it
266
+ out, so a half-typed word like "resig" can still become "resignation" in one piece.
267
+ """
268
+
269
+ def __init__(self, name: str, next_logits: Callable, encode: Callable[[str], list[int]], token_texts: list[str],
270
+ stop_ids: set[int], prompt: Callable[[list[int], list[int]], list[int]] | None = None,
271
+ context_chars: int = 1500, suffix_chars: int = 300):
272
+ self.name = name
273
+ self.next_logits = next_logits
274
+ self.encode = encode
275
+ self.token_texts = token_texts
276
+ self.stop_ids = stop_ids
277
+ self.prompt = prompt
278
+ self.sees_after = prompt is not None
279
+ self.context_chars = context_chars
280
+ self.suffix_chars = suffix_chars
281
+ lengths = np.array([len(t) for t in token_texts])
282
+ self.nonempty = lengths > 0
283
+
284
+ def allowed(self, need: str) -> np.ndarray:
285
+ """Pieces consistent with the rest of the half-typed word."""
286
+ return np.array([bool(t) and (need.startswith(t) or t.startswith(need)) for t in self.token_texts])
287
+
288
+ def suggest(self, before: str, after: str) -> Trajectory:
289
+ began = time.perf_counter()
290
+ before = before[-self.context_chars:]
291
+ # The half-typed word keeps its leading space, since word pieces start with one. A line break stays in the prompt.
292
+ space, newline = before.rfind(" "), before.rfind("\n")
293
+ if space > newline:
294
+ stem, need = before[:space], before[space:]
295
+ else:
296
+ stem, need = before[: newline + 1], before[newline + 1:]
297
+ prefix_ids = self.encode(stem)
298
+ if self.prompt:
299
+ ids = self.prompt(prefix_ids, self.encode(after[: self.suffix_chars]) if after else [])
300
+ else:
301
+ ids = prefix_ids
302
+ logits, cache = self.next_logits(ids, None)
303
+
304
+ trajectory, generated, confidence = Trajectory(), "", 1.0
305
+ for _ in range(16):
306
+ logits = logits.astype(np.float64)
307
+ if need:
308
+ mask = self.allowed(need)
309
+ if not mask.any():
310
+ break
311
+ logits = np.where(mask, logits, -np.inf)
312
+ probabilities = np.exp(logits - logits.max())
313
+ probabilities /= probabilities.sum()
314
+ token = int(probabilities.argmax())
315
+ text = self.token_texts[token]
316
+ if token in self.stop_ids or "\n" in text:
317
+ if generated:
318
+ trajectory.candidates.append((generated, confidence))
319
+ break
320
+ if need:
321
+ consumed = min(len(need), len(text))
322
+ text, need = text[consumed:], need[consumed:]
323
+ if text[:1].isspace() and generated:
324
+ trajectory.candidates.append((generated, confidence))
325
+ confidence *= float(probabilities[token])
326
+ generated += text
327
+ if generated.rstrip()[-1:] in PUNCTUATION and generated.strip():
328
+ trajectory.candidates.append((generated, confidence))
329
+ if confidence < min(CUTOFFS) or len(generated) > MAX_CHARS or len(generated.split()) > MAX_WORDS:
330
+ break
331
+ logits, cache = self.next_logits([token], cache)
332
+ trajectory.milliseconds = (time.perf_counter() - began) * 1000
333
+ return trajectory
334
+
335
+
336
+ def hf_causal_lm(name: str, repo: str) -> TokenLM:
337
+ import torch
338
+ from transformers import AutoModelForCausalLM, AutoTokenizer
339
+
340
+ device = "mps" if torch.backends.mps.is_available() else "cuda" if torch.cuda.is_available() else "cpu"
341
+ tokenizer = AutoTokenizer.from_pretrained(repo)
342
+ model = AutoModelForCausalLM.from_pretrained(repo, torch_dtype=torch.float32).to(device).eval()
343
+
344
+ @torch.no_grad()
345
+ def next_logits(ids, cache):
346
+ out = model(torch.tensor([ids], device=device), past_key_values=cache, use_cache=True)
347
+ return out.logits[0, -1].float().cpu().numpy(), out.past_key_values
348
+
349
+ texts = [tokenizer.decode([i]) for i in range(len(tokenizer))]
350
+ stops = {tokenizer.eos_token_id} if tokenizer.eos_token_id is not None else set()
351
+ encode = lambda text: tokenizer.encode(text, add_special_tokens=False) or [tokenizer.eos_token_id]
352
+ return TokenLM(name, next_logits, encode, texts, stops)
353
+
354
+
355
+ def onnx_token_lm(name: str, folder: str, fim: bool) -> TokenLM:
356
+ """A tinynext v2 export: graph.onnx + weights.f32 (compressed, expanded) or model.onnx, plus tokenizer.json."""
357
+ import onnxruntime as ort
358
+ from tokenizers import Tokenizer
359
+
360
+ folder = Path(folder)
361
+ graph = folder / "graph.onnx" if (folder / "graph.onnx").exists() else folder / "model.onnx"
362
+ session = ort.InferenceSession(str(graph), providers=["CPUExecutionProvider"])
363
+ config = json.loads((folder / "config.json").read_text())
364
+ tokenizer = Tokenizer.from_file(str(folder / "tokenizer.json"))
365
+ empty = np.zeros((config["layers"], 1, config["heads"], 0, config["dim"] // config["heads"]), np.float32)
366
+
367
+ def next_logits(ids, cache):
368
+ past_k, past_v = cache if cache else (empty, empty)
369
+ logits, k, v = session.run(None, {"input_ids": np.array([ids], np.int64), "past_k": past_k, "past_v": past_v})
370
+ return logits[0], (k, v)
371
+
372
+ special = len(config["special_tokens"])
373
+ texts = [tokenizer.decode([i], skip_special_tokens=False) if i >= special else "" for i in range(config["vocab"])]
374
+ encode = lambda text: tokenizer.encode(text).ids or [0]
375
+ prompt = (lambda prefix, suffix: prefix if not suffix else [2, *suffix, 1, *prefix, 3]) if fim else None
376
+ return TokenLM(name, next_logits, encode, texts, set(range(special)), prompt=prompt)
377
+
378
+
379
+ # ---- Scoring ---------------------------------------------------------------------------------------------------
380
+
381
+
382
+ def word_by_word(suggestion: str, truth: str) -> int:
383
+ """Characters you'd keep accepting a word at a time until a word is wrong."""
384
+ kept = 0
385
+ for match in re.finditer(r"\s*\S+", suggestion):
386
+ if truth.startswith(suggestion[: match.end()]):
387
+ kept = match.end()
388
+ else:
389
+ break
390
+ return kept
391
+
392
+
393
+ def score(model: Model, all_pauses: list[Pause]) -> dict:
394
+ results: dict = {"model": model.name, "sees_after": model.sees_after, "cutoffs": {}, "examples": [], "latency_ms": {}}
395
+ latencies: list[float] = []
396
+ raw = []
397
+ for index, pause in enumerate(all_pauses):
398
+ trajectory = model.suggest(pause.before, pause.after if model.sees_after else "")
399
+ latencies.append(trajectory.milliseconds)
400
+ raw.append((pause, trajectory))
401
+ if index % 500 == 0:
402
+ print(f" {model.name}: {index}/{len(all_pauses)} pauses, median {statistics.median(latencies):.1f} ms", flush=True)
403
+
404
+ for cutoff in CUTOFFS:
405
+ table: dict = {}
406
+ for domain in DOMAINS:
407
+ for scenario in SCENARIOS:
408
+ rows = [(p, t.at(cutoff)) for p, t in raw if p.domain == domain and p.scenario == scenario]
409
+ if not rows:
410
+ continue
411
+ shown = [(p, s) for p, s in rows if len(s) >= MIN_SHOWN_CHARS]
412
+ right = [(p, s) for p, s in shown if p.truth.startswith(s)]
413
+ multi = [(p, s) for p, s in shown if len(s.split()) >= 2]
414
+ multi_right = [(p, s) for p, s in multi if p.truth.startswith(s)]
415
+ typed = len(rows) * PAUSE_EVERY
416
+ table[f"{domain}/{scenario}"] = {
417
+ "pauses": len(rows),
418
+ "shown": round(len(shown) / len(rows), 4),
419
+ "right": round(len(right) / max(1, len(shown)), 4),
420
+ "avg_right_chars": round(sum(len(s) for _, s in right) / max(1, len(right)), 2),
421
+ "multiword_shown": round(len(multi) / len(rows), 4),
422
+ "multiword_right": round(len(multi_right) / max(1, len(multi)), 4),
423
+ "saved_with_tab": round(sum(len(s) for _, s in right) / typed, 4),
424
+ "saved_word_by_word": round(sum(word_by_word(s, p.truth) for p, s in shown) / typed, 4),
425
+ }
426
+ results["cutoffs"][str(cutoff)] = table
427
+
428
+ for pause, trajectory in raw[:: max(1, len(raw) // 60)]:
429
+ suggestion = trajectory.at(MAIN_CUTOFF)
430
+ if len(suggestion) >= MIN_SHOWN_CHARS:
431
+ results["examples"].append({
432
+ "domain": pause.domain, "scenario": pause.scenario, "before": pause.before[-60:],
433
+ "suggestion": suggestion, "truth": pause.truth[: len(suggestion) + 12], "right": pause.truth.startswith(suggestion),
434
+ })
435
+ results["latency_ms"] = {
436
+ "p50": round(statistics.median(latencies), 2),
437
+ "p95": round(float(np.percentile(latencies, 95)), 2),
438
+ "note": "Measured in this eval process, not in a browser.",
439
+ }
440
+ return results
441
+
442
+
443
+ # ---- Report ----------------------------------------------------------------------------------------------------
444
+
445
+
446
+ def table(paths: list[Path]) -> str:
447
+ runs = [json.loads(p.read_text()) for p in paths]
448
+ lines = []
449
+ for scenario in SCENARIOS:
450
+ lines.append(f"\n### {scenario.capitalize()}, {int(MAIN_CUTOFF * 100)}% cutoff, suggestions of {MIN_SHOWN_CHARS}+ characters\n")
451
+ lines.append("| Model | Domain | Shown | Right | Avg right | 2+ words shown | 2+ words right | Saved (Tab) | Saved (word by word) |")
452
+ lines.append("|---|---|---|---|---|---|---|---|---|")
453
+ for run in runs:
454
+ for domain in DOMAINS:
455
+ m = run["cutoffs"][str(MAIN_CUTOFF)].get(f"{domain}/{scenario}")
456
+ if not m:
457
+ continue
458
+ lines.append(
459
+ f"| {run['model']} | {domain} | {m['shown']:.0%} | {m['right']:.0%} | {m['avg_right_chars']:.1f} ch "
460
+ f"| {m['multiword_shown']:.0%} | {m['multiword_right']:.0%} | {m['saved_with_tab']:.1%} | {m['saved_word_by_word']:.1%} |"
461
+ )
462
+ lines.append("\n| Model | Latency p50 | p95 |\n|---|---|---|")
463
+ for run in runs:
464
+ lines.append(f"| {run['model']} | {run['latency_ms']['p50']} ms | {run['latency_ms']['p95']} ms |")
465
+ return "\n".join(lines)
466
+
467
+
468
+ def main():
469
+ parser = argparse.ArgumentParser()
470
+ parser.add_argument("--model", help="Name for the metrics file")
471
+ parser.add_argument("--onnx", help="A character-level GRU exported like enron-autocomplete")
472
+ parser.add_argument("--hf", help="A Hugging Face causal LM to evaluate, e.g. HuggingFaceTB/SmolLM2-135M")
473
+ parser.add_argument("--onnx-lm", help="Folder with a tinynext v2 ONNX export, config.json and tokenizer.json")
474
+ parser.add_argument("--no-fim", action="store_true", help="With --onnx-lm: the model wasn't trained to fill in the middle")
475
+ parser.add_argument("--max-pauses", type=int, default=1000, help="Per domain and scenario")
476
+ parser.add_argument("--table", action="store_true", help="Print a comparison of every metrics/*.json")
477
+ args = parser.parse_args()
478
+
479
+ if args.table:
480
+ print(table(sorted(METRICS.glob("*.json"))))
481
+ return
482
+ if args.onnx:
483
+ model: Model = CharGRU(args.model, args.onnx)
484
+ elif args.hf:
485
+ model = hf_causal_lm(args.model, args.hf)
486
+ elif args.onnx_lm:
487
+ model = onnx_token_lm(args.model, args.onnx_lm, fim=not args.no_fim)
488
+ else:
489
+ parser.error("Pass --onnx, --hf or --table. train.py evaluates its own model through score().")
490
+ all_pauses = pauses(load_eval_set(), args.max_pauses)
491
+ print(f"{len(all_pauses)} pauses", flush=True)
492
+ results = score(model, all_pauses)
493
+ METRICS.mkdir(exist_ok=True)
494
+ (METRICS / f"{args.model}.json").write_text(json.dumps(results, indent=1))
495
+ print(table([METRICS / f"{args.model}.json"]))
496
+
497
+
498
+ if __name__ == "__main__":
499
+ main()
v2/experiments/tinynext-v2-3b/eval_set.json ADDED
The diff for this file is too large to render. See raw diff
 
v2/experiments/tinynext-v2-3b/metrics/tinynext-v2-3b.json ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "tinynext-v2-3b",
3
+ "sees_after": true,
4
+ "cutoffs": {
5
+ "0.3": {
6
+ "articles/typing": {
7
+ "pauses": 1000,
8
+ "shown": 0.204,
9
+ "right": 0.5882,
10
+ "avg_right_chars": 6.04,
11
+ "multiword_shown": 0.086,
12
+ "multiword_right": 0.5465,
13
+ "saved_with_tab": 0.0604,
14
+ "saved_word_by_word": 0.0679
15
+ },
16
+ "articles/editing": {
17
+ "pauses": 1000,
18
+ "shown": 0.152,
19
+ "right": 0.6382,
20
+ "avg_right_chars": 5.86,
21
+ "multiword_shown": 0.062,
22
+ "multiword_right": 0.629,
23
+ "saved_with_tab": 0.0473,
24
+ "saved_word_by_word": 0.0516
25
+ },
26
+ "messages/typing": {
27
+ "pauses": 1000,
28
+ "shown": 0.324,
29
+ "right": 0.5463,
30
+ "avg_right_chars": 7.8,
31
+ "multiword_shown": 0.18,
32
+ "multiword_right": 0.5333,
33
+ "saved_with_tab": 0.115,
34
+ "saved_word_by_word": 0.1404
35
+ },
36
+ "messages/editing": {
37
+ "pauses": 1000,
38
+ "shown": 0.317,
39
+ "right": 0.5584,
40
+ "avg_right_chars": 7.47,
41
+ "multiword_shown": 0.173,
42
+ "multiword_right": 0.5376,
43
+ "saved_with_tab": 0.1102,
44
+ "saved_word_by_word": 0.1328
45
+ },
46
+ "email/typing": {
47
+ "pauses": 1000,
48
+ "shown": 0.234,
49
+ "right": 0.5598,
50
+ "avg_right_chars": 6.03,
51
+ "multiword_shown": 0.087,
52
+ "multiword_right": 0.4023,
53
+ "saved_with_tab": 0.0658,
54
+ "saved_word_by_word": 0.0746
55
+ },
56
+ "email/editing": {
57
+ "pauses": 1000,
58
+ "shown": 0.213,
59
+ "right": 0.5915,
60
+ "avg_right_chars": 5.63,
61
+ "multiword_shown": 0.078,
62
+ "multiword_right": 0.4231,
63
+ "saved_with_tab": 0.0591,
64
+ "saved_word_by_word": 0.0653
65
+ }
66
+ },
67
+ "0.4": {
68
+ "articles/typing": {
69
+ "pauses": 1000,
70
+ "shown": 0.149,
71
+ "right": 0.698,
72
+ "avg_right_chars": 6.12,
73
+ "multiword_shown": 0.057,
74
+ "multiword_right": 0.614,
75
+ "saved_with_tab": 0.0531,
76
+ "saved_word_by_word": 0.0563
77
+ },
78
+ "articles/editing": {
79
+ "pauses": 1000,
80
+ "shown": 0.107,
81
+ "right": 0.7103,
82
+ "avg_right_chars": 5.72,
83
+ "multiword_shown": 0.034,
84
+ "multiword_right": 0.6765,
85
+ "saved_with_tab": 0.0362,
86
+ "saved_word_by_word": 0.0384
87
+ },
88
+ "messages/typing": {
89
+ "pauses": 1000,
90
+ "shown": 0.251,
91
+ "right": 0.6255,
92
+ "avg_right_chars": 7.7,
93
+ "multiword_shown": 0.133,
94
+ "multiword_right": 0.6015,
95
+ "saved_with_tab": 0.1008,
96
+ "saved_word_by_word": 0.1159
97
+ },
98
+ "messages/editing": {
99
+ "pauses": 1000,
100
+ "shown": 0.239,
101
+ "right": 0.6569,
102
+ "avg_right_chars": 7.29,
103
+ "multiword_shown": 0.127,
104
+ "multiword_right": 0.6378,
105
+ "saved_with_tab": 0.0954,
106
+ "saved_word_by_word": 0.1078
107
+ },
108
+ "email/typing": {
109
+ "pauses": 1000,
110
+ "shown": 0.162,
111
+ "right": 0.679,
112
+ "avg_right_chars": 5.84,
113
+ "multiword_shown": 0.049,
114
+ "multiword_right": 0.4898,
115
+ "saved_with_tab": 0.0535,
116
+ "saved_word_by_word": 0.0575
117
+ },
118
+ "email/editing": {
119
+ "pauses": 1000,
120
+ "shown": 0.165,
121
+ "right": 0.6727,
122
+ "avg_right_chars": 5.56,
123
+ "multiword_shown": 0.055,
124
+ "multiword_right": 0.5818,
125
+ "saved_with_tab": 0.0514,
126
+ "saved_word_by_word": 0.0545
127
+ }
128
+ },
129
+ "0.5": {
130
+ "articles/typing": {
131
+ "pauses": 1000,
132
+ "shown": 0.101,
133
+ "right": 0.7822,
134
+ "avg_right_chars": 5.86,
135
+ "multiword_shown": 0.035,
136
+ "multiword_right": 0.7143,
137
+ "saved_with_tab": 0.0386,
138
+ "saved_word_by_word": 0.04
139
+ },
140
+ "articles/editing": {
141
+ "pauses": 1000,
142
+ "shown": 0.08,
143
+ "right": 0.825,
144
+ "avg_right_chars": 5.61,
145
+ "multiword_shown": 0.024,
146
+ "multiword_right": 0.8333,
147
+ "saved_with_tab": 0.0308,
148
+ "saved_word_by_word": 0.0317
149
+ },
150
+ "messages/typing": {
151
+ "pauses": 1000,
152
+ "shown": 0.193,
153
+ "right": 0.7461,
154
+ "avg_right_chars": 7.31,
155
+ "multiword_shown": 0.094,
156
+ "multiword_right": 0.7021,
157
+ "saved_with_tab": 0.0877,
158
+ "saved_word_by_word": 0.0969
159
+ },
160
+ "messages/editing": {
161
+ "pauses": 1000,
162
+ "shown": 0.175,
163
+ "right": 0.76,
164
+ "avg_right_chars": 6.87,
165
+ "multiword_shown": 0.091,
166
+ "multiword_right": 0.7033,
167
+ "saved_with_tab": 0.0762,
168
+ "saved_word_by_word": 0.0843
169
+ },
170
+ "email/typing": {
171
+ "pauses": 1000,
172
+ "shown": 0.112,
173
+ "right": 0.7768,
174
+ "avg_right_chars": 5.54,
175
+ "multiword_shown": 0.035,
176
+ "multiword_right": 0.5143,
177
+ "saved_with_tab": 0.0402,
178
+ "saved_word_by_word": 0.0427
179
+ },
180
+ "email/editing": {
181
+ "pauses": 1000,
182
+ "shown": 0.118,
183
+ "right": 0.7797,
184
+ "avg_right_chars": 5.5,
185
+ "multiword_shown": 0.031,
186
+ "multiword_right": 0.7097,
187
+ "saved_with_tab": 0.0422,
188
+ "saved_word_by_word": 0.0435
189
+ }
190
+ },
191
+ "0.6": {
192
+ "articles/typing": {
193
+ "pauses": 1000,
194
+ "shown": 0.076,
195
+ "right": 0.8026,
196
+ "avg_right_chars": 5.77,
197
+ "multiword_shown": 0.026,
198
+ "multiword_right": 0.7692,
199
+ "saved_with_tab": 0.0293,
200
+ "saved_word_by_word": 0.0303
201
+ },
202
+ "articles/editing": {
203
+ "pauses": 1000,
204
+ "shown": 0.059,
205
+ "right": 0.8983,
206
+ "avg_right_chars": 5.45,
207
+ "multiword_shown": 0.014,
208
+ "multiword_right": 0.7857,
209
+ "saved_with_tab": 0.0241,
210
+ "saved_word_by_word": 0.0244
211
+ },
212
+ "messages/typing": {
213
+ "pauses": 1000,
214
+ "shown": 0.152,
215
+ "right": 0.8421,
216
+ "avg_right_chars": 7.22,
217
+ "multiword_shown": 0.068,
218
+ "multiword_right": 0.8382,
219
+ "saved_with_tab": 0.077,
220
+ "saved_word_by_word": 0.0795
221
+ },
222
+ "messages/editing": {
223
+ "pauses": 1000,
224
+ "shown": 0.141,
225
+ "right": 0.8369,
226
+ "avg_right_chars": 6.39,
227
+ "multiword_shown": 0.065,
228
+ "multiword_right": 0.7538,
229
+ "saved_with_tab": 0.0628,
230
+ "saved_word_by_word": 0.0669
231
+ },
232
+ "email/typing": {
233
+ "pauses": 1000,
234
+ "shown": 0.086,
235
+ "right": 0.8256,
236
+ "avg_right_chars": 5.23,
237
+ "multiword_shown": 0.015,
238
+ "multiword_right": 0.5333,
239
+ "saved_with_tab": 0.0309,
240
+ "saved_word_by_word": 0.0322
241
+ },
242
+ "email/editing": {
243
+ "pauses": 1000,
244
+ "shown": 0.084,
245
+ "right": 0.8452,
246
+ "avg_right_chars": 5.34,
247
+ "multiword_shown": 0.016,
248
+ "multiword_right": 0.75,
249
+ "saved_with_tab": 0.0316,
250
+ "saved_word_by_word": 0.0319
251
+ }
252
+ },
253
+ "0.7": {
254
+ "articles/typing": {
255
+ "pauses": 1000,
256
+ "shown": 0.052,
257
+ "right": 0.8654,
258
+ "avg_right_chars": 6.09,
259
+ "multiword_shown": 0.017,
260
+ "multiword_right": 0.8235,
261
+ "saved_with_tab": 0.0228,
262
+ "saved_word_by_word": 0.0232
263
+ },
264
+ "articles/editing": {
265
+ "pauses": 1000,
266
+ "shown": 0.043,
267
+ "right": 0.9302,
268
+ "avg_right_chars": 5.25,
269
+ "multiword_shown": 0.01,
270
+ "multiword_right": 0.9,
271
+ "saved_with_tab": 0.0175,
272
+ "saved_word_by_word": 0.0177
273
+ },
274
+ "messages/typing": {
275
+ "pauses": 1000,
276
+ "shown": 0.117,
277
+ "right": 0.8718,
278
+ "avg_right_chars": 6.64,
279
+ "multiword_shown": 0.045,
280
+ "multiword_right": 0.8889,
281
+ "saved_with_tab": 0.0564,
282
+ "saved_word_by_word": 0.0579
283
+ },
284
+ "messages/editing": {
285
+ "pauses": 1000,
286
+ "shown": 0.105,
287
+ "right": 0.8857,
288
+ "avg_right_chars": 5.98,
289
+ "multiword_shown": 0.038,
290
+ "multiword_right": 0.8684,
291
+ "saved_with_tab": 0.0463,
292
+ "saved_word_by_word": 0.0476
293
+ },
294
+ "email/typing": {
295
+ "pauses": 1000,
296
+ "shown": 0.058,
297
+ "right": 0.8103,
298
+ "avg_right_chars": 5.19,
299
+ "multiword_shown": 0.007,
300
+ "multiword_right": 0.4286,
301
+ "saved_with_tab": 0.0203,
302
+ "saved_word_by_word": 0.0206
303
+ },
304
+ "email/editing": {
305
+ "pauses": 1000,
306
+ "shown": 0.066,
307
+ "right": 0.8788,
308
+ "avg_right_chars": 4.98,
309
+ "multiword_shown": 0.008,
310
+ "multiword_right": 0.5,
311
+ "saved_with_tab": 0.0241,
312
+ "saved_word_by_word": 0.0244
313
+ }
314
+ }
315
+ },
316
+ "examples": [
317
+ {
318
+ "domain": "articles",
319
+ "scenario": "editing",
320
+ "before": " the Spaghetti Western film genre) and Hate Boat, were publi",
321
+ "suggestion": "shed",
322
+ "truth": "shed by Comico C",
323
+ "right": true
324
+ },
325
+ {
326
+ "domain": "articles",
327
+ "scenario": "editing",
328
+ "before": "rked on a tour of Canada with I Hate Sally, Hiretsukan, GFK,",
329
+ "suggestion": " and",
330
+ "truth": " and DeadPoets a",
331
+ "right": true
332
+ },
333
+ {
334
+ "domain": "messages",
335
+ "scenario": "typing",
336
+ "before": " be completely benevolent and will always act in our best in",
337
+ "suggestion": "terests.",
338
+ "truth": "terests, ensuring th",
339
+ "right": false
340
+ },
341
+ {
342
+ "domain": "messages",
343
+ "scenario": "typing",
344
+ "before": "onsidered new. Those decisions make the graphs as neat as po",
345
+ "suggestion": "ssible.",
346
+ "truth": "ssible, and it is t",
347
+ "right": false
348
+ },
349
+ {
350
+ "domain": "messages",
351
+ "scenario": "editing",
352
+ "before": "e optimal level of production and pricing for businesses. L'",
353
+ "suggestion": "Hpital's Rule can",
354
+ "truth": "Hpital's Rule can help analyz",
355
+ "right": true
356
+ },
357
+ {
358
+ "domain": "email",
359
+ "scenario": "editing",
360
+ "before": "ured products and marketing of electricity for the United St",
361
+ "suggestion": "ates",
362
+ "truth": "ates ? Actively ",
363
+ "right": true
364
+ }
365
+ ],
366
+ "latency_ms": {
367
+ "p50": 15.38,
368
+ "p95": 42.67,
369
+ "note": "Measured in this eval process, not in a browser."
370
+ }
371
+ }
v2/experiments/tinynext-v2-3b/model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d720a8d92844daca8da7820debd0be2c22d064b33de16ccbcfa69adf79307f13
3
+ size 60813561
v2/experiments/tinynext-v2-3b/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0ffd7eaaee87262d76e9b401514eee2449e72b7aae899579b227c114d4a37366
3
+ size 50161328
v2/experiments/tinynext-v2-3b/model_int8.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:34f405276119c07d523af9a5e43354ab50acbaa0e2be167166320401a3d35a2c
3
+ size 15638872
v2/experiments/tinynext-v2-3b/report.json ADDED
@@ -0,0 +1,2390 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "args": {
3
+ "vocab": 8192,
4
+ "dim": 320,
5
+ "layers": 8,
6
+ "heads": 5,
7
+ "ffn": 864,
8
+ "seq": 512,
9
+ "tokens": 3000000000.0,
10
+ "val_tokens": 4000000.0,
11
+ "epochs": 1.0,
12
+ "batch": 512,
13
+ "lr": 0.003,
14
+ "fim_rate": 0.5,
15
+ "mix": "fineweb=0.6,smoltalk=0.3,enron=0.1",
16
+ "fineweb_files": 4,
17
+ "smoltalk_files": 9,
18
+ "tokenizer_docs": 200000,
19
+ "eval_pauses": 1000,
20
+ "log_every": 50,
21
+ "repo": "cfahlgren1/tinynext",
22
+ "folder": "v2/experiments/tinynext-v2-3b",
23
+ "eval_folder": "v2",
24
+ "name": "tinynext-v2-3b",
25
+ "trackio_space": "cfahlgren1/tinynext-trackio",
26
+ "stage": "train",
27
+ "data_repo": "cfahlgren1/tinynext-data",
28
+ "smoke": false,
29
+ "no_push": false
30
+ },
31
+ "parameters": 12539200,
32
+ "data": {
33
+ "fineweb": {
34
+ "docs": 2122073,
35
+ "train_tokens": 1800000000
36
+ },
37
+ "smoltalk": {
38
+ "docs": 1041801,
39
+ "train_tokens": 900000000
40
+ },
41
+ "enron": {
42
+ "docs": 268120,
43
+ "train_tokens": 300000000
44
+ }
45
+ },
46
+ "data_minutes": 0.2,
47
+ "train_minutes": 55.8,
48
+ "history": [
49
+ {
50
+ "step": 0,
51
+ "train_loss": 9.0702,
52
+ "lr": 1.3e-05,
53
+ "tokens_per_second": 12800,
54
+ "minutes": 0.34,
55
+ "mfu_percent": 0.3,
56
+ "gpu_util_percent": 98,
57
+ "gpu_memory_gb": 76.5,
58
+ "val_loss": 9.0663
59
+ },
60
+ {
61
+ "step": 50,
62
+ "train_loss": 7.215,
63
+ "lr": 0.000671,
64
+ "tokens_per_second": 930958,
65
+ "minutes": 0.62,
66
+ "mfu_percent": 22.4,
67
+ "gpu_util_percent": 100,
68
+ "gpu_memory_gb": 85.1
69
+ },
70
+ {
71
+ "step": 100,
72
+ "train_loss": 6.1306,
73
+ "lr": 0.001329,
74
+ "tokens_per_second": 925320,
75
+ "minutes": 0.86,
76
+ "mfu_percent": 22.3,
77
+ "gpu_util_percent": 100,
78
+ "gpu_memory_gb": 85.1
79
+ },
80
+ {
81
+ "step": 150,
82
+ "train_loss": 5.5618,
83
+ "lr": 0.001986,
84
+ "tokens_per_second": 924266,
85
+ "minutes": 1.1,
86
+ "mfu_percent": 22.3,
87
+ "gpu_util_percent": 100,
88
+ "gpu_memory_gb": 85.1
89
+ },
90
+ {
91
+ "step": 200,
92
+ "train_loss": 5.1433,
93
+ "lr": 0.002643,
94
+ "tokens_per_second": 923447,
95
+ "minutes": 1.33,
96
+ "mfu_percent": 22.3,
97
+ "gpu_util_percent": 100,
98
+ "gpu_memory_gb": 85.1
99
+ },
100
+ {
101
+ "step": 250,
102
+ "train_loss": 4.6259,
103
+ "lr": 0.002997,
104
+ "tokens_per_second": 923503,
105
+ "minutes": 1.57,
106
+ "mfu_percent": 22.3,
107
+ "gpu_util_percent": 100,
108
+ "gpu_memory_gb": 85.1
109
+ },
110
+ {
111
+ "step": 300,
112
+ "train_loss": 4.2702,
113
+ "lr": 0.002995,
114
+ "tokens_per_second": 923163,
115
+ "minutes": 1.81,
116
+ "mfu_percent": 22.3,
117
+ "gpu_util_percent": 100,
118
+ "gpu_memory_gb": 85.1
119
+ },
120
+ {
121
+ "step": 350,
122
+ "train_loss": 4.0271,
123
+ "lr": 0.002994,
124
+ "tokens_per_second": 922754,
125
+ "minutes": 2.04,
126
+ "mfu_percent": 22.3,
127
+ "gpu_util_percent": 100,
128
+ "gpu_memory_gb": 85.1
129
+ },
130
+ {
131
+ "step": 400,
132
+ "train_loss": 3.8697,
133
+ "lr": 0.002992,
134
+ "tokens_per_second": 922255,
135
+ "minutes": 2.28,
136
+ "mfu_percent": 22.2,
137
+ "gpu_util_percent": 100,
138
+ "gpu_memory_gb": 85.1
139
+ },
140
+ {
141
+ "step": 450,
142
+ "train_loss": 3.6918,
143
+ "lr": 0.00299,
144
+ "tokens_per_second": 921900,
145
+ "minutes": 2.52,
146
+ "mfu_percent": 22.2,
147
+ "gpu_util_percent": 100,
148
+ "gpu_memory_gb": 85.1
149
+ },
150
+ {
151
+ "step": 500,
152
+ "train_loss": 3.6502,
153
+ "lr": 0.002987,
154
+ "tokens_per_second": 921603,
155
+ "minutes": 2.75,
156
+ "mfu_percent": 22.2,
157
+ "gpu_util_percent": 100,
158
+ "gpu_memory_gb": 85.1,
159
+ "val_loss": 3.6813
160
+ },
161
+ {
162
+ "step": 550,
163
+ "train_loss": 3.6069,
164
+ "lr": 0.002985,
165
+ "tokens_per_second": 922139,
166
+ "minutes": 3.04,
167
+ "mfu_percent": 22.2,
168
+ "gpu_util_percent": 100,
169
+ "gpu_memory_gb": 85.1
170
+ },
171
+ {
172
+ "step": 600,
173
+ "train_loss": 3.6009,
174
+ "lr": 0.002982,
175
+ "tokens_per_second": 921841,
176
+ "minutes": 3.28,
177
+ "mfu_percent": 22.2,
178
+ "gpu_util_percent": 100,
179
+ "gpu_memory_gb": 85.1
180
+ },
181
+ {
182
+ "step": 650,
183
+ "train_loss": 3.5049,
184
+ "lr": 0.002978,
185
+ "tokens_per_second": 921515,
186
+ "minutes": 3.51,
187
+ "mfu_percent": 22.2,
188
+ "gpu_util_percent": 100,
189
+ "gpu_memory_gb": 85.1
190
+ },
191
+ {
192
+ "step": 700,
193
+ "train_loss": 3.4728,
194
+ "lr": 0.002975,
195
+ "tokens_per_second": 921918,
196
+ "minutes": 3.75,
197
+ "mfu_percent": 22.2,
198
+ "gpu_util_percent": 100,
199
+ "gpu_memory_gb": 85.1
200
+ },
201
+ {
202
+ "step": 750,
203
+ "train_loss": 3.4877,
204
+ "lr": 0.002971,
205
+ "tokens_per_second": 921386,
206
+ "minutes": 3.99,
207
+ "mfu_percent": 22.2,
208
+ "gpu_util_percent": 100,
209
+ "gpu_memory_gb": 85.1
210
+ },
211
+ {
212
+ "step": 800,
213
+ "train_loss": 3.4264,
214
+ "lr": 0.002967,
215
+ "tokens_per_second": 921002,
216
+ "minutes": 4.22,
217
+ "mfu_percent": 22.2,
218
+ "gpu_util_percent": 100,
219
+ "gpu_memory_gb": 85.1
220
+ },
221
+ {
222
+ "step": 850,
223
+ "train_loss": 3.3612,
224
+ "lr": 0.002963,
225
+ "tokens_per_second": 920889,
226
+ "minutes": 4.46,
227
+ "mfu_percent": 22.2,
228
+ "gpu_util_percent": 100,
229
+ "gpu_memory_gb": 85.1
230
+ },
231
+ {
232
+ "step": 900,
233
+ "train_loss": 3.3516,
234
+ "lr": 0.002959,
235
+ "tokens_per_second": 920832,
236
+ "minutes": 4.7,
237
+ "mfu_percent": 22.2,
238
+ "gpu_util_percent": 100,
239
+ "gpu_memory_gb": 85.1
240
+ },
241
+ {
242
+ "step": 950,
243
+ "train_loss": 3.3742,
244
+ "lr": 0.002954,
245
+ "tokens_per_second": 920862,
246
+ "minutes": 4.94,
247
+ "mfu_percent": 22.2,
248
+ "gpu_util_percent": 100,
249
+ "gpu_memory_gb": 85.1
250
+ },
251
+ {
252
+ "step": 1000,
253
+ "train_loss": 3.3632,
254
+ "lr": 0.002949,
255
+ "tokens_per_second": 920554,
256
+ "minutes": 5.17,
257
+ "mfu_percent": 22.2,
258
+ "gpu_util_percent": 100,
259
+ "gpu_memory_gb": 85.1,
260
+ "val_loss": 3.3796
261
+ },
262
+ {
263
+ "step": 1050,
264
+ "train_loss": 3.3061,
265
+ "lr": 0.002944,
266
+ "tokens_per_second": 920467,
267
+ "minutes": 5.46,
268
+ "mfu_percent": 22.2,
269
+ "gpu_util_percent": 100,
270
+ "gpu_memory_gb": 85.1
271
+ },
272
+ {
273
+ "step": 1100,
274
+ "train_loss": 3.3351,
275
+ "lr": 0.002939,
276
+ "tokens_per_second": 921248,
277
+ "minutes": 5.69,
278
+ "mfu_percent": 22.2,
279
+ "gpu_util_percent": 100,
280
+ "gpu_memory_gb": 85.1
281
+ },
282
+ {
283
+ "step": 1150,
284
+ "train_loss": 3.3238,
285
+ "lr": 0.002933,
286
+ "tokens_per_second": 921674,
287
+ "minutes": 5.93,
288
+ "mfu_percent": 22.2,
289
+ "gpu_util_percent": 100,
290
+ "gpu_memory_gb": 85.1
291
+ },
292
+ {
293
+ "step": 1200,
294
+ "train_loss": 3.3208,
295
+ "lr": 0.002927,
296
+ "tokens_per_second": 921693,
297
+ "minutes": 6.17,
298
+ "mfu_percent": 22.2,
299
+ "gpu_util_percent": 100,
300
+ "gpu_memory_gb": 85.1
301
+ },
302
+ {
303
+ "step": 1250,
304
+ "train_loss": 3.286,
305
+ "lr": 0.002921,
306
+ "tokens_per_second": 921893,
307
+ "minutes": 6.41,
308
+ "mfu_percent": 22.2,
309
+ "gpu_util_percent": 100,
310
+ "gpu_memory_gb": 85.1
311
+ },
312
+ {
313
+ "step": 1300,
314
+ "train_loss": 3.2941,
315
+ "lr": 0.002914,
316
+ "tokens_per_second": 922496,
317
+ "minutes": 6.64,
318
+ "mfu_percent": 22.2,
319
+ "gpu_util_percent": 100,
320
+ "gpu_memory_gb": 85.1
321
+ },
322
+ {
323
+ "step": 1350,
324
+ "train_loss": 3.2475,
325
+ "lr": 0.002908,
326
+ "tokens_per_second": 922535,
327
+ "minutes": 6.88,
328
+ "mfu_percent": 22.2,
329
+ "gpu_util_percent": 100,
330
+ "gpu_memory_gb": 85.1
331
+ },
332
+ {
333
+ "step": 1400,
334
+ "train_loss": 3.21,
335
+ "lr": 0.002901,
336
+ "tokens_per_second": 921917,
337
+ "minutes": 7.12,
338
+ "mfu_percent": 22.2,
339
+ "gpu_util_percent": 100,
340
+ "gpu_memory_gb": 85.1
341
+ },
342
+ {
343
+ "step": 1450,
344
+ "train_loss": 3.2507,
345
+ "lr": 0.002894,
346
+ "tokens_per_second": 921722,
347
+ "minutes": 7.35,
348
+ "mfu_percent": 22.2,
349
+ "gpu_util_percent": 100,
350
+ "gpu_memory_gb": 85.1
351
+ },
352
+ {
353
+ "step": 1500,
354
+ "train_loss": 3.2548,
355
+ "lr": 0.002887,
356
+ "tokens_per_second": 920926,
357
+ "minutes": 7.59,
358
+ "mfu_percent": 22.2,
359
+ "gpu_util_percent": 100,
360
+ "gpu_memory_gb": 85.1,
361
+ "val_loss": 3.2689
362
+ },
363
+ {
364
+ "step": 1550,
365
+ "train_loss": 3.2187,
366
+ "lr": 0.002879,
367
+ "tokens_per_second": 920076,
368
+ "minutes": 7.87,
369
+ "mfu_percent": 22.2,
370
+ "gpu_util_percent": 100,
371
+ "gpu_memory_gb": 85.1
372
+ },
373
+ {
374
+ "step": 1600,
375
+ "train_loss": 3.2435,
376
+ "lr": 0.002871,
377
+ "tokens_per_second": 920390,
378
+ "minutes": 8.11,
379
+ "mfu_percent": 22.2,
380
+ "gpu_util_percent": 100,
381
+ "gpu_memory_gb": 85.1
382
+ },
383
+ {
384
+ "step": 1650,
385
+ "train_loss": 3.1958,
386
+ "lr": 0.002863,
387
+ "tokens_per_second": 920901,
388
+ "minutes": 8.35,
389
+ "mfu_percent": 22.2,
390
+ "gpu_util_percent": 100,
391
+ "gpu_memory_gb": 85.1
392
+ },
393
+ {
394
+ "step": 1700,
395
+ "train_loss": 3.1797,
396
+ "lr": 0.002855,
397
+ "tokens_per_second": 920930,
398
+ "minutes": 8.59,
399
+ "mfu_percent": 22.2,
400
+ "gpu_util_percent": 100,
401
+ "gpu_memory_gb": 85.1
402
+ },
403
+ {
404
+ "step": 1750,
405
+ "train_loss": 3.2016,
406
+ "lr": 0.002846,
407
+ "tokens_per_second": 921134,
408
+ "minutes": 8.82,
409
+ "mfu_percent": 22.2,
410
+ "gpu_util_percent": 100,
411
+ "gpu_memory_gb": 85.1
412
+ },
413
+ {
414
+ "step": 1800,
415
+ "train_loss": 3.1736,
416
+ "lr": 0.002838,
417
+ "tokens_per_second": 922208,
418
+ "minutes": 9.06,
419
+ "mfu_percent": 22.2,
420
+ "gpu_util_percent": 100,
421
+ "gpu_memory_gb": 85.1
422
+ },
423
+ {
424
+ "step": 1850,
425
+ "train_loss": 3.1912,
426
+ "lr": 0.002829,
427
+ "tokens_per_second": 922453,
428
+ "minutes": 9.3,
429
+ "mfu_percent": 22.2,
430
+ "gpu_util_percent": 100,
431
+ "gpu_memory_gb": 85.1
432
+ },
433
+ {
434
+ "step": 1900,
435
+ "train_loss": 3.1827,
436
+ "lr": 0.00282,
437
+ "tokens_per_second": 922660,
438
+ "minutes": 9.53,
439
+ "mfu_percent": 22.2,
440
+ "gpu_util_percent": 100,
441
+ "gpu_memory_gb": 85.1
442
+ },
443
+ {
444
+ "step": 1950,
445
+ "train_loss": 3.202,
446
+ "lr": 0.00281,
447
+ "tokens_per_second": 922623,
448
+ "minutes": 9.77,
449
+ "mfu_percent": 22.2,
450
+ "gpu_util_percent": 100,
451
+ "gpu_memory_gb": 85.1
452
+ },
453
+ {
454
+ "step": 2000,
455
+ "train_loss": 3.2288,
456
+ "lr": 0.002801,
457
+ "tokens_per_second": 922474,
458
+ "minutes": 10.01,
459
+ "mfu_percent": 22.2,
460
+ "gpu_util_percent": 100,
461
+ "gpu_memory_gb": 85.1,
462
+ "val_loss": 3.208
463
+ },
464
+ {
465
+ "step": 2050,
466
+ "train_loss": 3.1591,
467
+ "lr": 0.002791,
468
+ "tokens_per_second": 922197,
469
+ "minutes": 10.29,
470
+ "mfu_percent": 22.2,
471
+ "gpu_util_percent": 100,
472
+ "gpu_memory_gb": 85.1
473
+ },
474
+ {
475
+ "step": 2100,
476
+ "train_loss": 3.2074,
477
+ "lr": 0.002781,
478
+ "tokens_per_second": 922026,
479
+ "minutes": 10.53,
480
+ "mfu_percent": 22.2,
481
+ "gpu_util_percent": 100,
482
+ "gpu_memory_gb": 85.1
483
+ },
484
+ {
485
+ "step": 2150,
486
+ "train_loss": 3.1784,
487
+ "lr": 0.002771,
488
+ "tokens_per_second": 922145,
489
+ "minutes": 10.77,
490
+ "mfu_percent": 22.2,
491
+ "gpu_util_percent": 100,
492
+ "gpu_memory_gb": 85.1
493
+ },
494
+ {
495
+ "step": 2200,
496
+ "train_loss": 3.1416,
497
+ "lr": 0.00276,
498
+ "tokens_per_second": 921690,
499
+ "minutes": 11.0,
500
+ "mfu_percent": 22.2,
501
+ "gpu_util_percent": 100,
502
+ "gpu_memory_gb": 85.1
503
+ },
504
+ {
505
+ "step": 2250,
506
+ "train_loss": 3.1531,
507
+ "lr": 0.002749,
508
+ "tokens_per_second": 921458,
509
+ "minutes": 11.24,
510
+ "mfu_percent": 22.2,
511
+ "gpu_util_percent": 100,
512
+ "gpu_memory_gb": 85.1
513
+ },
514
+ {
515
+ "step": 2300,
516
+ "train_loss": 3.1148,
517
+ "lr": 0.002738,
518
+ "tokens_per_second": 920727,
519
+ "minutes": 11.48,
520
+ "mfu_percent": 22.2,
521
+ "gpu_util_percent": 100,
522
+ "gpu_memory_gb": 85.1
523
+ },
524
+ {
525
+ "step": 2350,
526
+ "train_loss": 3.1005,
527
+ "lr": 0.002727,
528
+ "tokens_per_second": 921746,
529
+ "minutes": 11.71,
530
+ "mfu_percent": 22.2,
531
+ "gpu_util_percent": 100,
532
+ "gpu_memory_gb": 85.1
533
+ },
534
+ {
535
+ "step": 2400,
536
+ "train_loss": 3.2112,
537
+ "lr": 0.002716,
538
+ "tokens_per_second": 922555,
539
+ "minutes": 11.95,
540
+ "mfu_percent": 22.2,
541
+ "gpu_util_percent": 100,
542
+ "gpu_memory_gb": 85.1
543
+ },
544
+ {
545
+ "step": 2450,
546
+ "train_loss": 3.1437,
547
+ "lr": 0.002705,
548
+ "tokens_per_second": 922219,
549
+ "minutes": 12.19,
550
+ "mfu_percent": 22.2,
551
+ "gpu_util_percent": 100,
552
+ "gpu_memory_gb": 85.1
553
+ },
554
+ {
555
+ "step": 2500,
556
+ "train_loss": 3.114,
557
+ "lr": 0.002693,
558
+ "tokens_per_second": 922404,
559
+ "minutes": 12.42,
560
+ "mfu_percent": 22.2,
561
+ "gpu_util_percent": 100,
562
+ "gpu_memory_gb": 85.1,
563
+ "val_loss": 3.1664
564
+ },
565
+ {
566
+ "step": 2550,
567
+ "train_loss": 3.1538,
568
+ "lr": 0.002681,
569
+ "tokens_per_second": 922093,
570
+ "minutes": 12.71,
571
+ "mfu_percent": 22.2,
572
+ "gpu_util_percent": 100,
573
+ "gpu_memory_gb": 85.1
574
+ },
575
+ {
576
+ "step": 2600,
577
+ "train_loss": 3.1874,
578
+ "lr": 0.002669,
579
+ "tokens_per_second": 921696,
580
+ "minutes": 12.95,
581
+ "mfu_percent": 22.2,
582
+ "gpu_util_percent": 100,
583
+ "gpu_memory_gb": 85.1
584
+ },
585
+ {
586
+ "step": 2650,
587
+ "train_loss": 3.1451,
588
+ "lr": 0.002657,
589
+ "tokens_per_second": 920726,
590
+ "minutes": 13.18,
591
+ "mfu_percent": 22.2,
592
+ "gpu_util_percent": 100,
593
+ "gpu_memory_gb": 85.1
594
+ },
595
+ {
596
+ "step": 2700,
597
+ "train_loss": 3.1138,
598
+ "lr": 0.002644,
599
+ "tokens_per_second": 920915,
600
+ "minutes": 13.42,
601
+ "mfu_percent": 22.2,
602
+ "gpu_util_percent": 100,
603
+ "gpu_memory_gb": 85.1
604
+ },
605
+ {
606
+ "step": 2750,
607
+ "train_loss": 3.1194,
608
+ "lr": 0.002632,
609
+ "tokens_per_second": 920910,
610
+ "minutes": 13.66,
611
+ "mfu_percent": 22.2,
612
+ "gpu_util_percent": 100,
613
+ "gpu_memory_gb": 85.1
614
+ },
615
+ {
616
+ "step": 2800,
617
+ "train_loss": 3.0835,
618
+ "lr": 0.002619,
619
+ "tokens_per_second": 920819,
620
+ "minutes": 13.89,
621
+ "mfu_percent": 22.2,
622
+ "gpu_util_percent": 100,
623
+ "gpu_memory_gb": 85.1
624
+ },
625
+ {
626
+ "step": 2850,
627
+ "train_loss": 3.1095,
628
+ "lr": 0.002606,
629
+ "tokens_per_second": 920420,
630
+ "minutes": 14.13,
631
+ "mfu_percent": 22.2,
632
+ "gpu_util_percent": 100,
633
+ "gpu_memory_gb": 85.1
634
+ },
635
+ {
636
+ "step": 2900,
637
+ "train_loss": 3.1067,
638
+ "lr": 0.002593,
639
+ "tokens_per_second": 920261,
640
+ "minutes": 14.37,
641
+ "mfu_percent": 22.2,
642
+ "gpu_util_percent": 100,
643
+ "gpu_memory_gb": 85.1
644
+ },
645
+ {
646
+ "step": 2950,
647
+ "train_loss": 3.0488,
648
+ "lr": 0.002579,
649
+ "tokens_per_second": 921100,
650
+ "minutes": 14.61,
651
+ "mfu_percent": 22.2,
652
+ "gpu_util_percent": 100,
653
+ "gpu_memory_gb": 85.1
654
+ },
655
+ {
656
+ "step": 3000,
657
+ "train_loss": 3.1089,
658
+ "lr": 0.002566,
659
+ "tokens_per_second": 921915,
660
+ "minutes": 14.84,
661
+ "mfu_percent": 22.2,
662
+ "gpu_util_percent": 100,
663
+ "gpu_memory_gb": 85.1,
664
+ "val_loss": 3.1334
665
+ },
666
+ {
667
+ "step": 3050,
668
+ "train_loss": 3.0736,
669
+ "lr": 0.002552,
670
+ "tokens_per_second": 922085,
671
+ "minutes": 15.13,
672
+ "mfu_percent": 22.2,
673
+ "gpu_util_percent": 100,
674
+ "gpu_memory_gb": 85.1
675
+ },
676
+ {
677
+ "step": 3100,
678
+ "train_loss": 3.0915,
679
+ "lr": 0.002538,
680
+ "tokens_per_second": 921538,
681
+ "minutes": 15.36,
682
+ "mfu_percent": 22.2,
683
+ "gpu_util_percent": 100,
684
+ "gpu_memory_gb": 85.1
685
+ },
686
+ {
687
+ "step": 3150,
688
+ "train_loss": 3.0751,
689
+ "lr": 0.002524,
690
+ "tokens_per_second": 921498,
691
+ "minutes": 15.6,
692
+ "mfu_percent": 22.2,
693
+ "gpu_util_percent": 100,
694
+ "gpu_memory_gb": 85.1
695
+ },
696
+ {
697
+ "step": 3200,
698
+ "train_loss": 3.1093,
699
+ "lr": 0.00251,
700
+ "tokens_per_second": 920747,
701
+ "minutes": 15.84,
702
+ "mfu_percent": 22.2,
703
+ "gpu_util_percent": 100,
704
+ "gpu_memory_gb": 85.1
705
+ },
706
+ {
707
+ "step": 3250,
708
+ "train_loss": 3.1425,
709
+ "lr": 0.002495,
710
+ "tokens_per_second": 920376,
711
+ "minutes": 16.08,
712
+ "mfu_percent": 22.2,
713
+ "gpu_util_percent": 100,
714
+ "gpu_memory_gb": 85.1
715
+ },
716
+ {
717
+ "step": 3300,
718
+ "train_loss": 3.0762,
719
+ "lr": 0.002481,
720
+ "tokens_per_second": 920083,
721
+ "minutes": 16.31,
722
+ "mfu_percent": 22.2,
723
+ "gpu_util_percent": 100,
724
+ "gpu_memory_gb": 85.1
725
+ },
726
+ {
727
+ "step": 3350,
728
+ "train_loss": 3.1273,
729
+ "lr": 0.002466,
730
+ "tokens_per_second": 919889,
731
+ "minutes": 16.55,
732
+ "mfu_percent": 22.2,
733
+ "gpu_util_percent": 100,
734
+ "gpu_memory_gb": 85.1
735
+ },
736
+ {
737
+ "step": 3400,
738
+ "train_loss": 3.1,
739
+ "lr": 0.002451,
740
+ "tokens_per_second": 921166,
741
+ "minutes": 16.79,
742
+ "mfu_percent": 22.2,
743
+ "gpu_util_percent": 100,
744
+ "gpu_memory_gb": 85.1
745
+ },
746
+ {
747
+ "step": 3450,
748
+ "train_loss": 3.1428,
749
+ "lr": 0.002436,
750
+ "tokens_per_second": 921093,
751
+ "minutes": 17.02,
752
+ "mfu_percent": 22.2,
753
+ "gpu_util_percent": 100,
754
+ "gpu_memory_gb": 85.1
755
+ },
756
+ {
757
+ "step": 3500,
758
+ "train_loss": 3.0703,
759
+ "lr": 0.002421,
760
+ "tokens_per_second": 920938,
761
+ "minutes": 17.26,
762
+ "mfu_percent": 22.2,
763
+ "gpu_util_percent": 100,
764
+ "gpu_memory_gb": 85.1,
765
+ "val_loss": 3.1114
766
+ },
767
+ {
768
+ "step": 3550,
769
+ "train_loss": 3.1048,
770
+ "lr": 0.002406,
771
+ "tokens_per_second": 921446,
772
+ "minutes": 17.55,
773
+ "mfu_percent": 22.2,
774
+ "gpu_util_percent": 100,
775
+ "gpu_memory_gb": 85.1
776
+ },
777
+ {
778
+ "step": 3600,
779
+ "train_loss": 3.0576,
780
+ "lr": 0.00239,
781
+ "tokens_per_second": 921917,
782
+ "minutes": 17.78,
783
+ "mfu_percent": 22.2,
784
+ "gpu_util_percent": 100,
785
+ "gpu_memory_gb": 85.1
786
+ },
787
+ {
788
+ "step": 3650,
789
+ "train_loss": 3.0815,
790
+ "lr": 0.002375,
791
+ "tokens_per_second": 921369,
792
+ "minutes": 18.02,
793
+ "mfu_percent": 22.2,
794
+ "gpu_util_percent": 100,
795
+ "gpu_memory_gb": 85.1
796
+ },
797
+ {
798
+ "step": 3700,
799
+ "train_loss": 3.102,
800
+ "lr": 0.002359,
801
+ "tokens_per_second": 921425,
802
+ "minutes": 18.26,
803
+ "mfu_percent": 22.2,
804
+ "gpu_util_percent": 100,
805
+ "gpu_memory_gb": 85.1
806
+ },
807
+ {
808
+ "step": 3750,
809
+ "train_loss": 3.0362,
810
+ "lr": 0.002343,
811
+ "tokens_per_second": 921350,
812
+ "minutes": 18.49,
813
+ "mfu_percent": 22.2,
814
+ "gpu_util_percent": 100,
815
+ "gpu_memory_gb": 85.1
816
+ },
817
+ {
818
+ "step": 3800,
819
+ "train_loss": 3.0936,
820
+ "lr": 0.002327,
821
+ "tokens_per_second": 920994,
822
+ "minutes": 18.73,
823
+ "mfu_percent": 22.2,
824
+ "gpu_util_percent": 100,
825
+ "gpu_memory_gb": 85.1
826
+ },
827
+ {
828
+ "step": 3850,
829
+ "train_loss": 3.0723,
830
+ "lr": 0.002311,
831
+ "tokens_per_second": 920745,
832
+ "minutes": 18.97,
833
+ "mfu_percent": 22.2,
834
+ "gpu_util_percent": 100,
835
+ "gpu_memory_gb": 85.1
836
+ },
837
+ {
838
+ "step": 3900,
839
+ "train_loss": 3.0621,
840
+ "lr": 0.002295,
841
+ "tokens_per_second": 920434,
842
+ "minutes": 19.21,
843
+ "mfu_percent": 22.2,
844
+ "gpu_util_percent": 100,
845
+ "gpu_memory_gb": 85.1
846
+ },
847
+ {
848
+ "step": 3950,
849
+ "train_loss": 3.1088,
850
+ "lr": 0.002278,
851
+ "tokens_per_second": 919905,
852
+ "minutes": 19.44,
853
+ "mfu_percent": 22.2,
854
+ "gpu_util_percent": 100,
855
+ "gpu_memory_gb": 85.1
856
+ },
857
+ {
858
+ "step": 4000,
859
+ "train_loss": 3.019,
860
+ "lr": 0.002262,
861
+ "tokens_per_second": 919709,
862
+ "minutes": 19.68,
863
+ "mfu_percent": 22.2,
864
+ "gpu_util_percent": 100,
865
+ "gpu_memory_gb": 85.1,
866
+ "val_loss": 3.0871
867
+ },
868
+ {
869
+ "step": 4050,
870
+ "train_loss": 3.0537,
871
+ "lr": 0.002245,
872
+ "tokens_per_second": 919817,
873
+ "minutes": 19.97,
874
+ "mfu_percent": 22.2,
875
+ "gpu_util_percent": 100,
876
+ "gpu_memory_gb": 85.1
877
+ },
878
+ {
879
+ "step": 4100,
880
+ "train_loss": 3.0237,
881
+ "lr": 0.002228,
882
+ "tokens_per_second": 919826,
883
+ "minutes": 20.2,
884
+ "mfu_percent": 22.2,
885
+ "gpu_util_percent": 100,
886
+ "gpu_memory_gb": 85.1
887
+ },
888
+ {
889
+ "step": 4150,
890
+ "train_loss": 3.0252,
891
+ "lr": 0.002212,
892
+ "tokens_per_second": 920702,
893
+ "minutes": 20.44,
894
+ "mfu_percent": 22.2,
895
+ "gpu_util_percent": 100,
896
+ "gpu_memory_gb": 85.1
897
+ },
898
+ {
899
+ "step": 4200,
900
+ "train_loss": 2.9956,
901
+ "lr": 0.002195,
902
+ "tokens_per_second": 920999,
903
+ "minutes": 20.68,
904
+ "mfu_percent": 22.2,
905
+ "gpu_util_percent": 100,
906
+ "gpu_memory_gb": 85.1
907
+ },
908
+ {
909
+ "step": 4250,
910
+ "train_loss": 3.0576,
911
+ "lr": 0.002178,
912
+ "tokens_per_second": 920918,
913
+ "minutes": 20.92,
914
+ "mfu_percent": 22.2,
915
+ "gpu_util_percent": 100,
916
+ "gpu_memory_gb": 85.1
917
+ },
918
+ {
919
+ "step": 4300,
920
+ "train_loss": 3.0944,
921
+ "lr": 0.00216,
922
+ "tokens_per_second": 921070,
923
+ "minutes": 21.15,
924
+ "mfu_percent": 22.2,
925
+ "gpu_util_percent": 100,
926
+ "gpu_memory_gb": 85.1
927
+ },
928
+ {
929
+ "step": 4350,
930
+ "train_loss": 3.0655,
931
+ "lr": 0.002143,
932
+ "tokens_per_second": 921341,
933
+ "minutes": 21.39,
934
+ "mfu_percent": 22.2,
935
+ "gpu_util_percent": 100,
936
+ "gpu_memory_gb": 85.1
937
+ },
938
+ {
939
+ "step": 4400,
940
+ "train_loss": 3.0756,
941
+ "lr": 0.002126,
942
+ "tokens_per_second": 920880,
943
+ "minutes": 21.63,
944
+ "mfu_percent": 22.2,
945
+ "gpu_util_percent": 100,
946
+ "gpu_memory_gb": 85.1
947
+ },
948
+ {
949
+ "step": 4450,
950
+ "train_loss": 3.0513,
951
+ "lr": 0.002108,
952
+ "tokens_per_second": 921004,
953
+ "minutes": 21.86,
954
+ "mfu_percent": 22.2,
955
+ "gpu_util_percent": 100,
956
+ "gpu_memory_gb": 85.1
957
+ },
958
+ {
959
+ "step": 4500,
960
+ "train_loss": 3.0553,
961
+ "lr": 0.002091,
962
+ "tokens_per_second": 921256,
963
+ "minutes": 22.1,
964
+ "mfu_percent": 22.2,
965
+ "gpu_util_percent": 100,
966
+ "gpu_memory_gb": 85.1,
967
+ "val_loss": 3.0698
968
+ },
969
+ {
970
+ "step": 4550,
971
+ "train_loss": 2.967,
972
+ "lr": 0.002073,
973
+ "tokens_per_second": 920961,
974
+ "minutes": 22.39,
975
+ "mfu_percent": 22.2,
976
+ "gpu_util_percent": 100,
977
+ "gpu_memory_gb": 85.1
978
+ },
979
+ {
980
+ "step": 4600,
981
+ "train_loss": 3.0567,
982
+ "lr": 0.002056,
983
+ "tokens_per_second": 920043,
984
+ "minutes": 22.62,
985
+ "mfu_percent": 22.2,
986
+ "gpu_util_percent": 100,
987
+ "gpu_memory_gb": 85.1
988
+ },
989
+ {
990
+ "step": 4650,
991
+ "train_loss": 3.0559,
992
+ "lr": 0.002038,
993
+ "tokens_per_second": 919645,
994
+ "minutes": 22.86,
995
+ "mfu_percent": 22.2,
996
+ "gpu_util_percent": 100,
997
+ "gpu_memory_gb": 85.1
998
+ },
999
+ {
1000
+ "step": 4700,
1001
+ "train_loss": 3.0584,
1002
+ "lr": 0.00202,
1003
+ "tokens_per_second": 920036,
1004
+ "minutes": 23.1,
1005
+ "mfu_percent": 22.2,
1006
+ "gpu_util_percent": 100,
1007
+ "gpu_memory_gb": 85.1
1008
+ },
1009
+ {
1010
+ "step": 4750,
1011
+ "train_loss": 3.0321,
1012
+ "lr": 0.002002,
1013
+ "tokens_per_second": 920584,
1014
+ "minutes": 23.34,
1015
+ "mfu_percent": 22.2,
1016
+ "gpu_util_percent": 100,
1017
+ "gpu_memory_gb": 85.1
1018
+ },
1019
+ {
1020
+ "step": 4800,
1021
+ "train_loss": 2.9678,
1022
+ "lr": 0.001984,
1023
+ "tokens_per_second": 920212,
1024
+ "minutes": 23.57,
1025
+ "mfu_percent": 22.2,
1026
+ "gpu_util_percent": 100,
1027
+ "gpu_memory_gb": 85.1
1028
+ },
1029
+ {
1030
+ "step": 4850,
1031
+ "train_loss": 2.9969,
1032
+ "lr": 0.001966,
1033
+ "tokens_per_second": 921129,
1034
+ "minutes": 23.81,
1035
+ "mfu_percent": 22.2,
1036
+ "gpu_util_percent": 100,
1037
+ "gpu_memory_gb": 85.1
1038
+ },
1039
+ {
1040
+ "step": 4900,
1041
+ "train_loss": 3.0317,
1042
+ "lr": 0.001948,
1043
+ "tokens_per_second": 921336,
1044
+ "minutes": 24.05,
1045
+ "mfu_percent": 22.2,
1046
+ "gpu_util_percent": 100,
1047
+ "gpu_memory_gb": 85.1
1048
+ },
1049
+ {
1050
+ "step": 4950,
1051
+ "train_loss": 3.0314,
1052
+ "lr": 0.00193,
1053
+ "tokens_per_second": 921674,
1054
+ "minutes": 24.28,
1055
+ "mfu_percent": 22.2,
1056
+ "gpu_util_percent": 100,
1057
+ "gpu_memory_gb": 85.1
1058
+ },
1059
+ {
1060
+ "step": 5000,
1061
+ "train_loss": 3.0467,
1062
+ "lr": 0.001912,
1063
+ "tokens_per_second": 922491,
1064
+ "minutes": 24.52,
1065
+ "mfu_percent": 22.2,
1066
+ "gpu_util_percent": 100,
1067
+ "gpu_memory_gb": 85.1,
1068
+ "val_loss": 3.0507
1069
+ },
1070
+ {
1071
+ "step": 5050,
1072
+ "train_loss": 3.0208,
1073
+ "lr": 0.001894,
1074
+ "tokens_per_second": 922889,
1075
+ "minutes": 24.8,
1076
+ "mfu_percent": 22.3,
1077
+ "gpu_util_percent": 100,
1078
+ "gpu_memory_gb": 85.1
1079
+ },
1080
+ {
1081
+ "step": 5100,
1082
+ "train_loss": 2.9957,
1083
+ "lr": 0.001875,
1084
+ "tokens_per_second": 922887,
1085
+ "minutes": 25.04,
1086
+ "mfu_percent": 22.3,
1087
+ "gpu_util_percent": 100,
1088
+ "gpu_memory_gb": 85.1
1089
+ },
1090
+ {
1091
+ "step": 5150,
1092
+ "train_loss": 3.0355,
1093
+ "lr": 0.001857,
1094
+ "tokens_per_second": 922331,
1095
+ "minutes": 25.28,
1096
+ "mfu_percent": 22.2,
1097
+ "gpu_util_percent": 100,
1098
+ "gpu_memory_gb": 85.1
1099
+ },
1100
+ {
1101
+ "step": 5200,
1102
+ "train_loss": 3.0702,
1103
+ "lr": 0.001839,
1104
+ "tokens_per_second": 922142,
1105
+ "minutes": 25.51,
1106
+ "mfu_percent": 22.2,
1107
+ "gpu_util_percent": 100,
1108
+ "gpu_memory_gb": 85.1
1109
+ },
1110
+ {
1111
+ "step": 5250,
1112
+ "train_loss": 3.0055,
1113
+ "lr": 0.00182,
1114
+ "tokens_per_second": 922504,
1115
+ "minutes": 25.75,
1116
+ "mfu_percent": 22.2,
1117
+ "gpu_util_percent": 100,
1118
+ "gpu_memory_gb": 85.1
1119
+ },
1120
+ {
1121
+ "step": 5300,
1122
+ "train_loss": 2.9704,
1123
+ "lr": 0.001802,
1124
+ "tokens_per_second": 922274,
1125
+ "minutes": 25.99,
1126
+ "mfu_percent": 22.2,
1127
+ "gpu_util_percent": 100,
1128
+ "gpu_memory_gb": 85.1
1129
+ },
1130
+ {
1131
+ "step": 5350,
1132
+ "train_loss": 2.9492,
1133
+ "lr": 0.001783,
1134
+ "tokens_per_second": 921682,
1135
+ "minutes": 26.23,
1136
+ "mfu_percent": 22.2,
1137
+ "gpu_util_percent": 100,
1138
+ "gpu_memory_gb": 85.1
1139
+ },
1140
+ {
1141
+ "step": 5400,
1142
+ "train_loss": 3.0025,
1143
+ "lr": 0.001765,
1144
+ "tokens_per_second": 921085,
1145
+ "minutes": 26.46,
1146
+ "mfu_percent": 22.2,
1147
+ "gpu_util_percent": 100,
1148
+ "gpu_memory_gb": 85.1
1149
+ },
1150
+ {
1151
+ "step": 5450,
1152
+ "train_loss": 2.9997,
1153
+ "lr": 0.001746,
1154
+ "tokens_per_second": 921161,
1155
+ "minutes": 26.7,
1156
+ "mfu_percent": 22.2,
1157
+ "gpu_util_percent": 100,
1158
+ "gpu_memory_gb": 85.1
1159
+ },
1160
+ {
1161
+ "step": 5500,
1162
+ "train_loss": 3.012,
1163
+ "lr": 0.001728,
1164
+ "tokens_per_second": 921331,
1165
+ "minutes": 26.94,
1166
+ "mfu_percent": 22.2,
1167
+ "gpu_util_percent": 100,
1168
+ "gpu_memory_gb": 85.1,
1169
+ "val_loss": 3.0312
1170
+ },
1171
+ {
1172
+ "step": 5550,
1173
+ "train_loss": 3.0025,
1174
+ "lr": 0.001709,
1175
+ "tokens_per_second": 921305,
1176
+ "minutes": 27.22,
1177
+ "mfu_percent": 22.2,
1178
+ "gpu_util_percent": 100,
1179
+ "gpu_memory_gb": 85.1
1180
+ },
1181
+ {
1182
+ "step": 5600,
1183
+ "train_loss": 2.9965,
1184
+ "lr": 0.001691,
1185
+ "tokens_per_second": 921367,
1186
+ "minutes": 27.46,
1187
+ "mfu_percent": 22.2,
1188
+ "gpu_util_percent": 100,
1189
+ "gpu_memory_gb": 85.1
1190
+ },
1191
+ {
1192
+ "step": 5650,
1193
+ "train_loss": 3.0261,
1194
+ "lr": 0.001672,
1195
+ "tokens_per_second": 921494,
1196
+ "minutes": 27.7,
1197
+ "mfu_percent": 22.2,
1198
+ "gpu_util_percent": 100,
1199
+ "gpu_memory_gb": 85.1
1200
+ },
1201
+ {
1202
+ "step": 5700,
1203
+ "train_loss": 2.9857,
1204
+ "lr": 0.001654,
1205
+ "tokens_per_second": 921931,
1206
+ "minutes": 27.93,
1207
+ "mfu_percent": 22.2,
1208
+ "gpu_util_percent": 100,
1209
+ "gpu_memory_gb": 85.1
1210
+ },
1211
+ {
1212
+ "step": 5750,
1213
+ "train_loss": 2.9916,
1214
+ "lr": 0.001635,
1215
+ "tokens_per_second": 921797,
1216
+ "minutes": 28.17,
1217
+ "mfu_percent": 22.2,
1218
+ "gpu_util_percent": 100,
1219
+ "gpu_memory_gb": 85.1
1220
+ },
1221
+ {
1222
+ "step": 5800,
1223
+ "train_loss": 2.9868,
1224
+ "lr": 0.001616,
1225
+ "tokens_per_second": 921701,
1226
+ "minutes": 28.41,
1227
+ "mfu_percent": 22.2,
1228
+ "gpu_util_percent": 100,
1229
+ "gpu_memory_gb": 85.1
1230
+ },
1231
+ {
1232
+ "step": 5850,
1233
+ "train_loss": 3.0135,
1234
+ "lr": 0.001598,
1235
+ "tokens_per_second": 921157,
1236
+ "minutes": 28.64,
1237
+ "mfu_percent": 22.2,
1238
+ "gpu_util_percent": 100,
1239
+ "gpu_memory_gb": 85.1
1240
+ },
1241
+ {
1242
+ "step": 5900,
1243
+ "train_loss": 3.0171,
1244
+ "lr": 0.001579,
1245
+ "tokens_per_second": 921347,
1246
+ "minutes": 28.88,
1247
+ "mfu_percent": 22.2,
1248
+ "gpu_util_percent": 100,
1249
+ "gpu_memory_gb": 85.1
1250
+ },
1251
+ {
1252
+ "step": 5950,
1253
+ "train_loss": 3.0381,
1254
+ "lr": 0.001561,
1255
+ "tokens_per_second": 921107,
1256
+ "minutes": 29.12,
1257
+ "mfu_percent": 22.2,
1258
+ "gpu_util_percent": 100,
1259
+ "gpu_memory_gb": 85.1
1260
+ },
1261
+ {
1262
+ "step": 6000,
1263
+ "train_loss": 3.0234,
1264
+ "lr": 0.001542,
1265
+ "tokens_per_second": 920618,
1266
+ "minutes": 29.35,
1267
+ "mfu_percent": 22.2,
1268
+ "gpu_util_percent": 100,
1269
+ "gpu_memory_gb": 85.1,
1270
+ "val_loss": 3.014
1271
+ },
1272
+ {
1273
+ "step": 6050,
1274
+ "train_loss": 2.9671,
1275
+ "lr": 0.001524,
1276
+ "tokens_per_second": 920459,
1277
+ "minutes": 29.64,
1278
+ "mfu_percent": 22.2,
1279
+ "gpu_util_percent": 100,
1280
+ "gpu_memory_gb": 85.1
1281
+ },
1282
+ {
1283
+ "step": 6100,
1284
+ "train_loss": 3.0432,
1285
+ "lr": 0.001505,
1286
+ "tokens_per_second": 919920,
1287
+ "minutes": 29.88,
1288
+ "mfu_percent": 22.2,
1289
+ "gpu_util_percent": 100,
1290
+ "gpu_memory_gb": 85.1
1291
+ },
1292
+ {
1293
+ "step": 6150,
1294
+ "train_loss": 3.0124,
1295
+ "lr": 0.001487,
1296
+ "tokens_per_second": 920638,
1297
+ "minutes": 30.11,
1298
+ "mfu_percent": 22.2,
1299
+ "gpu_util_percent": 100,
1300
+ "gpu_memory_gb": 85.1
1301
+ },
1302
+ {
1303
+ "step": 6200,
1304
+ "train_loss": 3.0045,
1305
+ "lr": 0.001468,
1306
+ "tokens_per_second": 919978,
1307
+ "minutes": 30.35,
1308
+ "mfu_percent": 22.2,
1309
+ "gpu_util_percent": 100,
1310
+ "gpu_memory_gb": 85.1
1311
+ },
1312
+ {
1313
+ "step": 6250,
1314
+ "train_loss": 3.0096,
1315
+ "lr": 0.00145,
1316
+ "tokens_per_second": 919568,
1317
+ "minutes": 30.59,
1318
+ "mfu_percent": 22.2,
1319
+ "gpu_util_percent": 100,
1320
+ "gpu_memory_gb": 85.1
1321
+ },
1322
+ {
1323
+ "step": 6300,
1324
+ "train_loss": 2.9723,
1325
+ "lr": 0.001432,
1326
+ "tokens_per_second": 919292,
1327
+ "minutes": 30.83,
1328
+ "mfu_percent": 22.2,
1329
+ "gpu_util_percent": 100,
1330
+ "gpu_memory_gb": 85.1
1331
+ },
1332
+ {
1333
+ "step": 6350,
1334
+ "train_loss": 2.9286,
1335
+ "lr": 0.001413,
1336
+ "tokens_per_second": 919775,
1337
+ "minutes": 31.06,
1338
+ "mfu_percent": 22.2,
1339
+ "gpu_util_percent": 100,
1340
+ "gpu_memory_gb": 85.1
1341
+ },
1342
+ {
1343
+ "step": 6400,
1344
+ "train_loss": 3.0349,
1345
+ "lr": 0.001395,
1346
+ "tokens_per_second": 920505,
1347
+ "minutes": 31.3,
1348
+ "mfu_percent": 22.2,
1349
+ "gpu_util_percent": 100,
1350
+ "gpu_memory_gb": 85.1
1351
+ },
1352
+ {
1353
+ "step": 6450,
1354
+ "train_loss": 2.9374,
1355
+ "lr": 0.001377,
1356
+ "tokens_per_second": 920417,
1357
+ "minutes": 31.54,
1358
+ "mfu_percent": 22.2,
1359
+ "gpu_util_percent": 100,
1360
+ "gpu_memory_gb": 85.1
1361
+ },
1362
+ {
1363
+ "step": 6500,
1364
+ "train_loss": 2.9625,
1365
+ "lr": 0.001359,
1366
+ "tokens_per_second": 919773,
1367
+ "minutes": 31.78,
1368
+ "mfu_percent": 22.2,
1369
+ "gpu_util_percent": 100,
1370
+ "gpu_memory_gb": 85.1,
1371
+ "val_loss": 2.9999
1372
+ },
1373
+ {
1374
+ "step": 6550,
1375
+ "train_loss": 2.9994,
1376
+ "lr": 0.001341,
1377
+ "tokens_per_second": 919974,
1378
+ "minutes": 32.06,
1379
+ "mfu_percent": 22.2,
1380
+ "gpu_util_percent": 100,
1381
+ "gpu_memory_gb": 85.1
1382
+ },
1383
+ {
1384
+ "step": 6600,
1385
+ "train_loss": 2.9899,
1386
+ "lr": 0.001323,
1387
+ "tokens_per_second": 920286,
1388
+ "minutes": 32.3,
1389
+ "mfu_percent": 22.2,
1390
+ "gpu_util_percent": 100,
1391
+ "gpu_memory_gb": 85.1
1392
+ },
1393
+ {
1394
+ "step": 6650,
1395
+ "train_loss": 2.9663,
1396
+ "lr": 0.001305,
1397
+ "tokens_per_second": 920398,
1398
+ "minutes": 32.54,
1399
+ "mfu_percent": 22.2,
1400
+ "gpu_util_percent": 100,
1401
+ "gpu_memory_gb": 85.1
1402
+ },
1403
+ {
1404
+ "step": 6700,
1405
+ "train_loss": 2.9463,
1406
+ "lr": 0.001287,
1407
+ "tokens_per_second": 921337,
1408
+ "minutes": 32.77,
1409
+ "mfu_percent": 22.2,
1410
+ "gpu_util_percent": 100,
1411
+ "gpu_memory_gb": 85.1
1412
+ },
1413
+ {
1414
+ "step": 6750,
1415
+ "train_loss": 2.9973,
1416
+ "lr": 0.001269,
1417
+ "tokens_per_second": 921370,
1418
+ "minutes": 33.01,
1419
+ "mfu_percent": 22.2,
1420
+ "gpu_util_percent": 100,
1421
+ "gpu_memory_gb": 85.1
1422
+ },
1423
+ {
1424
+ "step": 6800,
1425
+ "train_loss": 2.9206,
1426
+ "lr": 0.001251,
1427
+ "tokens_per_second": 920742,
1428
+ "minutes": 33.25,
1429
+ "mfu_percent": 22.2,
1430
+ "gpu_util_percent": 100,
1431
+ "gpu_memory_gb": 85.1
1432
+ },
1433
+ {
1434
+ "step": 6850,
1435
+ "train_loss": 2.9574,
1436
+ "lr": 0.001233,
1437
+ "tokens_per_second": 920284,
1438
+ "minutes": 33.48,
1439
+ "mfu_percent": 22.2,
1440
+ "gpu_util_percent": 100,
1441
+ "gpu_memory_gb": 85.1
1442
+ },
1443
+ {
1444
+ "step": 6900,
1445
+ "train_loss": 2.9734,
1446
+ "lr": 0.001216,
1447
+ "tokens_per_second": 919909,
1448
+ "minutes": 33.72,
1449
+ "mfu_percent": 22.2,
1450
+ "gpu_util_percent": 100,
1451
+ "gpu_memory_gb": 85.1
1452
+ },
1453
+ {
1454
+ "step": 6950,
1455
+ "train_loss": 2.992,
1456
+ "lr": 0.001198,
1457
+ "tokens_per_second": 919380,
1458
+ "minutes": 33.96,
1459
+ "mfu_percent": 22.2,
1460
+ "gpu_util_percent": 100,
1461
+ "gpu_memory_gb": 85.1
1462
+ },
1463
+ {
1464
+ "step": 7000,
1465
+ "train_loss": 2.9482,
1466
+ "lr": 0.001181,
1467
+ "tokens_per_second": 919305,
1468
+ "minutes": 34.2,
1469
+ "mfu_percent": 22.2,
1470
+ "gpu_util_percent": 100,
1471
+ "gpu_memory_gb": 85.1,
1472
+ "val_loss": 2.9834
1473
+ },
1474
+ {
1475
+ "step": 7050,
1476
+ "train_loss": 2.9944,
1477
+ "lr": 0.001163,
1478
+ "tokens_per_second": 919402,
1479
+ "minutes": 34.48,
1480
+ "mfu_percent": 22.2,
1481
+ "gpu_util_percent": 100,
1482
+ "gpu_memory_gb": 85.1
1483
+ },
1484
+ {
1485
+ "step": 7100,
1486
+ "train_loss": 2.9649,
1487
+ "lr": 0.001146,
1488
+ "tokens_per_second": 919490,
1489
+ "minutes": 34.72,
1490
+ "mfu_percent": 22.2,
1491
+ "gpu_util_percent": 100,
1492
+ "gpu_memory_gb": 85.1
1493
+ },
1494
+ {
1495
+ "step": 7150,
1496
+ "train_loss": 2.9533,
1497
+ "lr": 0.001129,
1498
+ "tokens_per_second": 919450,
1499
+ "minutes": 34.96,
1500
+ "mfu_percent": 22.2,
1501
+ "gpu_util_percent": 100,
1502
+ "gpu_memory_gb": 85.1
1503
+ },
1504
+ {
1505
+ "step": 7200,
1506
+ "train_loss": 2.9467,
1507
+ "lr": 0.001112,
1508
+ "tokens_per_second": 919809,
1509
+ "minutes": 35.19,
1510
+ "mfu_percent": 22.2,
1511
+ "gpu_util_percent": 100,
1512
+ "gpu_memory_gb": 85.1
1513
+ },
1514
+ {
1515
+ "step": 7250,
1516
+ "train_loss": 2.9482,
1517
+ "lr": 0.001095,
1518
+ "tokens_per_second": 920118,
1519
+ "minutes": 35.43,
1520
+ "mfu_percent": 22.2,
1521
+ "gpu_util_percent": 100,
1522
+ "gpu_memory_gb": 85.1
1523
+ },
1524
+ {
1525
+ "step": 7300,
1526
+ "train_loss": 2.9288,
1527
+ "lr": 0.001078,
1528
+ "tokens_per_second": 920161,
1529
+ "minutes": 35.67,
1530
+ "mfu_percent": 22.2,
1531
+ "gpu_util_percent": 100,
1532
+ "gpu_memory_gb": 85.1
1533
+ },
1534
+ {
1535
+ "step": 7350,
1536
+ "train_loss": 2.9711,
1537
+ "lr": 0.001061,
1538
+ "tokens_per_second": 920166,
1539
+ "minutes": 35.91,
1540
+ "mfu_percent": 22.2,
1541
+ "gpu_util_percent": 100,
1542
+ "gpu_memory_gb": 85.1
1543
+ },
1544
+ {
1545
+ "step": 7400,
1546
+ "train_loss": 2.9711,
1547
+ "lr": 0.001045,
1548
+ "tokens_per_second": 920031,
1549
+ "minutes": 36.14,
1550
+ "mfu_percent": 22.2,
1551
+ "gpu_util_percent": 100,
1552
+ "gpu_memory_gb": 85.1
1553
+ },
1554
+ {
1555
+ "step": 7450,
1556
+ "train_loss": 2.9619,
1557
+ "lr": 0.001028,
1558
+ "tokens_per_second": 920234,
1559
+ "minutes": 36.38,
1560
+ "mfu_percent": 22.2,
1561
+ "gpu_util_percent": 100,
1562
+ "gpu_memory_gb": 85.1
1563
+ },
1564
+ {
1565
+ "step": 7500,
1566
+ "train_loss": 2.9199,
1567
+ "lr": 0.001012,
1568
+ "tokens_per_second": 920421,
1569
+ "minutes": 36.62,
1570
+ "mfu_percent": 22.2,
1571
+ "gpu_util_percent": 100,
1572
+ "gpu_memory_gb": 85.1,
1573
+ "val_loss": 2.9683
1574
+ },
1575
+ {
1576
+ "step": 7550,
1577
+ "train_loss": 2.9091,
1578
+ "lr": 0.000995,
1579
+ "tokens_per_second": 921260,
1580
+ "minutes": 36.9,
1581
+ "mfu_percent": 22.2,
1582
+ "gpu_util_percent": 100,
1583
+ "gpu_memory_gb": 85.1
1584
+ },
1585
+ {
1586
+ "step": 7600,
1587
+ "train_loss": 2.9747,
1588
+ "lr": 0.000979,
1589
+ "tokens_per_second": 920906,
1590
+ "minutes": 37.14,
1591
+ "mfu_percent": 22.2,
1592
+ "gpu_util_percent": 100,
1593
+ "gpu_memory_gb": 85.1
1594
+ },
1595
+ {
1596
+ "step": 7650,
1597
+ "train_loss": 2.9445,
1598
+ "lr": 0.000963,
1599
+ "tokens_per_second": 920371,
1600
+ "minutes": 37.38,
1601
+ "mfu_percent": 22.2,
1602
+ "gpu_util_percent": 100,
1603
+ "gpu_memory_gb": 85.1
1604
+ },
1605
+ {
1606
+ "step": 7700,
1607
+ "train_loss": 2.9593,
1608
+ "lr": 0.000947,
1609
+ "tokens_per_second": 920018,
1610
+ "minutes": 37.62,
1611
+ "mfu_percent": 22.2,
1612
+ "gpu_util_percent": 100,
1613
+ "gpu_memory_gb": 85.1
1614
+ },
1615
+ {
1616
+ "step": 7750,
1617
+ "train_loss": 2.9826,
1618
+ "lr": 0.000931,
1619
+ "tokens_per_second": 920997,
1620
+ "minutes": 37.85,
1621
+ "mfu_percent": 22.2,
1622
+ "gpu_util_percent": 100,
1623
+ "gpu_memory_gb": 85.1
1624
+ },
1625
+ {
1626
+ "step": 7800,
1627
+ "train_loss": 2.933,
1628
+ "lr": 0.000916,
1629
+ "tokens_per_second": 921462,
1630
+ "minutes": 38.09,
1631
+ "mfu_percent": 22.2,
1632
+ "gpu_util_percent": 100,
1633
+ "gpu_memory_gb": 85.1
1634
+ },
1635
+ {
1636
+ "step": 7850,
1637
+ "train_loss": 2.9202,
1638
+ "lr": 0.0009,
1639
+ "tokens_per_second": 921036,
1640
+ "minutes": 38.33,
1641
+ "mfu_percent": 22.2,
1642
+ "gpu_util_percent": 100,
1643
+ "gpu_memory_gb": 85.1
1644
+ },
1645
+ {
1646
+ "step": 7900,
1647
+ "train_loss": 2.9152,
1648
+ "lr": 0.000885,
1649
+ "tokens_per_second": 920538,
1650
+ "minutes": 38.56,
1651
+ "mfu_percent": 22.2,
1652
+ "gpu_util_percent": 100,
1653
+ "gpu_memory_gb": 85.1
1654
+ },
1655
+ {
1656
+ "step": 7950,
1657
+ "train_loss": 2.926,
1658
+ "lr": 0.00087,
1659
+ "tokens_per_second": 919615,
1660
+ "minutes": 38.8,
1661
+ "mfu_percent": 22.2,
1662
+ "gpu_util_percent": 100,
1663
+ "gpu_memory_gb": 85.1
1664
+ },
1665
+ {
1666
+ "step": 8000,
1667
+ "train_loss": 2.8966,
1668
+ "lr": 0.000855,
1669
+ "tokens_per_second": 919288,
1670
+ "minutes": 39.04,
1671
+ "mfu_percent": 22.2,
1672
+ "gpu_util_percent": 100,
1673
+ "gpu_memory_gb": 85.1,
1674
+ "val_loss": 2.9544
1675
+ },
1676
+ {
1677
+ "step": 8050,
1678
+ "train_loss": 2.9517,
1679
+ "lr": 0.00084,
1680
+ "tokens_per_second": 919884,
1681
+ "minutes": 39.32,
1682
+ "mfu_percent": 22.2,
1683
+ "gpu_util_percent": 100,
1684
+ "gpu_memory_gb": 85.1
1685
+ },
1686
+ {
1687
+ "step": 8100,
1688
+ "train_loss": 2.9257,
1689
+ "lr": 0.000825,
1690
+ "tokens_per_second": 921250,
1691
+ "minutes": 39.56,
1692
+ "mfu_percent": 22.2,
1693
+ "gpu_util_percent": 100,
1694
+ "gpu_memory_gb": 85.1
1695
+ },
1696
+ {
1697
+ "step": 8150,
1698
+ "train_loss": 2.9299,
1699
+ "lr": 0.00081,
1700
+ "tokens_per_second": 921425,
1701
+ "minutes": 39.8,
1702
+ "mfu_percent": 22.2,
1703
+ "gpu_util_percent": 100,
1704
+ "gpu_memory_gb": 85.1
1705
+ },
1706
+ {
1707
+ "step": 8200,
1708
+ "train_loss": 2.934,
1709
+ "lr": 0.000796,
1710
+ "tokens_per_second": 921296,
1711
+ "minutes": 40.04,
1712
+ "mfu_percent": 22.2,
1713
+ "gpu_util_percent": 100,
1714
+ "gpu_memory_gb": 85.1
1715
+ },
1716
+ {
1717
+ "step": 8250,
1718
+ "train_loss": 2.8999,
1719
+ "lr": 0.000782,
1720
+ "tokens_per_second": 920909,
1721
+ "minutes": 40.27,
1722
+ "mfu_percent": 22.2,
1723
+ "gpu_util_percent": 100,
1724
+ "gpu_memory_gb": 85.1
1725
+ },
1726
+ {
1727
+ "step": 8300,
1728
+ "train_loss": 2.9048,
1729
+ "lr": 0.000767,
1730
+ "tokens_per_second": 920173,
1731
+ "minutes": 40.51,
1732
+ "mfu_percent": 22.2,
1733
+ "gpu_util_percent": 100,
1734
+ "gpu_memory_gb": 85.1
1735
+ },
1736
+ {
1737
+ "step": 8350,
1738
+ "train_loss": 2.8714,
1739
+ "lr": 0.000753,
1740
+ "tokens_per_second": 919853,
1741
+ "minutes": 40.75,
1742
+ "mfu_percent": 22.2,
1743
+ "gpu_util_percent": 100,
1744
+ "gpu_memory_gb": 85.1
1745
+ },
1746
+ {
1747
+ "step": 8400,
1748
+ "train_loss": 2.8722,
1749
+ "lr": 0.00074,
1750
+ "tokens_per_second": 919551,
1751
+ "minutes": 40.98,
1752
+ "mfu_percent": 22.2,
1753
+ "gpu_util_percent": 100,
1754
+ "gpu_memory_gb": 85.1
1755
+ },
1756
+ {
1757
+ "step": 8450,
1758
+ "train_loss": 2.9693,
1759
+ "lr": 0.000726,
1760
+ "tokens_per_second": 920679,
1761
+ "minutes": 41.22,
1762
+ "mfu_percent": 22.2,
1763
+ "gpu_util_percent": 100,
1764
+ "gpu_memory_gb": 85.1
1765
+ },
1766
+ {
1767
+ "step": 8500,
1768
+ "train_loss": 2.9393,
1769
+ "lr": 0.000713,
1770
+ "tokens_per_second": 921776,
1771
+ "minutes": 41.46,
1772
+ "mfu_percent": 22.2,
1773
+ "gpu_util_percent": 100,
1774
+ "gpu_memory_gb": 85.1,
1775
+ "val_loss": 2.9392
1776
+ },
1777
+ {
1778
+ "step": 8550,
1779
+ "train_loss": 2.8964,
1780
+ "lr": 0.000699,
1781
+ "tokens_per_second": 920913,
1782
+ "minutes": 41.74,
1783
+ "mfu_percent": 22.2,
1784
+ "gpu_util_percent": 100,
1785
+ "gpu_memory_gb": 85.1
1786
+ },
1787
+ {
1788
+ "step": 8600,
1789
+ "train_loss": 2.9192,
1790
+ "lr": 0.000686,
1791
+ "tokens_per_second": 919992,
1792
+ "minutes": 41.98,
1793
+ "mfu_percent": 22.2,
1794
+ "gpu_util_percent": 100,
1795
+ "gpu_memory_gb": 85.1
1796
+ },
1797
+ {
1798
+ "step": 8650,
1799
+ "train_loss": 2.9288,
1800
+ "lr": 0.000673,
1801
+ "tokens_per_second": 919914,
1802
+ "minutes": 42.22,
1803
+ "mfu_percent": 22.2,
1804
+ "gpu_util_percent": 100,
1805
+ "gpu_memory_gb": 85.1
1806
+ },
1807
+ {
1808
+ "step": 8700,
1809
+ "train_loss": 2.8246,
1810
+ "lr": 0.000661,
1811
+ "tokens_per_second": 919626,
1812
+ "minutes": 42.46,
1813
+ "mfu_percent": 22.2,
1814
+ "gpu_util_percent": 100,
1815
+ "gpu_memory_gb": 85.1
1816
+ },
1817
+ {
1818
+ "step": 8750,
1819
+ "train_loss": 2.9473,
1820
+ "lr": 0.000648,
1821
+ "tokens_per_second": 919635,
1822
+ "minutes": 42.69,
1823
+ "mfu_percent": 22.2,
1824
+ "gpu_util_percent": 100,
1825
+ "gpu_memory_gb": 85.1
1826
+ },
1827
+ {
1828
+ "step": 8800,
1829
+ "train_loss": 2.8781,
1830
+ "lr": 0.000636,
1831
+ "tokens_per_second": 921027,
1832
+ "minutes": 42.93,
1833
+ "mfu_percent": 22.2,
1834
+ "gpu_util_percent": 100,
1835
+ "gpu_memory_gb": 85.1
1836
+ },
1837
+ {
1838
+ "step": 8850,
1839
+ "train_loss": 2.8393,
1840
+ "lr": 0.000624,
1841
+ "tokens_per_second": 921693,
1842
+ "minutes": 43.17,
1843
+ "mfu_percent": 22.2,
1844
+ "gpu_util_percent": 100,
1845
+ "gpu_memory_gb": 85.1
1846
+ },
1847
+ {
1848
+ "step": 8900,
1849
+ "train_loss": 2.9105,
1850
+ "lr": 0.000612,
1851
+ "tokens_per_second": 922213,
1852
+ "minutes": 43.4,
1853
+ "mfu_percent": 22.2,
1854
+ "gpu_util_percent": 100,
1855
+ "gpu_memory_gb": 85.1
1856
+ },
1857
+ {
1858
+ "step": 8950,
1859
+ "train_loss": 2.9286,
1860
+ "lr": 0.0006,
1861
+ "tokens_per_second": 923184,
1862
+ "minutes": 43.64,
1863
+ "mfu_percent": 22.3,
1864
+ "gpu_util_percent": 100,
1865
+ "gpu_memory_gb": 85.1
1866
+ },
1867
+ {
1868
+ "step": 9000,
1869
+ "train_loss": 2.8668,
1870
+ "lr": 0.000588,
1871
+ "tokens_per_second": 923153,
1872
+ "minutes": 43.88,
1873
+ "mfu_percent": 22.3,
1874
+ "gpu_util_percent": 100,
1875
+ "gpu_memory_gb": 85.1,
1876
+ "val_loss": 2.9286
1877
+ },
1878
+ {
1879
+ "step": 9050,
1880
+ "train_loss": 2.9063,
1881
+ "lr": 0.000577,
1882
+ "tokens_per_second": 922088,
1883
+ "minutes": 44.16,
1884
+ "mfu_percent": 22.2,
1885
+ "gpu_util_percent": 100,
1886
+ "gpu_memory_gb": 85.1
1887
+ },
1888
+ {
1889
+ "step": 9100,
1890
+ "train_loss": 2.9428,
1891
+ "lr": 0.000566,
1892
+ "tokens_per_second": 921296,
1893
+ "minutes": 44.4,
1894
+ "mfu_percent": 22.2,
1895
+ "gpu_util_percent": 100,
1896
+ "gpu_memory_gb": 85.1
1897
+ },
1898
+ {
1899
+ "step": 9150,
1900
+ "train_loss": 2.9146,
1901
+ "lr": 0.000555,
1902
+ "tokens_per_second": 920478,
1903
+ "minutes": 44.64,
1904
+ "mfu_percent": 22.2,
1905
+ "gpu_util_percent": 100,
1906
+ "gpu_memory_gb": 85.1
1907
+ },
1908
+ {
1909
+ "step": 9200,
1910
+ "train_loss": 2.9337,
1911
+ "lr": 0.000544,
1912
+ "tokens_per_second": 919857,
1913
+ "minutes": 44.87,
1914
+ "mfu_percent": 22.2,
1915
+ "gpu_util_percent": 100,
1916
+ "gpu_memory_gb": 85.1
1917
+ },
1918
+ {
1919
+ "step": 9250,
1920
+ "train_loss": 2.8898,
1921
+ "lr": 0.000533,
1922
+ "tokens_per_second": 919677,
1923
+ "minutes": 45.11,
1924
+ "mfu_percent": 22.2,
1925
+ "gpu_util_percent": 100,
1926
+ "gpu_memory_gb": 85.1
1927
+ },
1928
+ {
1929
+ "step": 9300,
1930
+ "train_loss": 2.8907,
1931
+ "lr": 0.000523,
1932
+ "tokens_per_second": 919018,
1933
+ "minutes": 45.35,
1934
+ "mfu_percent": 22.2,
1935
+ "gpu_util_percent": 100,
1936
+ "gpu_memory_gb": 85.1
1937
+ },
1938
+ {
1939
+ "step": 9350,
1940
+ "train_loss": 2.9104,
1941
+ "lr": 0.000513,
1942
+ "tokens_per_second": 919151,
1943
+ "minutes": 45.59,
1944
+ "mfu_percent": 22.2,
1945
+ "gpu_util_percent": 100,
1946
+ "gpu_memory_gb": 85.1
1947
+ },
1948
+ {
1949
+ "step": 9400,
1950
+ "train_loss": 2.9346,
1951
+ "lr": 0.000503,
1952
+ "tokens_per_second": 919820,
1953
+ "minutes": 45.82,
1954
+ "mfu_percent": 22.2,
1955
+ "gpu_util_percent": 100,
1956
+ "gpu_memory_gb": 85.1
1957
+ },
1958
+ {
1959
+ "step": 9450,
1960
+ "train_loss": 2.864,
1961
+ "lr": 0.000493,
1962
+ "tokens_per_second": 920435,
1963
+ "minutes": 46.06,
1964
+ "mfu_percent": 22.2,
1965
+ "gpu_util_percent": 100,
1966
+ "gpu_memory_gb": 85.1
1967
+ },
1968
+ {
1969
+ "step": 9500,
1970
+ "train_loss": 2.8496,
1971
+ "lr": 0.000484,
1972
+ "tokens_per_second": 920689,
1973
+ "minutes": 46.3,
1974
+ "mfu_percent": 22.2,
1975
+ "gpu_util_percent": 100,
1976
+ "gpu_memory_gb": 85.1,
1977
+ "val_loss": 2.9166
1978
+ },
1979
+ {
1980
+ "step": 9550,
1981
+ "train_loss": 2.9794,
1982
+ "lr": 0.000475,
1983
+ "tokens_per_second": 920761,
1984
+ "minutes": 46.58,
1985
+ "mfu_percent": 22.2,
1986
+ "gpu_util_percent": 100,
1987
+ "gpu_memory_gb": 85.1
1988
+ },
1989
+ {
1990
+ "step": 9600,
1991
+ "train_loss": 2.8557,
1992
+ "lr": 0.000466,
1993
+ "tokens_per_second": 921094,
1994
+ "minutes": 46.82,
1995
+ "mfu_percent": 22.2,
1996
+ "gpu_util_percent": 100,
1997
+ "gpu_memory_gb": 85.1
1998
+ },
1999
+ {
2000
+ "step": 9650,
2001
+ "train_loss": 2.8992,
2002
+ "lr": 0.000457,
2003
+ "tokens_per_second": 921528,
2004
+ "minutes": 47.06,
2005
+ "mfu_percent": 22.2,
2006
+ "gpu_util_percent": 100,
2007
+ "gpu_memory_gb": 85.1
2008
+ },
2009
+ {
2010
+ "step": 9700,
2011
+ "train_loss": 2.835,
2012
+ "lr": 0.000448,
2013
+ "tokens_per_second": 920716,
2014
+ "minutes": 47.29,
2015
+ "mfu_percent": 22.2,
2016
+ "gpu_util_percent": 100,
2017
+ "gpu_memory_gb": 85.1
2018
+ },
2019
+ {
2020
+ "step": 9750,
2021
+ "train_loss": 2.9188,
2022
+ "lr": 0.00044,
2023
+ "tokens_per_second": 920417,
2024
+ "minutes": 47.53,
2025
+ "mfu_percent": 22.2,
2026
+ "gpu_util_percent": 100,
2027
+ "gpu_memory_gb": 85.1
2028
+ },
2029
+ {
2030
+ "step": 9800,
2031
+ "train_loss": 2.925,
2032
+ "lr": 0.000432,
2033
+ "tokens_per_second": 920052,
2034
+ "minutes": 47.77,
2035
+ "mfu_percent": 22.2,
2036
+ "gpu_util_percent": 100,
2037
+ "gpu_memory_gb": 85.1
2038
+ },
2039
+ {
2040
+ "step": 9850,
2041
+ "train_loss": 2.9457,
2042
+ "lr": 0.000424,
2043
+ "tokens_per_second": 919561,
2044
+ "minutes": 48.01,
2045
+ "mfu_percent": 22.2,
2046
+ "gpu_util_percent": 100,
2047
+ "gpu_memory_gb": 85.1
2048
+ },
2049
+ {
2050
+ "step": 9900,
2051
+ "train_loss": 2.9236,
2052
+ "lr": 0.000416,
2053
+ "tokens_per_second": 919301,
2054
+ "minutes": 48.24,
2055
+ "mfu_percent": 22.2,
2056
+ "gpu_util_percent": 100,
2057
+ "gpu_memory_gb": 85.1
2058
+ },
2059
+ {
2060
+ "step": 9950,
2061
+ "train_loss": 2.8542,
2062
+ "lr": 0.000409,
2063
+ "tokens_per_second": 919176,
2064
+ "minutes": 48.48,
2065
+ "mfu_percent": 22.2,
2066
+ "gpu_util_percent": 100,
2067
+ "gpu_memory_gb": 85.1
2068
+ },
2069
+ {
2070
+ "step": 10000,
2071
+ "train_loss": 2.8907,
2072
+ "lr": 0.000402,
2073
+ "tokens_per_second": 919788,
2074
+ "minutes": 48.72,
2075
+ "mfu_percent": 22.2,
2076
+ "gpu_util_percent": 100,
2077
+ "gpu_memory_gb": 85.1,
2078
+ "val_loss": 2.9086
2079
+ },
2080
+ {
2081
+ "step": 10050,
2082
+ "train_loss": 2.7981,
2083
+ "lr": 0.000395,
2084
+ "tokens_per_second": 920621,
2085
+ "minutes": 49.0,
2086
+ "mfu_percent": 22.2,
2087
+ "gpu_util_percent": 100,
2088
+ "gpu_memory_gb": 85.1
2089
+ },
2090
+ {
2091
+ "step": 10100,
2092
+ "train_loss": 2.8218,
2093
+ "lr": 0.000388,
2094
+ "tokens_per_second": 920907,
2095
+ "minutes": 49.24,
2096
+ "mfu_percent": 22.2,
2097
+ "gpu_util_percent": 100,
2098
+ "gpu_memory_gb": 85.1
2099
+ },
2100
+ {
2101
+ "step": 10150,
2102
+ "train_loss": 2.8762,
2103
+ "lr": 0.000382,
2104
+ "tokens_per_second": 921101,
2105
+ "minutes": 49.48,
2106
+ "mfu_percent": 22.2,
2107
+ "gpu_util_percent": 100,
2108
+ "gpu_memory_gb": 85.1
2109
+ },
2110
+ {
2111
+ "step": 10200,
2112
+ "train_loss": 2.8884,
2113
+ "lr": 0.000375,
2114
+ "tokens_per_second": 920843,
2115
+ "minutes": 49.72,
2116
+ "mfu_percent": 22.2,
2117
+ "gpu_util_percent": 100,
2118
+ "gpu_memory_gb": 85.1
2119
+ },
2120
+ {
2121
+ "step": 10250,
2122
+ "train_loss": 2.8617,
2123
+ "lr": 0.000369,
2124
+ "tokens_per_second": 921002,
2125
+ "minutes": 49.95,
2126
+ "mfu_percent": 22.2,
2127
+ "gpu_util_percent": 100,
2128
+ "gpu_memory_gb": 85.1
2129
+ },
2130
+ {
2131
+ "step": 10300,
2132
+ "train_loss": 2.8414,
2133
+ "lr": 0.000364,
2134
+ "tokens_per_second": 921151,
2135
+ "minutes": 50.19,
2136
+ "mfu_percent": 22.2,
2137
+ "gpu_util_percent": 100,
2138
+ "gpu_memory_gb": 85.1
2139
+ },
2140
+ {
2141
+ "step": 10350,
2142
+ "train_loss": 2.8439,
2143
+ "lr": 0.000358,
2144
+ "tokens_per_second": 920309,
2145
+ "minutes": 50.43,
2146
+ "mfu_percent": 22.2,
2147
+ "gpu_util_percent": 100,
2148
+ "gpu_memory_gb": 85.1
2149
+ },
2150
+ {
2151
+ "step": 10400,
2152
+ "train_loss": 2.8992,
2153
+ "lr": 0.000353,
2154
+ "tokens_per_second": 919693,
2155
+ "minutes": 50.66,
2156
+ "mfu_percent": 22.2,
2157
+ "gpu_util_percent": 100,
2158
+ "gpu_memory_gb": 85.1
2159
+ },
2160
+ {
2161
+ "step": 10450,
2162
+ "train_loss": 2.8711,
2163
+ "lr": 0.000348,
2164
+ "tokens_per_second": 919270,
2165
+ "minutes": 50.9,
2166
+ "mfu_percent": 22.2,
2167
+ "gpu_util_percent": 100,
2168
+ "gpu_memory_gb": 85.1
2169
+ },
2170
+ {
2171
+ "step": 10500,
2172
+ "train_loss": 2.8533,
2173
+ "lr": 0.000343,
2174
+ "tokens_per_second": 920712,
2175
+ "minutes": 51.14,
2176
+ "mfu_percent": 22.2,
2177
+ "gpu_util_percent": 100,
2178
+ "gpu_memory_gb": 85.1,
2179
+ "val_loss": 2.9005
2180
+ },
2181
+ {
2182
+ "step": 10550,
2183
+ "train_loss": 2.8638,
2184
+ "lr": 0.000338,
2185
+ "tokens_per_second": 921492,
2186
+ "minutes": 51.42,
2187
+ "mfu_percent": 22.2,
2188
+ "gpu_util_percent": 100,
2189
+ "gpu_memory_gb": 85.1
2190
+ },
2191
+ {
2192
+ "step": 10600,
2193
+ "train_loss": 2.8673,
2194
+ "lr": 0.000334,
2195
+ "tokens_per_second": 921466,
2196
+ "minutes": 51.66,
2197
+ "mfu_percent": 22.2,
2198
+ "gpu_util_percent": 100,
2199
+ "gpu_memory_gb": 85.1
2200
+ },
2201
+ {
2202
+ "step": 10650,
2203
+ "train_loss": 2.8849,
2204
+ "lr": 0.00033,
2205
+ "tokens_per_second": 920910,
2206
+ "minutes": 51.9,
2207
+ "mfu_percent": 22.2,
2208
+ "gpu_util_percent": 100,
2209
+ "gpu_memory_gb": 85.1
2210
+ },
2211
+ {
2212
+ "step": 10700,
2213
+ "train_loss": 2.8652,
2214
+ "lr": 0.000326,
2215
+ "tokens_per_second": 920277,
2216
+ "minutes": 52.14,
2217
+ "mfu_percent": 22.2,
2218
+ "gpu_util_percent": 100,
2219
+ "gpu_memory_gb": 85.1
2220
+ },
2221
+ {
2222
+ "step": 10750,
2223
+ "train_loss": 2.8523,
2224
+ "lr": 0.000323,
2225
+ "tokens_per_second": 919903,
2226
+ "minutes": 52.37,
2227
+ "mfu_percent": 22.2,
2228
+ "gpu_util_percent": 100,
2229
+ "gpu_memory_gb": 85.1
2230
+ },
2231
+ {
2232
+ "step": 10800,
2233
+ "train_loss": 2.8797,
2234
+ "lr": 0.00032,
2235
+ "tokens_per_second": 919671,
2236
+ "minutes": 52.61,
2237
+ "mfu_percent": 22.2,
2238
+ "gpu_util_percent": 100,
2239
+ "gpu_memory_gb": 85.1
2240
+ },
2241
+ {
2242
+ "step": 10850,
2243
+ "train_loss": 2.858,
2244
+ "lr": 0.000317,
2245
+ "tokens_per_second": 919122,
2246
+ "minutes": 52.85,
2247
+ "mfu_percent": 22.2,
2248
+ "gpu_util_percent": 100,
2249
+ "gpu_memory_gb": 85.1
2250
+ },
2251
+ {
2252
+ "step": 10900,
2253
+ "train_loss": 2.8607,
2254
+ "lr": 0.000314,
2255
+ "tokens_per_second": 919161,
2256
+ "minutes": 53.09,
2257
+ "mfu_percent": 22.2,
2258
+ "gpu_util_percent": 100,
2259
+ "gpu_memory_gb": 85.1
2260
+ },
2261
+ {
2262
+ "step": 10950,
2263
+ "train_loss": 2.8733,
2264
+ "lr": 0.000311,
2265
+ "tokens_per_second": 920086,
2266
+ "minutes": 53.32,
2267
+ "mfu_percent": 22.2,
2268
+ "gpu_util_percent": 100,
2269
+ "gpu_memory_gb": 85.1
2270
+ },
2271
+ {
2272
+ "step": 11000,
2273
+ "train_loss": 2.8539,
2274
+ "lr": 0.000309,
2275
+ "tokens_per_second": 920688,
2276
+ "minutes": 53.56,
2277
+ "mfu_percent": 22.2,
2278
+ "gpu_util_percent": 100,
2279
+ "gpu_memory_gb": 85.1,
2280
+ "val_loss": 2.8962
2281
+ },
2282
+ {
2283
+ "step": 11050,
2284
+ "train_loss": 2.8992,
2285
+ "lr": 0.000307,
2286
+ "tokens_per_second": 920928,
2287
+ "minutes": 53.84,
2288
+ "mfu_percent": 22.2,
2289
+ "gpu_util_percent": 100,
2290
+ "gpu_memory_gb": 85.1
2291
+ },
2292
+ {
2293
+ "step": 11100,
2294
+ "train_loss": 2.8486,
2295
+ "lr": 0.000305,
2296
+ "tokens_per_second": 920127,
2297
+ "minutes": 54.08,
2298
+ "mfu_percent": 22.2,
2299
+ "gpu_util_percent": 100,
2300
+ "gpu_memory_gb": 85.1
2301
+ },
2302
+ {
2303
+ "step": 11150,
2304
+ "train_loss": 2.8515,
2305
+ "lr": 0.000304,
2306
+ "tokens_per_second": 919904,
2307
+ "minutes": 54.32,
2308
+ "mfu_percent": 22.2,
2309
+ "gpu_util_percent": 100,
2310
+ "gpu_memory_gb": 85.1
2311
+ },
2312
+ {
2313
+ "step": 11200,
2314
+ "train_loss": 2.8647,
2315
+ "lr": 0.000302,
2316
+ "tokens_per_second": 919524,
2317
+ "minutes": 54.56,
2318
+ "mfu_percent": 22.2,
2319
+ "gpu_util_percent": 100,
2320
+ "gpu_memory_gb": 85.1
2321
+ },
2322
+ {
2323
+ "step": 11250,
2324
+ "train_loss": 2.8809,
2325
+ "lr": 0.000301,
2326
+ "tokens_per_second": 920127,
2327
+ "minutes": 54.79,
2328
+ "mfu_percent": 22.2,
2329
+ "gpu_util_percent": 100,
2330
+ "gpu_memory_gb": 85.1
2331
+ },
2332
+ {
2333
+ "step": 11300,
2334
+ "train_loss": 2.8752,
2335
+ "lr": 0.000301,
2336
+ "tokens_per_second": 921273,
2337
+ "minutes": 55.03,
2338
+ "mfu_percent": 22.2,
2339
+ "gpu_util_percent": 100,
2340
+ "gpu_memory_gb": 85.1
2341
+ },
2342
+ {
2343
+ "step": 11350,
2344
+ "train_loss": 2.9045,
2345
+ "lr": 0.0003,
2346
+ "tokens_per_second": 920772,
2347
+ "minutes": 55.27,
2348
+ "mfu_percent": 22.2,
2349
+ "gpu_util_percent": 100,
2350
+ "gpu_memory_gb": 85.1
2351
+ },
2352
+ {
2353
+ "step": 11400,
2354
+ "train_loss": 2.7934,
2355
+ "lr": 0.0003,
2356
+ "tokens_per_second": 920858,
2357
+ "minutes": 55.51,
2358
+ "mfu_percent": 22.2,
2359
+ "gpu_util_percent": 100,
2360
+ "gpu_memory_gb": 85.1
2361
+ },
2362
+ {
2363
+ "step": 11420,
2364
+ "train_loss": 2.8882,
2365
+ "lr": 0.0003,
2366
+ "tokens_per_second": 921250,
2367
+ "minutes": 55.6,
2368
+ "mfu_percent": 22.2,
2369
+ "gpu_util_percent": 100,
2370
+ "gpu_memory_gb": 85.1,
2371
+ "val_loss": 2.8934
2372
+ }
2373
+ ],
2374
+ "exports": {
2375
+ "parity_prompts": 300,
2376
+ "parity_steps_per_prompt": 5,
2377
+ "float": {
2378
+ "megabytes": 60.81,
2379
+ "same_first_token": 1.0,
2380
+ "same_five_greedy_tokens": 1.0,
2381
+ "cached_step_ms_p50_cpu": 5.544
2382
+ },
2383
+ "int8": {
2384
+ "megabytes": 15.64,
2385
+ "same_first_token": 0.0,
2386
+ "same_five_greedy_tokens": 0.0,
2387
+ "cached_step_ms_p50_cpu": 4.808
2388
+ }
2389
+ }
2390
+ }
v2/experiments/tinynext-v2-3b/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
v2/experiments/tinynext-v2-3b/train.py ADDED
@@ -0,0 +1,691 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.11,<3.14"
3
+ # dependencies = [
4
+ # "torch>=2.6,<2.8",
5
+ # "datasets>=3.0",
6
+ # "numpy<3",
7
+ # "pyarrow>=15",
8
+ # "tokenizers>=0.20",
9
+ # "transformers>=4.46",
10
+ # "onnx>=1.17",
11
+ # "onnxruntime>=1.20",
12
+ # "safetensors>=0.4",
13
+ # "huggingface_hub>=0.30",
14
+ # "nvidia-ml-py>=12",
15
+ # "trackio>=0.2",
16
+ # ]
17
+ # ///
18
+ """tinynext v2: a small word-piece transformer for in-browser autocomplete that can also fill in the middle.
19
+
20
+ One script does everything: it trains a tokenizer, builds a mix of general text, conversation and email,
21
+ trains the model, scores it with eval.py (the same harness as tinynext v1 and SmolLM2), exports ONNX with a
22
+ key/value cache for the browser, and uploads the lot to the model repo under v2/. Training curves, throughput
23
+ and GPU use go to a Trackio dashboard.
24
+
25
+ hf jobs uv run --namespace huggingface --flavor a100-large --secrets HF_TOKEN train.py
26
+ hf jobs uv run --namespace huggingface --flavor a10g-small --secrets HF_TOKEN train.py --smoke --no-push
27
+
28
+ The data is prepared on every CPU core before training starts, and the whole token array then sits in GPU
29
+ memory, so the GPU never waits on Python between steps.
30
+ """
31
+
32
+ import argparse
33
+ import importlib.util
34
+ import json
35
+ import math
36
+ import os
37
+ import random
38
+ import shutil
39
+ import sys
40
+ import time
41
+ from pathlib import Path
42
+
43
+ import numpy as np
44
+ import torch
45
+ import torch.nn as nn
46
+ import torch.nn.functional as F
47
+
48
+ HERE = Path(__file__).parent
49
+ SPECIAL = ["<|endoftext|>", "<|pre|>", "<|suf|>", "<|mid|>"]
50
+ EOT, PRE, SUF, MID = range(4)
51
+
52
+ FINEWEB = "hf://datasets/HuggingFaceFW/fineweb-edu/sample/10BT/{:03d}_00000.parquet"
53
+ SMOLTALK = "hf://datasets/HuggingFaceTB/smoltalk/data/all/train-{:05d}-of-00009.parquet"
54
+ ENRON = "hf://datasets/corbt/enron-emails/data/train-{:05d}-of-00003.parquet"
55
+
56
+
57
+ def cpus() -> int:
58
+ return len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() or 1
59
+
60
+
61
+ # ---- Shared eval code ------------------------------------------------------------------------------------------
62
+
63
+
64
+ def load_eval_module(repo: str, folder: str):
65
+ """Uses eval.py and eval_set.json next to this script, or fetches them from the repo, so every model is scored
66
+ by one implementation on the same text. Registered as a module so worker processes can use it too."""
67
+ path = HERE / "eval.py"
68
+ if not path.exists():
69
+ from huggingface_hub import hf_hub_download
70
+
71
+ for name in ("eval.py", "eval_set.json"):
72
+ shutil.copy(hf_hub_download(repo, f"{folder}/{name}"), HERE / name)
73
+ spec = importlib.util.spec_from_file_location("tinynext_eval", path)
74
+ module = importlib.util.module_from_spec(spec)
75
+ sys.modules["tinynext_eval"] = module
76
+ spec.loader.exec_module(module)
77
+ return module
78
+
79
+
80
+ # ---- Data ------------------------------------------------------------------------------------------------------
81
+
82
+
83
+ def to_ascii(text: str) -> str:
84
+ """The browser model only sees keyboard characters, so it trains on the same."""
85
+ import tinynext_eval as ev
86
+
87
+ return text.translate(ev.SMART_CHARS).encode("ascii", "ignore").decode().strip()
88
+
89
+
90
+ def smoltalk_text(batch):
91
+ return {"text": [to_ascii("\n\n".join(m["content"] for m in turns if m["role"] in ("user", "assistant"))) for turns in batch["messages"]]}
92
+
93
+
94
+ def enron_text(batch):
95
+ """Training senders only: the eval's held-out senders and enron-autocomplete's validation senders are skipped."""
96
+ import hashlib
97
+
98
+ import tinynext_eval as ev
99
+
100
+ texts = []
101
+ for sender, body in zip(batch["from"], batch["body"]):
102
+ sender = (sender or "").lower()
103
+ keep = "@enron.com" in sender and not ev.BULK_SENDER.search(sender)
104
+ keep = keep and int(hashlib.sha1(sender.encode()).hexdigest(), 16) % 100 >= 10
105
+ texts.append(to_ascii("\n\n".join(ev.clean_email(body or ""))) if keep else "")
106
+ return {"text": texts}
107
+
108
+
109
+ # Byte-level BPE with an 8K vocabulary averages a little over 4 characters per token on English. Reading 40% more
110
+ # text than that covers the variance, and anything short gets repeated (and logged) in flatten().
111
+ CHARS_PER_TOKEN = 4.0
112
+ TEXT_MARGIN = 1.4
113
+
114
+
115
+ def enough_rows(ds, budget_tokens: int, lengths: np.ndarray, seed: int):
116
+ """A random subset of rows whose text adds up to roughly the token budget, so only that much gets cleaned."""
117
+ order = np.random.default_rng(seed).permutation(len(ds))
118
+ needed = np.searchsorted(np.cumsum(lengths[order]), budget_tokens * CHARS_PER_TOKEN * TEXT_MARGIN) + 1
119
+ return ds.select(np.sort(order[:needed]))
120
+
121
+
122
+ def load_sources(args, budgets: dict) -> dict:
123
+ """Downloads parquet files, keeps only as many documents as each source's budget needs, and cleans them on
124
+ every core."""
125
+ import pyarrow.compute as pc
126
+ from datasets import load_dataset
127
+
128
+ procs = cpus()
129
+ fineweb = load_dataset("parquet", data_files=[FINEWEB.format(i) for i in range(args.fineweb_files)], split="train")
130
+ fineweb = fineweb.select_columns(["text"])
131
+ fineweb = enough_rows(fineweb, budgets["fineweb"], pc.utf8_length(fineweb.data.column("text")).to_numpy(), seed=0)
132
+ fineweb = fineweb.map(lambda b: {"text": [to_ascii(t) for t in b["text"]]}, batched=True, num_proc=procs)
133
+ smoltalk = load_dataset("parquet", data_files=[SMOLTALK.format(i) for i in range(args.smoltalk_files)], split="train")
134
+ sample = smoltalk.shuffle(seed=0).select(range(min(2000, len(smoltalk))))
135
+ average = np.mean([sum(len(m["content"]) for m in row["messages"]) for row in sample])
136
+ smoltalk = enough_rows(smoltalk, budgets["smoltalk"], np.full(len(smoltalk), average), seed=1)
137
+ smoltalk = smoltalk.map(smoltalk_text, batched=True, num_proc=procs, remove_columns=smoltalk.column_names)
138
+ enron = load_dataset("parquet", data_files=[ENRON.format(i) for i in range(3)], split="train")
139
+ enron = enron.map(enron_text, batched=True, num_proc=procs, remove_columns=enron.column_names)
140
+ sources = {"fineweb": fineweb, "smoltalk": smoltalk, "enron": enron}
141
+ return {name: ds.filter(lambda b: [len(t) >= 40 for t in b["text"]], batched=True, num_proc=procs) for name, ds in sources.items()}
142
+
143
+
144
+ def train_tokenizer(sources: dict, weights: dict, vocab: int, sample_docs: int):
145
+ from tokenizers import Tokenizer, decoders, models, pre_tokenizers, trainers
146
+
147
+ def sample():
148
+ for name, weight in weights.items():
149
+ ds = sources[name].shuffle(seed=0)
150
+ for row in ds.select(range(min(len(ds), int(sample_docs * weight)))):
151
+ yield row["text"]
152
+
153
+ tokenizer = Tokenizer(models.BPE())
154
+ tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
155
+ tokenizer.decoder = decoders.ByteLevel()
156
+ trainer = trainers.BpeTrainer(vocab_size=vocab, special_tokens=SPECIAL, min_frequency=2,
157
+ initial_alphabet=pre_tokenizers.ByteLevel.alphabet())
158
+ tokenizer.train_from_iterator(sample(), trainer=trainer)
159
+ return tokenizer
160
+
161
+
162
+ def fim(ids: list[int], rng: random.Random, max_middle: int, max_suffix: int, max_prefix: int) -> list[int]:
163
+ """Suffix first, then prefix, then the middle, so typing at the cursor only appends to the prompt."""
164
+ if len(ids) < 8:
165
+ return ids + [EOT]
166
+ start = rng.randint(1, len(ids) - 2)
167
+ end = min(len(ids), start + rng.randint(1, max_middle))
168
+ prefix, middle, suffix = ids[max(0, start - max_prefix):start], ids[start:end], ids[end:end + max_suffix]
169
+ if rng.random() < 0.25:
170
+ suffix = [] # also learn the plain "nothing after the cursor" case in this format
171
+ return [SUF, *suffix, PRE, *prefix, MID, *middle, EOT]
172
+
173
+
174
+ def make_encoder(tokenizer_json: str, seq: int, fim_rate: float):
175
+ """Tokenizes a batch and turns each document into training pieces: plain chunks, or fill-in-the-middle examples."""
176
+
177
+ def encode(batch, indices):
178
+ from tokenizers import Tokenizer
179
+
180
+ tokenizer = Tokenizer.from_str(tokenizer_json)
181
+ rng = random.Random(indices[0])
182
+ out = []
183
+ for encoding in tokenizer.encode_batch(batch["text"]):
184
+ ids, pieces = encoding.ids, []
185
+ for at in range(0, len(ids), seq - 8):
186
+ chunk = ids[at: at + seq - 8]
187
+ pieces += fim(chunk, rng, 24, 64, seq - 100) if rng.random() < fim_rate else chunk + [EOT]
188
+ out.append(pieces)
189
+ return {"tokens": out}
190
+
191
+ return encode
192
+
193
+
194
+ def flatten(ds, budget: int) -> np.ndarray:
195
+ """All of a source's tokens as one array, cut to its share of the budget (repeated if the source is small)."""
196
+ import pyarrow.compute as pc
197
+
198
+ column = ds.data.column("tokens")
199
+ tokens = pc.list_flatten(column).to_numpy().astype(np.uint16)
200
+ if len(tokens) < budget:
201
+ print(f" only {len(tokens) / 1e6:.0f}M tokens, repeating to reach {budget / 1e6:.0f}M", flush=True)
202
+ tokens = np.tile(tokens, math.ceil(budget / len(tokens)))
203
+ return tokens[:budget]
204
+
205
+
206
+ def build_rows(sources: dict, tokenizer, weights: dict, args) -> tuple[np.ndarray, np.ndarray, dict]:
207
+ encode = make_encoder(tokenizer.to_str(), args.seq, args.fim_rate)
208
+ train_parts, val_parts, counts = [], [], {}
209
+ for name, weight in weights.items():
210
+ started = time.time()
211
+ split = sources[name].train_test_split(test_size=0.002, seed=0)
212
+ encoded = {part: split[part].map(encode, batched=True, batch_size=512, with_indices=True, num_proc=cpus(),
213
+ remove_columns=["text"]) for part in ("train", "test")}
214
+ budget = int(args.tokens * weight)
215
+ train_parts.append(flatten(encoded["train"], budget))
216
+ val_parts.append(flatten(encoded["test"], int(args.val_tokens * weight)))
217
+ counts[name] = {"docs": len(split["train"]), "train_tokens": int(len(train_parts[-1]))}
218
+ print(f" {name}: {len(split['train']):,} docs → {len(train_parts[-1]) / 1e6:.0f}M tokens in {time.time() - started:.0f}s", flush=True)
219
+
220
+ def rows(parts):
221
+ tokens = np.concatenate(parts)
222
+ count = len(tokens) // (args.seq + 1)
223
+ return tokens[: count * (args.seq + 1)].reshape(count, args.seq + 1)
224
+
225
+ return rows(train_parts), rows(val_parts), counts
226
+
227
+
228
+ # ---- Model -----------------------------------------------------------------------------------------------------
229
+
230
+
231
+ class RMSNorm(nn.Module):
232
+ def __init__(self, dim: int):
233
+ super().__init__()
234
+ self.weight = nn.Parameter(torch.ones(dim))
235
+
236
+ def forward(self, x):
237
+ return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + 1e-6) * self.weight
238
+
239
+
240
+ def rotate(x, cos, sin):
241
+ half = x.shape[-1] // 2
242
+ x1, x2 = x[..., :half], x[..., half:]
243
+ return torch.cat([x1 * cos - x2 * sin, x2 * cos + x1 * sin], dim=-1)
244
+
245
+
246
+ class Block(nn.Module):
247
+ def __init__(self, dim: int, heads: int, ffn: int):
248
+ super().__init__()
249
+ self.heads, self.head_dim = heads, dim // heads
250
+ self.norm1, self.norm2 = RMSNorm(dim), RMSNorm(dim)
251
+ self.qkv = nn.Linear(dim, 3 * dim, bias=False)
252
+ self.out = nn.Linear(dim, dim, bias=False)
253
+ self.gate_up = nn.Linear(dim, 2 * ffn, bias=False)
254
+ self.down = nn.Linear(ffn, dim, bias=False)
255
+
256
+ def forward(self, x, cos, sin, past_k, past_v, mask):
257
+ batch, length, _ = x.shape
258
+ q, k, v = self.qkv(self.norm1(x)).view(batch, length, 3, self.heads, self.head_dim).unbind(2)
259
+ q, k, v = rotate(q.transpose(1, 2), cos, sin), rotate(k.transpose(1, 2), cos, sin), v.transpose(1, 2)
260
+ if past_k is not None:
261
+ k, v = torch.cat([past_k, k], dim=2), torch.cat([past_v, v], dim=2)
262
+ if mask is None:
263
+ attended = F.scaled_dot_product_attention(q, k, v, is_causal=True)
264
+ else:
265
+ attended = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
266
+ x = x + self.out(attended.transpose(1, 2).reshape(batch, length, -1))
267
+ gate, up = self.gate_up(self.norm2(x)).chunk(2, dim=-1)
268
+ return x + self.down(F.silu(gate) * up), k, v
269
+
270
+
271
+ class TinyNext(nn.Module):
272
+ def __init__(self, vocab: int, dim: int, layers: int, heads: int, ffn: int):
273
+ super().__init__()
274
+ self.config = {"vocab": vocab, "dim": dim, "layers": layers, "heads": heads, "ffn": ffn}
275
+ self.embed = nn.Embedding(vocab, dim)
276
+ self.blocks = nn.ModuleList(Block(dim, heads, ffn) for _ in range(layers))
277
+ self.norm = RMSNorm(dim)
278
+ self.head = None # set only for export: an untied copy of the output weights, so int8 quantization covers it
279
+ self.head_dim = dim // heads
280
+ inverse = 1.0 / (10000 ** (torch.arange(0, self.head_dim, 2).float() / self.head_dim))
281
+ self.register_buffer("inverse_frequency", inverse, persistent=False)
282
+ nn.init.normal_(self.embed.weight, std=0.02)
283
+
284
+ def forward(self, ids, past_k=None, past_v=None):
285
+ """ids: [batch, length]. past_k/past_v: [layers, batch, heads, past, head_dim] or None.
286
+ Returns logits for every position and the updated cache."""
287
+ past = 0 if past_k is None else past_k.shape[3]
288
+ length = ids.shape[1]
289
+ positions = torch.arange(length, device=ids.device) + past
290
+ angles = positions[:, None].float() * self.inverse_frequency[None, :]
291
+ cos, sin = angles.cos()[None, None], angles.sin()[None, None]
292
+ mask = None
293
+ if past_k is not None:
294
+ keys = torch.arange(past + length, device=ids.device)
295
+ mask = keys[None, :] <= positions[:, None]
296
+ x = self.embed(ids)
297
+ new_k, new_v = [], []
298
+ for index, block in enumerate(self.blocks):
299
+ x, k, v = block(x, cos, sin, None if past_k is None else past_k[index], None if past_v is None else past_v[index], mask)
300
+ new_k.append(k)
301
+ new_v.append(v)
302
+ logits = self.norm(x) @ (self.embed.weight.T if self.head is None else self.head)
303
+ return logits, torch.stack(new_k), torch.stack(new_v)
304
+
305
+
306
+ class BrowserStep(nn.Module):
307
+ """The ONNX graph: always takes a cache (possibly empty) and returns only the last position's logits."""
308
+
309
+ def __init__(self, model: TinyNext):
310
+ super().__init__()
311
+ self.model = model
312
+
313
+ def forward(self, ids, past_k, past_v):
314
+ logits, k, v = self.model(ids, past_k, past_v)
315
+ return logits[:, -1], k, v
316
+
317
+
318
+ # ---- Training --------------------------------------------------------------------------------------------------
319
+
320
+
321
+ class GpuMonitor:
322
+ """Reads GPU utilization and memory through NVML, so the logs show whether the GPU is actually busy."""
323
+
324
+ def __init__(self):
325
+ try:
326
+ import pynvml
327
+
328
+ pynvml.nvmlInit()
329
+ self.nvml, self.handle = pynvml, pynvml.nvmlDeviceGetHandleByIndex(0)
330
+ except Exception as error: # no NVIDIA GPU (a laptop smoke run): report nothing rather than fail
331
+ print(f"GPU monitoring off: {error}", flush=True)
332
+ self.nvml = None
333
+
334
+ def read(self) -> dict:
335
+ if not self.nvml:
336
+ return {}
337
+ rates = self.nvml.nvmlDeviceGetUtilizationRates(self.handle)
338
+ memory = self.nvml.nvmlDeviceGetMemoryInfo(self.handle)
339
+ return {"gpu_util_percent": rates.gpu, "gpu_memory_gb": round(memory.used / 1e9, 1)}
340
+
341
+
342
+ def evaluate_loss(model, rows: torch.Tensor, device, batch: int) -> float:
343
+ model.eval()
344
+ losses = []
345
+ with torch.no_grad(), torch.autocast(device.type, dtype=torch.bfloat16, enabled=device.type == "cuda"):
346
+ for at in range(0, len(rows), batch):
347
+ chunk = rows[at: at + batch].long()
348
+ logits, _, _ = model(chunk[:, :-1])
349
+ losses.append(F.cross_entropy(logits.float().flatten(0, 1), chunk[:, 1:].flatten()).item())
350
+ model.train()
351
+ return float(np.mean(losses))
352
+
353
+
354
+ def train(model, rows: torch.Tensor, val_rows: torch.Tensor, args, device, tracker) -> list[dict]:
355
+ steps = int(args.epochs * len(rows) // args.batch)
356
+ decay = [p for p in model.parameters() if p.dim() >= 2]
357
+ no_decay = [p for p in model.parameters() if p.dim() < 2]
358
+ optimizer = torch.optim.AdamW([{"params": decay, "weight_decay": 0.1}, {"params": no_decay, "weight_decay": 0.0}],
359
+ lr=args.lr, betas=(0.9, 0.95), fused=device.type == "cuda")
360
+ warmup = max(1, steps // 50)
361
+ schedule = lambda step: min(1, step / warmup) * (0.1 + 0.9 * 0.5 * (1 + math.cos(math.pi * min(1, step / steps))))
362
+ scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, schedule)
363
+ compiled = torch.compile(model) if device.type == "cuda" else model
364
+ parameters = sum(p.numel() for p in model.parameters())
365
+ gpu, history = GpuMonitor(), []
366
+ generator = torch.Generator(device=device).manual_seed(0)
367
+ window_started, window_tokens, started = time.time(), 0, time.time()
368
+ print(f"training {steps:,} steps of {args.batch * args.seq:,} tokens", flush=True)
369
+ for step in range(steps):
370
+ index = torch.randint(0, len(rows), (args.batch,), device=device, generator=generator)
371
+ chunk = rows[index].long()
372
+ with torch.autocast(device.type, dtype=torch.bfloat16, enabled=device.type == "cuda"):
373
+ logits, _, _ = compiled(chunk[:, :-1])
374
+ loss = F.cross_entropy(logits.float().flatten(0, 1), chunk[:, 1:].flatten())
375
+ optimizer.zero_grad(set_to_none=True)
376
+ loss.backward()
377
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
378
+ optimizer.step()
379
+ scheduler.step()
380
+ window_tokens += args.batch * args.seq
381
+ if step % args.log_every == 0 or step == steps - 1:
382
+ loss_value = loss.item() # syncs the GPU, so throughput below is real
383
+ seconds = time.time() - window_started
384
+ tokens_per_second = window_tokens / max(seconds, 1e-9)
385
+ entry = {
386
+ "step": step, "train_loss": round(loss_value, 4), "lr": round(scheduler.get_last_lr()[0], 6),
387
+ "tokens_per_second": round(tokens_per_second), "minutes": round((time.time() - started) / 60, 2),
388
+ # Training compute is about 6 FLOPs per parameter per token; an A100 does 312 TFLOP/s in bf16.
389
+ "mfu_percent": round(100 * 6 * parameters * tokens_per_second / 312e12, 1), **gpu.read(),
390
+ }
391
+ if step % (args.log_every * 10) == 0 or step == steps - 1:
392
+ entry["val_loss"] = round(evaluate_loss(model, val_rows, device, args.batch), 4)
393
+ history.append(entry)
394
+ tracker.log(entry, step=step)
395
+ print(json.dumps(entry), flush=True)
396
+ window_started, window_tokens = time.time(), 0
397
+ return history
398
+
399
+
400
+ # ---- Eval, export --------------------------------------------------------------------------------------------
401
+
402
+
403
+ def as_token_lm(ev, name: str, model: TinyNext, tokenizer, device, fim: bool = True):
404
+ texts = [tokenizer.decode([i], skip_special_tokens=False) if i >= len(SPECIAL) else "" for i in range(tokenizer.get_vocab_size())]
405
+
406
+ @torch.no_grad()
407
+ def next_logits(ids, cache):
408
+ past_k, past_v = cache if cache else (None, None)
409
+ logits, k, v = model(torch.tensor([ids], device=device), past_k, past_v)
410
+ return logits[0, -1].float().cpu().numpy(), (k, v)
411
+
412
+ encode = lambda text: tokenizer.encode(text).ids or [EOT]
413
+ # A model trained without fill-in-the-middle never saw the special tokens, so it only gets the text before.
414
+ prompt = (lambda prefix, suffix: prefix if not suffix else [SUF, *suffix, PRE, *prefix, MID]) if fim else None
415
+ return ev.TokenLM(name, next_logits, encode, texts, set(range(len(SPECIAL))), prompt=prompt)
416
+
417
+
418
+ def parity_prompts(ev, tokenizer, count: int = 300) -> list[list[int]]:
419
+ """Real text from the eval set, cut at random points, as token prompts."""
420
+ rng = random.Random(0)
421
+ texts = [p for domain in ev.load_eval_set().values() for doc in domain for p in doc]
422
+ prompts = []
423
+ for text in rng.sample(texts, min(count, len(texts))):
424
+ ids = tokenizer.encode(text[: rng.randint(40, len(text))]).ids[-300:]
425
+ if len(ids) >= 4:
426
+ prompts.append(ids)
427
+ return prompts
428
+
429
+
430
+ def greedy_tokens(step, ids: list[int], empty, steps: int) -> list[int]:
431
+ """Prompt, then `steps` cached greedy steps; returns the top token at each point."""
432
+ logits, k, v = step(np.array([ids]), empty, empty)
433
+ tops = [int(logits.argmax())]
434
+ for _ in range(steps):
435
+ logits, k, v = step(np.array([[tops[-1]]]), k, v)
436
+ tops.append(int(logits.argmax()))
437
+ return tops
438
+
439
+
440
+ def export(model: TinyNext, output: Path, device, prompts: list[list[int]]) -> dict:
441
+ """Float and int8 ONNX for the browser, checked against PyTorch on real prompts over several cached steps."""
442
+ import onnxruntime as ort
443
+ from onnxruntime.quantization import QuantType, quantize_dynamic
444
+
445
+ model = model.float().eval().to("cpu")
446
+ model.head = nn.Parameter(model.embed.weight.detach().T.contiguous().clone())
447
+ cfg = model.config
448
+ head_dim = cfg["dim"] // cfg["heads"]
449
+ empty = np.zeros((cfg["layers"], 1, cfg["heads"], 0, head_dim), np.float32)
450
+ # Trace with a non-empty cache so no shape gets fixed at zero; the checks below start from an empty one.
451
+ with torch.no_grad():
452
+ _, trace_k, trace_v = model(torch.tensor([[1, 2]]))
453
+ float_path, int8_path = output / "model.onnx", output / "model_int8.onnx"
454
+ torch.onnx.export(
455
+ BrowserStep(model), (torch.tensor([[5, 6, 7]]), trace_k, trace_v), float_path, opset_version=17,
456
+ input_names=["input_ids", "past_k", "past_v"], output_names=["logits", "present_k", "present_v"],
457
+ dynamic_axes={"input_ids": {1: "length"}, "past_k": {3: "past"}, "past_v": {3: "past"},
458
+ "present_k": {3: "total"}, "present_v": {3: "total"}},
459
+ dynamo=False,
460
+ )
461
+ quantize_dynamic(float_path, int8_path, weight_type=QuantType.QInt8, op_types_to_quantize=["MatMul", "Gather"], per_channel=True)
462
+
463
+ @torch.no_grad()
464
+ def torch_step(ids, k, v):
465
+ past = None if k.shape[3] == 0 else (torch.from_numpy(k), torch.from_numpy(v))
466
+ logits, nk, nv = model(torch.from_numpy(ids), *(past or (None, None)))
467
+ return logits[0, -1].numpy(), nk.numpy(), nv.numpy()
468
+
469
+ expected = [greedy_tokens(torch_step, ids, empty, 4) for ids in prompts]
470
+ report = {"parity_prompts": len(prompts), "parity_steps_per_prompt": 5}
471
+ for name, path in (("float", float_path), ("int8", int8_path)):
472
+ session = ort.InferenceSession(str(path), providers=["CPUExecutionProvider"])
473
+ onnx_step = lambda ids, k, v: (lambda out: (out[0][0], out[1], out[2]))(
474
+ session.run(None, {"input_ids": ids.astype(np.int64), "past_k": k, "past_v": v}))
475
+ got = [greedy_tokens(onnx_step, ids, empty, 4) for ids in prompts]
476
+ first = np.mean([g[0] == e[0] for g, e in zip(got, expected)])
477
+ all_steps = np.mean([g == e for g, e in zip(got, expected)])
478
+ _, pk, pv = onnx_step(np.array([prompts[0]]), empty, empty)
479
+ timings = []
480
+ for _ in range(200):
481
+ began = time.perf_counter()
482
+ onnx_step(np.array([[8]]), pk, pv)
483
+ timings.append((time.perf_counter() - began) * 1000)
484
+ report[name] = {
485
+ "megabytes": round(path.stat().st_size / 1e6, 2),
486
+ "same_first_token": round(float(first), 4),
487
+ "same_five_greedy_tokens": round(float(all_steps), 4),
488
+ "cached_step_ms_p50_cpu": round(float(np.median(timings)), 3),
489
+ }
490
+ model.head = None
491
+ model.to(device)
492
+ return report
493
+
494
+
495
+ def headline(scored: dict) -> dict:
496
+ """The few eval numbers worth a chart: typing saved and 2+ word suggestions at the app's cutoff."""
497
+ table = scored["cutoffs"]["0.5"]
498
+ out = {}
499
+ for key, m in table.items():
500
+ slug = key.replace("/", "_")
501
+ out[f"eval/{slug}/saved_with_tab"] = m["saved_with_tab"]
502
+ out[f"eval/{slug}/right"] = m["right"]
503
+ out[f"eval/{slug}/multiword_shown"] = m["multiword_shown"]
504
+ return out
505
+
506
+
507
+ # ---- Prepared data ---------------------------------------------------------------------------------------------
508
+
509
+
510
+ def data_key(args) -> str:
511
+ """Prepared data depends only on these settings, so a training run can find the matching files."""
512
+ return f"{args.name}-v{args.vocab}-s{args.seq}-t{int(args.tokens / 1e6)}M-fim{args.fim_rate}-{args.mix}".replace(",", "_").replace("=", "")
513
+
514
+
515
+ def prepare(args, weights: dict, output: Path):
516
+ budgets = {name: int((args.tokens + args.val_tokens) * weight) for name, weight in weights.items()}
517
+ started = time.time()
518
+ sources = load_sources(args, budgets)
519
+ print(f"sources ready in {time.time() - started:.0f}s: " + ", ".join(f"{n} {len(d):,} docs" for n, d in sources.items()), flush=True)
520
+ tokenizer = train_tokenizer(sources, weights, args.vocab, args.tokenizer_docs)
521
+ tokenizer.save(str(output / "tokenizer.json"))
522
+ rows, val_rows, counts = build_rows(sources, tokenizer, weights, args)
523
+ return tokenizer, rows, val_rows, counts
524
+
525
+
526
+ def upload_prepared(args, output: Path, rows: np.ndarray, val_rows: np.ndarray, counts: dict, minutes: float):
527
+ from huggingface_hub import HfApi
528
+
529
+ folder = output / "data"
530
+ folder.mkdir(exist_ok=True)
531
+ rows.astype(np.uint16).tofile(folder / "train.bin")
532
+ val_rows.astype(np.uint16).tofile(folder / "val.bin")
533
+ shutil.copy(output / "tokenizer.json", folder / "tokenizer.json")
534
+ meta = {"seq": args.seq, "train_rows": len(rows), "val_rows": len(val_rows), "counts": counts, "minutes": round(minutes, 1), "args": vars(args)}
535
+ (folder / "meta.json").write_text(json.dumps(meta, indent=1))
536
+ api = HfApi(token=os.environ.get("HF_TOKEN"))
537
+ api.create_repo(args.data_repo, repo_type="dataset", private=True, exist_ok=True)
538
+ api.upload_folder(folder_path=str(folder), repo_id=args.data_repo, repo_type="dataset", path_in_repo=data_key(args),
539
+ commit_message=f"prepared data: {data_key(args)}")
540
+ print(f"Prepared data at https://huggingface.co/datasets/{args.data_repo}/tree/main/{data_key(args)}", flush=True)
541
+
542
+
543
+ def download_prepared(args, output: Path):
544
+ from huggingface_hub import snapshot_download
545
+ from tokenizers import Tokenizer
546
+
547
+ local = Path(snapshot_download(args.data_repo, repo_type="dataset", allow_patterns=[f"{data_key(args)}/*"], max_workers=16))
548
+ folder = local / data_key(args)
549
+ meta = json.loads((folder / "meta.json").read_text())
550
+ shutil.copy(folder / "tokenizer.json", output / "tokenizer.json")
551
+ rows = np.fromfile(folder / "train.bin", dtype=np.uint16).reshape(meta["train_rows"], meta["seq"] + 1)
552
+ val_rows = np.fromfile(folder / "val.bin", dtype=np.uint16).reshape(meta["val_rows"], meta["seq"] + 1)
553
+ return Tokenizer.from_file(str(output / "tokenizer.json")), rows, val_rows, meta["counts"]
554
+
555
+
556
+ # ---- Main ------------------------------------------------------------------------------------------------------
557
+
558
+
559
+ def parse_args():
560
+ parser = argparse.ArgumentParser()
561
+ parser.add_argument("--vocab", type=int, default=8192)
562
+ parser.add_argument("--dim", type=int, default=320)
563
+ parser.add_argument("--layers", type=int, default=8)
564
+ parser.add_argument("--heads", type=int, default=5)
565
+ parser.add_argument("--ffn", type=int, default=864)
566
+ parser.add_argument("--seq", type=int, default=512)
567
+ parser.add_argument("--tokens", type=float, default=1.5e9, help="Training tokens")
568
+ parser.add_argument("--val-tokens", type=float, default=4e6)
569
+ parser.add_argument("--epochs", type=float, default=1.0)
570
+ parser.add_argument("--batch", type=int, default=512)
571
+ parser.add_argument("--lr", type=float, default=3e-3)
572
+ parser.add_argument("--fim-rate", type=float, default=0.5)
573
+ parser.add_argument("--mix", default="fineweb=0.6,smoltalk=0.3,enron=0.1")
574
+ parser.add_argument("--fineweb-files", type=int, default=2, help="Each is about 2 GB of text, roughly 0.9B tokens")
575
+ parser.add_argument("--smoltalk-files", type=int, default=9)
576
+ parser.add_argument("--tokenizer-docs", type=int, default=200_000)
577
+ parser.add_argument("--eval-pauses", type=int, default=1000)
578
+ parser.add_argument("--log-every", type=int, default=50)
579
+ parser.add_argument("--repo", default="cfahlgren1/tinynext")
580
+ parser.add_argument("--folder", default="v2", help="Where this run's outputs are uploaded")
581
+ parser.add_argument("--eval-folder", default="v2", help="Where the shared eval.py and eval_set.json live")
582
+ parser.add_argument("--name", default="tinynext-v2")
583
+ parser.add_argument("--trackio-space", default="cfahlgren1/tinynext-trackio")
584
+ parser.add_argument("--stage", choices=("all", "prepare", "train"), default="all",
585
+ help="prepare: build the tokenizer and token arrays on a CPU job and save them to --data-repo. "
586
+ "train: download them and train on the GPU. all: both in one job.")
587
+ parser.add_argument("--data-repo", default="cfahlgren1/tinynext-data")
588
+ parser.add_argument("--smoke", action="store_true", help="Tiny settings to check the pipeline end to end")
589
+ parser.add_argument("--no-push", action="store_true")
590
+ args = parser.parse_args()
591
+ if args.smoke:
592
+ args.vocab, args.dim, args.layers, args.heads, args.ffn, args.seq = 2048, 64, 2, 2, 128, 256
593
+ args.tokens, args.val_tokens, args.batch, args.tokenizer_docs = 5e6, 2e5, 64, 5000
594
+ args.fineweb_files, args.smoltalk_files, args.eval_pauses, args.log_every = 1, 1, 20, 10
595
+ args.name = "tinynext-v2-smoke"
596
+ return args
597
+
598
+
599
+ class NoTracker:
600
+ def log(self, *_, **__):
601
+ pass
602
+
603
+ def finish(self):
604
+ pass
605
+
606
+
607
+ def start_tracker(args):
608
+ if args.no_push and not os.environ.get("HF_TOKEN"):
609
+ return NoTracker()
610
+ import trackio
611
+
612
+ trackio.init(project="tinynext", name=args.name, space_id=args.trackio_space, config=vars(args))
613
+ return trackio
614
+
615
+
616
+ def main():
617
+ args = parse_args()
618
+ device = torch.device("cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu")
619
+ torch.manual_seed(0)
620
+ torch.backends.cuda.matmul.allow_tf32 = True
621
+ torch.backends.cudnn.allow_tf32 = True
622
+ output = HERE / "output"
623
+ output.mkdir(exist_ok=True)
624
+ ev = load_eval_module(args.repo, args.eval_folder)
625
+ weights = {k: float(v) for k, v in (part.split("=") for part in args.mix.split(","))}
626
+ print(f"device {device}, {cpus()} CPUs, mix {weights}", flush=True)
627
+ tracker = start_tracker(args)
628
+
629
+ started = time.time()
630
+ if args.stage == "train":
631
+ tokenizer, rows, val_rows, counts = download_prepared(args, output)
632
+ else:
633
+ tokenizer, rows, val_rows, counts = prepare(args, weights, output)
634
+ data_minutes = (time.time() - started) / 60
635
+ print(f"data ready in {data_minutes:.1f} min: {rows.size / 1e6:.0f}M train tokens, {val_rows.size / 1e6:.1f}M val", flush=True)
636
+ if args.stage == "prepare":
637
+ upload_prepared(args, output, rows, val_rows, counts, data_minutes)
638
+ return
639
+
640
+ # The whole token array lives on the GPU; each step just indexes into it.
641
+ rows_gpu = torch.from_numpy(rows.astype(np.int32)).to(device)
642
+ val_gpu = torch.from_numpy(val_rows.astype(np.int32)).to(device)
643
+ model = TinyNext(tokenizer.get_vocab_size(), args.dim, args.layers, args.heads, args.ffn).to(device)
644
+ parameters = sum(p.numel() for p in model.parameters())
645
+ print(f"{parameters / 1e6:.2f}M parameters", flush=True)
646
+ started = time.time()
647
+ history = train(model, rows_gpu, val_gpu, args, device, tracker)
648
+ train_minutes = (time.time() - started) / 60
649
+
650
+ model.eval()
651
+ from safetensors.torch import save_file
652
+
653
+ save_file({k: v.contiguous() for k, v in model.state_dict().items()}, output / "model.safetensors")
654
+ scored = ev.score(as_token_lm(ev, args.name, model, tokenizer, device, fim=args.fim_rate > 0), ev.pauses(ev.load_eval_set(), args.eval_pauses))
655
+ (output / "metrics").mkdir(exist_ok=True)
656
+ (output / "metrics" / f"{args.name}.json").write_text(json.dumps(scored, indent=1))
657
+ tracker.log(headline(scored))
658
+ exported = export(model, output, device, parity_prompts(ev, tokenizer))
659
+
660
+ config = {
661
+ **model.config, "parameters": parameters, "seq": args.seq, "special_tokens": dict(zip(SPECIAL, range(len(SPECIAL)))),
662
+ "prompt": "No text after the cursor: the text before it. Otherwise: <|suf|> after <|pre|> before <|mid|>.",
663
+ }
664
+ (output / "config.json").write_text(json.dumps(config, indent=1))
665
+ report = {"args": vars(args), "parameters": parameters, "data": counts, "data_minutes": round(data_minutes, 1),
666
+ "train_minutes": round(train_minutes, 1), "history": history, "exports": exported}
667
+ (output / "report.json").write_text(json.dumps(report, indent=1))
668
+ print(json.dumps(exported, indent=1))
669
+ print(ev.table([output / "metrics" / f"{args.name}.json"]))
670
+ tracker.finish()
671
+
672
+ for name in ("train.py", "eval.py", "eval_set.json"):
673
+ shutil.copy(HERE / name, output / name)
674
+ if (HERE / "metrics").exists():
675
+ for path in (HERE / "metrics").glob("*.json"):
676
+ if not (output / "metrics" / path.name).exists():
677
+ shutil.copy(path, output / "metrics" / path.name)
678
+ if args.no_push:
679
+ print(f"Skipping upload; everything is in {output}")
680
+ return
681
+ from huggingface_hub import HfApi
682
+
683
+ HfApi(token=os.environ.get("HF_TOKEN")).upload_folder(
684
+ folder_path=str(output), repo_id=args.repo, path_in_repo=args.folder, repo_type="model",
685
+ commit_message=f"{args.name}: {parameters / 1e6:.1f}M params, {train_minutes:.0f} min on GPU",
686
+ )
687
+ print(f"Uploaded to https://huggingface.co/{args.repo}/tree/main/{args.folder}")
688
+
689
+
690
+ if __name__ == "__main__":
691
+ main()