""" Pretraining Data Pipeline -- Phase 3 (0.4B Tokens) =================================================== Streams 400 Million tokens of domain-diverse text: - Wikipedia (wikimedia/wikipedia) -- 200M tokens (50%) - Gutenberg (emozilla/pg19) -- 120M tokens (30%) - Python Code (bigcode/the-stack) -- 80M tokens (20%) Outputs to `./data_phase3` to avoid touching active training in `./data`. Usage: python scripts/dataprep_phase3.py """ import argparse import json import os import time import numpy as np import tiktoken from tqdm import tqdm DEFAULT_OUTPUT_DIR = "./data_phase3" DEFAULT_TOKENIZER = "gpt2" DEFAULT_TOTAL_TOKENS = 400_000_000 # 0.4B tokens DEFAULT_VAL_RATIO = 0.01 DEFAULT_WRITE_CHUNK = 1_000_000 SOURCE_ALLOCATIONS = { "wikipedia": 0.50, # 200M tokens "gutenberg": 0.30, # 120M tokens "starcoder": 0.20, # 80M tokens } GPT2_EOT = 50256 def iter_wikipedia(): from datasets import load_dataset ds = load_dataset("wikimedia/wikipedia", "20231101.en", split="train", streaming=True) for example in ds: text = example.get("text", "") if text: yield text def iter_gutenberg(): from datasets import load_dataset ds = load_dataset("emozilla/pg19", split="train", streaming=True) for example in ds: text = example.get("text", "") if text: yield text def iter_starcoder(): from datasets import load_dataset try: ds = load_dataset("bigcode/the-stack-smol", data_dir="data/python", split="train", streaming=True) for example in ds: text = example.get("content", "") if text: yield text except Exception: ds = load_dataset("codeparrot/codeparrot-clean", split="train", streaming=True) for example in ds: text = example.get("content", "") if text: yield text SOURCE_ITERATORS = { "wikipedia": iter_wikipedia, "gutenberg": iter_gutenberg, "starcoder": iter_starcoder, } def tokenize_and_write(source_name, text_iterator, target_tokens, encoder, eot_token, train_file, val_file, val_ratio, write_chunk, dtype): train_buffer, val_buffer = [], [] train_total, val_total = 0, 0 doc_count = 0 rng = np.random.default_rng(seed=42 + hash(source_name) % 10000) pbar = tqdm( total=target_tokens, unit="tok", unit_scale=True, desc=f" {source_name}", bar_format=" {desc}: {percentage:3.0f}% |{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]", ) for text in text_iterator: tokens = encoder.encode_ordinary(text) tokens.append(eot_token) is_val = rng.random() < val_ratio if is_val: val_buffer.extend(tokens) val_total += len(tokens) if len(val_buffer) >= write_chunk: val_file.write(np.array(val_buffer, dtype=dtype).tobytes()) val_buffer = [] else: train_buffer.extend(tokens) train_total += len(tokens) if len(train_buffer) >= write_chunk: train_file.write(np.array(train_buffer, dtype=dtype).tobytes()) train_buffer = [] doc_count += 1 pbar.update(len(tokens)) if train_total + val_total >= target_tokens: break if train_buffer: train_file.write(np.array(train_buffer, dtype=dtype).tobytes()) if val_buffer: val_file.write(np.array(val_buffer, dtype=dtype).tobytes()) pbar.close() print(f" {source_name}: {doc_count:,} docs | train {train_total:,} + val {val_total:,} tokens") return train_total, val_total def main(): parser = argparse.ArgumentParser(description="Pretraining Data Pipeline -- Phase 3") parser.add_argument("--output_dir", type=str, default=DEFAULT_OUTPUT_DIR) parser.add_argument("--total_tokens", type=int, default=DEFAULT_TOTAL_TOKENS) args = parser.parse_args() os.makedirs(args.output_dir, exist_ok=True) train_path = os.path.join(args.output_dir, "train.bin") val_path = os.path.join(args.output_dir, "val.bin") meta_path = os.path.join(args.output_dir, "meta.json") encoder = tiktoken.get_encoding(DEFAULT_TOKENIZER) vocab_size = encoder.n_vocab dtype = np.uint16 if vocab_size <= 65535 else np.uint32 source_targets = {k: int(args.total_tokens * v) for k, v in SOURCE_ALLOCATIONS.items()} print(f"\n{'='*70}") print(f" Phase 3 Data Pipeline (0.4B Tokens Target)") print(f"{'='*70}") print(f" Output Dir: {os.path.abspath(args.output_dir)}") for name, target in source_targets.items(): print(f" {name:15s}: {target:,} tokens") print(f"{'='*70}\n") total_train, total_val = 0, 0 t0 = time.time() with open(train_path, "wb") as train_file, open(val_path, "wb") as val_file: for source_name, target in source_targets.items(): print(f"\n [{source_name}] Streaming {target:,} tokens...") try: tr, va = tokenize_and_write( source_name, SOURCE_ITERATORS[source_name](), target, encoder, GPT2_EOT, train_file, val_file, DEFAULT_VAL_RATIO, DEFAULT_WRITE_CHUNK, dtype ) total_train += tr total_val += va except Exception as e: print(f" [WARN] Error on {source_name}: {e}") meta = { "tokenizer": DEFAULT_TOKENIZER, "vocab_size": vocab_size, "dtype": "uint16" if dtype == np.uint16 else "uint32", "train_tokens": total_train, "val_tokens": total_val, "total_tokens": total_train + total_val, "sources": list(source_targets.keys()), } with open(meta_path, "w") as f: json.dump(meta, f, indent=2) print(f"\n [DONE] Phase 3 data ready at {args.output_dir}! Total: {total_train + total_val:,} tokens") if __name__ == "__main__": main()