unparallelled commited on
Commit
d0a770f
·
verified ·
1 Parent(s): cf65d18

Upload scripts/peer/make_haystack.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. scripts/peer/make_haystack.py +61 -0
scripts/peer/make_haystack.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ make_haystack.py -- produce ONE UTF-8 text haystack shared by every peer model.
4
+
5
+ Why text and not token ids: each peer tokenizes differently, so the only thing
6
+ that can be held constant across models is the CHARACTERS. peer_gen.py then cuts
7
+ each model's prompt to an exact count of its own tokens from this same story.
8
+
9
+ Source is the b3_16384_32768 val shard of unparallelled/BluScriptCP-data --
10
+ GPT-2 uint16 token ids, decoded back to text here. That shard was held out from
11
+ every training arm of ours, and the peers never saw our data at all, so it is
12
+ out-of-distribution for everything being compared.
13
+
14
+ usage: make_haystack.py --out hay.txt --tokens 4000000
15
+ """
16
+
17
+ import argparse
18
+ import os
19
+
20
+ EOT = "<|endoftext|>"
21
+
22
+
23
+ def main():
24
+ ap = argparse.ArgumentParser()
25
+ ap.add_argument("--out", required=True)
26
+ ap.add_argument("--tokens", type=int, default=4_000_000,
27
+ help="GPT-2 tokens to decode; peers need prompt_len*(n+2) of "
28
+ "THEIR tokens, and their tokenizers are ~15%% denser than "
29
+ "GPT-2 on English, so over-provision")
30
+ ap.add_argument("--repo", default="unparallelled/BluScriptCP-data")
31
+ ap.add_argument("--file", default="cp_data_by_length/b3_16384_32768/val_shard_00000.bin")
32
+ args = ap.parse_args()
33
+
34
+ import numpy as np, tiktoken
35
+ from huggingface_hub import hf_hub_download
36
+ p = hf_hub_download(args.repo, args.file, repo_type="dataset")
37
+ toks = np.fromfile(p, dtype=np.uint16, count=args.tokens)
38
+ print(f" read {toks.size:,} GPT-2 tokens from {os.path.basename(p)}")
39
+ enc = tiktoken.get_encoding("gpt2")
40
+ # decode in chunks: one 4M-token decode builds a huge intermediate string
41
+ parts, CH = [], 200_000
42
+ for s in range(0, toks.size, CH):
43
+ parts.append(enc.decode(toks[s:s + CH].astype(np.int64).tolist()))
44
+ text = "".join(parts)
45
+ # STRIP the literal "<|endoftext|>". The corpus was tokenized with
46
+ # encode_ordinary, so GPT-2 id 50256 decodes back to that literal string --
47
+ # and the peers disagree about it: Qwen3 re-encodes it to ONE special token
48
+ # (151643, a document reset that truncates attention), while Spark-X2.5
49
+ # treats it as 7 ordinary tokens. Leaving it in would hand the two models
50
+ # materially different haystacks from identical text.
51
+ n_eot = text.count(EOT)
52
+ text = text.replace(EOT, " ")
53
+ print(f" stripped {n_eot:,} '{EOT}' markers "
54
+ f"(~{n_eot * 16384 / max(1, toks.size):.1f} per 16k-token prompt)")
55
+ with open(args.out, "w", encoding="utf-8") as f:
56
+ f.write(text)
57
+ print(f" wrote {args.out} {len(text):,} chars ({len(text)/toks.size:.2f} chars/gpt2-token)")
58
+
59
+
60
+ if __name__ == "__main__":
61
+ main()