Cion-lab commited on
Commit
ef0c95d
·
verified ·
1 Parent(s): 398d16e

perm_sha: identify the visit order by hashing the permutation, not just its seed (E-035 residual) -- cursor gains shuffle_perm_sha, asserted on resume; session repins the two files

Browse files
Files changed (1) hide show
  1. train/train_ounce100m.py +13 -6
train/train_ounce100m.py CHANGED
@@ -125,10 +125,11 @@ class HubPush(TrainerCallback):
125
  """
126
 
127
  def __init__(self, repo, api, token, seq_len, seed, fingerprint, seqs_per_step,
128
- prune=True, min_free_gb=6.0, repo_type="dataset", stop_after_steps=0):
129
  self.repo, self.api, self.token = repo, api, token
130
  self.seq_len, self.seed, self.fingerprint = seq_len, seed, fingerprint
131
  self.seqs_per_step, self.repo_type = seqs_per_step, repo_type
 
132
  self.prune, self.min_free_gb = prune, min_free_gb
133
  self.stop_after = int(stop_after_steps or 0)
134
  self.stopped_at = None
@@ -155,7 +156,7 @@ class HubPush(TrainerCallback):
155
  raise RuntimeError(f"Trainer reported a save but {d} does not exist")
156
  samples = samples_at(step, self.seqs_per_step)
157
  SD.save_cursor(d, SD.cursor_dict(self.seq_len, samples, self.seed, self.fingerprint,
158
- step=step))
159
  t0 = time.time()
160
  # Upload and verify *without* pruning, roll the pointer and read it back, and only then delete the
161
  # local copy. The old order (prune inside push_and_prune, pointer written afterwards) meant one
@@ -409,9 +410,13 @@ def main():
409
  say(f"resume from {args.resume} cursor={cur}")
410
  start_sample = (cur or {}).get("samples_consumed", 0)
411
  start_step = (cur or {}).get("step") or 0
 
 
 
 
412
  if cur:
413
  for k, v in (("seq_len", args.seq_len), ("shuffle_seed", args.data_seed),
414
- ("dataset_files_sha", fingerprint)):
415
  if cur.get(k) != v:
416
  raise SystemExit(f"resume refused: cursor {k}={cur.get(k)} != current {v}. "
417
  "A resumed run must read the same data in the same order (§3.1).")
@@ -425,6 +430,7 @@ def main():
425
  start_step = start_sample // seqs_per_step
426
 
427
  train_ds = SD.make_dataset(store, args.seq_len, args.data_seed, start_sample=start_sample)
 
428
  # Validation comes from the mix's held-out val/ shards, never from the training stream: scoring on
429
  # tokens the model already consumed would be a number nobody could reproduce (and §3.12).
430
  vs = [s["file"] for s in man.get("val_shards", [])]
@@ -546,7 +552,7 @@ def main():
546
  hub_cb = HubPush(args.hub_repo, api, os.environ.get("HF_TOKEN"), args.seq_len,
547
  args.data_seed, fingerprint, seqs_per_step,
548
  prune=args.prune, repo_type=args.hub_repo_type,
549
- stop_after_steps=args.stop_after_steps)
550
  tr.add_callback(hub_cb)
551
 
552
  # Precision must be ASSERTED, not configured: bf16 "works" silently on Turing and fp32 master
@@ -637,10 +643,11 @@ def main():
637
  shipped.append(fn)
638
  say(f"tokenizer files shipped with the export: {shipped or 'NONE -- Phase 5 must add them'}")
639
  SD.save_cursor(exp, SD.cursor_dict(args.seq_len, samples, args.data_seed, fingerprint,
640
- step=step))
641
  json.dump({"step": step, "start_step": start_step, "tokens_consumed": consumed,
642
  "tokens_this_run": consumed_this_run, "seconds": round(elapsed, 1),
643
- "params": pc, "fingerprint": fingerprint, "final_loss": final_loss,
 
644
  "prior_loss": prior, "samples_consumed": samples,
645
  "steps_planned": steps_planned},
646
  open(os.path.join(exp, "run_summary.json"), "w"), indent=1, sort_keys=True)
 
125
  """
126
 
127
  def __init__(self, repo, api, token, seq_len, seed, fingerprint, seqs_per_step,
128
+ prune=True, min_free_gb=6.0, repo_type="dataset", stop_after_steps=0, order_sha=None):
129
  self.repo, self.api, self.token = repo, api, token
130
  self.seq_len, self.seed, self.fingerprint = seq_len, seed, fingerprint
131
  self.seqs_per_step, self.repo_type = seqs_per_step, repo_type
132
+ self.order_sha = order_sha
133
  self.prune, self.min_free_gb = prune, min_free_gb
134
  self.stop_after = int(stop_after_steps or 0)
135
  self.stopped_at = None
 
156
  raise RuntimeError(f"Trainer reported a save but {d} does not exist")
157
  samples = samples_at(step, self.seqs_per_step)
158
  SD.save_cursor(d, SD.cursor_dict(self.seq_len, samples, self.seed, self.fingerprint,
159
+ step=step, order_sha=self.order_sha))
160
  t0 = time.time()
161
  # Upload and verify *without* pruning, roll the pointer and read it back, and only then delete the
162
  # local copy. The old order (prune inside push_and_prune, pointer written afterwards) meant one
 
410
  say(f"resume from {args.resume} cursor={cur}")
411
  start_sample = (cur or {}).get("samples_consumed", 0)
412
  start_step = (cur or {}).get("step") or 0
413
+ # Identity of the visit order, derived from the corpus size and the seed rather than trusted to the
414
+ # seed: numpy's permutation is only stable within a version, and Kaggle updates its image underneath
415
+ # long runs. ~50 ms once per session, and it is what makes "resumed onto a different order" loud.
416
+ order_sha = SD.perm_sha(store.total_tokens // args.seq_len, args.data_seed)
417
  if cur:
418
  for k, v in (("seq_len", args.seq_len), ("shuffle_seed", args.data_seed),
419
+ ("shuffle_perm_sha", order_sha), ("dataset_files_sha", fingerprint)):
420
  if cur.get(k) != v:
421
  raise SystemExit(f"resume refused: cursor {k}={cur.get(k)} != current {v}. "
422
  "A resumed run must read the same data in the same order (§3.1).")
 
430
  start_step = start_sample // seqs_per_step
431
 
432
  train_ds = SD.make_dataset(store, args.seq_len, args.data_seed, start_sample=start_sample)
433
+ assert getattr(train_ds, "order_sha", None) == order_sha, "two derivations of the order disagree"
434
  # Validation comes from the mix's held-out val/ shards, never from the training stream: scoring on
435
  # tokens the model already consumed would be a number nobody could reproduce (and §3.12).
436
  vs = [s["file"] for s in man.get("val_shards", [])]
 
552
  hub_cb = HubPush(args.hub_repo, api, os.environ.get("HF_TOKEN"), args.seq_len,
553
  args.data_seed, fingerprint, seqs_per_step,
554
  prune=args.prune, repo_type=args.hub_repo_type,
555
+ stop_after_steps=args.stop_after_steps, order_sha=order_sha)
556
  tr.add_callback(hub_cb)
557
 
558
  # Precision must be ASSERTED, not configured: bf16 "works" silently on Turing and fp32 master
 
643
  shipped.append(fn)
644
  say(f"tokenizer files shipped with the export: {shipped or 'NONE -- Phase 5 must add them'}")
645
  SD.save_cursor(exp, SD.cursor_dict(args.seq_len, samples, args.data_seed, fingerprint,
646
+ step=step, order_sha=order_sha))
647
  json.dump({"step": step, "start_step": start_step, "tokens_consumed": consumed,
648
  "tokens_this_run": consumed_this_run, "seconds": round(elapsed, 1),
649
+ "params": pc, "fingerprint": fingerprint, "shuffle_perm_sha": order_sha,
650
+ "final_loss": final_loss,
651
  "prior_loss": prior, "samples_consumed": samples,
652
  "steps_planned": steps_planned},
653
  open(os.path.join(exp, "run_summary.json"), "w"), indent=1, sort_keys=True)