File size: 8,016 Bytes
3dfccac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
"""
Phase 1b Data Preparation Script
=================================
Run this on the CPU-only free tier BEFORE starting your GPU session.
Downloads, tokenizes, and saves all training data to disk so that
the GPU session starts training immediately β€” zero data loading time.

Data sources:
  - TinyStories  (~2.1M stories, already seen in Phase 1 β€” good for annealing)
  - Wikipedia EN (~6.5M articles, NEVER seen by model β€” genuinely new data!)
    β†’ Phase 1 only used FineWeb-Edu + TinyStories, so Wikipedia is 100% fresh.
    β†’ Downloads as cached files (fast), NOT streaming.

Usage:
    python3 prepare_data_phase1b.py

Output:
    ./data/phase1b_chunks.pt   (~3-5 GB on disk)

Time on CPU free tier:
    TinyStories download:  ~3-5 min
    Wikipedia download:    ~20-40 min (~21 GB compressed)
    Tokenization:          ~20-40 min
    TOTAL:                 ~45-90 min  (no GPU cost!)

Then on GPU:
    bash run_phase1b.sh
    β†’ Loads data from disk in ~30 sec, training starts immediately.
"""

import os
import sys
import time
import json
import torch
from pathlib import Path
from tqdm import tqdm

# ── Configuration ─────────────────────────────────────────────────────────────
SEQ_LEN        = 2048
SAVE_PATH      = "./data/phase1b_chunks.pt"
META_PATH      = "./data/phase1b_chunks_meta.json"

# Data sources β€” both genuinely complement Phase 1 (FineWeb-Edu + TinyStories):
#   Wikipedia: 6.5M articles, NEVER seen in Phase 1 β†’ fresh new knowledge
#   TinyStories: already seen, but repeating at lower LR = data annealing (valid!)

# ── Setup ─────────────────────────────────────────────────────────────────────
Path("./data").mkdir(exist_ok=True)

print("\n" + "="*60)
print("  Phase 1b Data Preparation")
print("="*60)
print(f"  Data sources:    Wikipedia EN (new!) + TinyStories (annealing)")
print(f"  Seq len:         {SEQ_LEN}")
print(f"  Save path:       {SAVE_PATH}")
print()

# ── Check if already done ──────────────────────────────────────────────────────
if Path(SAVE_PATH).exists():
    size_gb = Path(SAVE_PATH).stat().st_size / 1e9
    print(f"βœ… Data file already exists: {SAVE_PATH} ({size_gb:.2f} GB)")
    if Path(META_PATH).exists():
        with open(META_PATH) as f:
            meta = json.load(f)
        print(f"   Chunks: {meta['n_chunks']:,} | Tokens: {meta['tokens_B']:.2f}B")
    print("\nπŸ‘‰ Ready to train! Run:  bash run_phase1b.sh")
    sys.exit(0)

# ── Load tokenizer ─────────────────────────────────────────────────────────────
print("πŸ”€ Loading tokenizer...")
from transformers import AutoTokenizer

tokenizer = None
for tok_name in [
    "NousResearch/Llama-2-7b-hf",
    "meta-llama/Llama-2-7b-hf",
    "gpt2",
]:
    try:
        tokenizer = AutoTokenizer.from_pretrained(tok_name)
        print(f"   βœ… {tok_name} β€” vocab: {tokenizer.vocab_size:,}")
        break
    except Exception as e:
        print(f"   ⚠️  {tok_name} unavailable: {e}")

if tokenizer is None:
    raise RuntimeError("No tokenizer available. Run: huggingface-cli login")

eos = tokenizer.eos_token_id

# ── Token packing helper ───────────────────────────────────────────────────────
def pack_texts(texts, desc="packing") -> list:
    """Pack text list into [SEQ_LEN+1] overlapping token chunks."""
    chunks, current = [], []
    for text in tqdm(texts, desc=f"  {desc}", leave=False):
        ids = tokenizer.encode(text, add_special_tokens=False) + [eos]
        if len(ids) >= SEQ_LEN + 1:
            if current:
                chunks.append(current + [eos] * ((SEQ_LEN+1) - len(current)))
                current = []
            for i in range(0, len(ids) - SEQ_LEN, SEQ_LEN):
                chunks.append(ids[i: i+SEQ_LEN+1])
        else:
            if len(current) + len(ids) > SEQ_LEN + 1:
                chunks.append(current + [eos] * ((SEQ_LEN+1) - len(current)))
                current = []
            current.extend(ids)
    if len(current) >= 2:
        chunks.append(current + [eos] * ((SEQ_LEN+1) - len(current)))
    return chunks

all_chunks = []

# ── 1. TinyStories (downloadable files, fast) ──────────────────────────────────
print("\nπŸ“š Downloading TinyStories (~1 GB, fast download)...")
from datasets import load_dataset

t0 = time.time()
ts_ds = load_dataset("roneneldan/TinyStories", split="train")
print(f"   βœ… {len(ts_ds):,} stories downloaded in {(time.time()-t0)/60:.1f} min")

ts_chunks = pack_texts([r["text"] for r in ts_ds], "Tokenizing TinyStories")
print(f"   βœ… {len(ts_chunks):,} chunks ({len(ts_chunks)*SEQ_LEN/1e9:.2f}B tokens)")
all_chunks.extend(ts_chunks)

# ── 2. Wikipedia EN (downloadable, ~21GB, NEVER seen in Phase 1) ──────────────
print(f"\nπŸ“š Downloading English Wikipedia (genuinely new data for model)...")
print("   Phase 1 only used FineWeb-Edu + TinyStories β€” Wikipedia is 100% fresh.")
print("   Downloading as cached files (not streaming) β€” much faster than FineWeb!")
print("   First download: ~20-40 min. Subsequent runs: instant from cache.\n")

t0 = time.time()
try:
    wiki_ds = load_dataset("wikipedia", "20220301.en", split="train",
                           trust_remote_code=True)
    elapsed_min = (time.time() - t0) / 60
    print(f"   βœ… {len(wiki_ds):,} articles downloaded in {elapsed_min:.1f} min")

    wiki_chunks = pack_texts([r["text"] for r in wiki_ds], "Tokenizing Wikipedia")
    print(f"   βœ… {len(wiki_chunks):,} chunks ({len(wiki_chunks)*SEQ_LEN/1e9:.2f}B tokens)")
    all_chunks.extend(wiki_chunks)
except Exception as e:
    print(f"   ⚠️  Wikipedia download failed: {e}")
    print("   Falling back to FineWeb-Edu streaming (500K docs)...")
    fw_ds = load_dataset("HuggingFaceFW/fineweb-edu", "sample-10BT",
                         split="train", streaming=True)
    fw_texts = []
    for i, row in enumerate(tqdm(fw_ds, total=500_000, desc="  FineWeb fallback")):
        if i >= 500_000: break
        fw_texts.append(row["text"])
    fw_chunks = pack_texts(fw_texts, "Tokenizing FineWeb")
    print(f"   βœ… {len(fw_chunks):,} chunks")
    all_chunks.extend(fw_chunks)

# ── Save to disk ───────────────────────────────────────────────────────────────
print(f"\nπŸ’Ύ Saving {len(all_chunks):,} total chunks to disk...")
print("   (Saving as int16 to halve file size β€” auto-cast to int64 when loading)")

# int16 works because vocab_size=32000 < 32767
tensor = torch.tensor(all_chunks, dtype=torch.int16)
torch.save(tensor, SAVE_PATH)

size_gb = Path(SAVE_PATH).stat().st_size / 1e9
tokens_B = len(all_chunks) * SEQ_LEN / 1e9

# Save metadata
meta = {
    "n_chunks": len(all_chunks),
    "tokens_B": round(tokens_B, 3),
    "seq_len": SEQ_LEN,
    "sources": ["TinyStories (annealing)", "Wikipedia EN (new data)"],
    "dtype": "int16",
    "instructions": "Load with: torch.load(path).long()",
}
with open(META_PATH, "w") as f:
    json.dump(meta, f, indent=2)

print(f"\n{'='*60}")
print(f"  βœ… DATA PREP COMPLETE")
print(f"{'='*60}")
print(f"  File:   {SAVE_PATH}")
print(f"  Size:   {size_gb:.2f} GB")
print(f"  Chunks: {len(all_chunks):,}")
print(f"  Tokens: {tokens_B:.2f}B")
print()
print("  Now start your GPU session and run:")
print("    bash run_phase1b.sh")
print()