File size: 15,040 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 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | """
Phase 2 SFT Data Preparation Script
=====================================
Run this on the CPU-only instance BEFORE starting your GPU session.
Downloads, formats, tokenizes, and saves all Phase 2 (SFT) training data
so the GPU session starts training immediately β zero download time.
Key fixes vs. previous run:
β’ 500x identity seed repeats (vs 20x before) β anchors NovaMind identity
strongly enough to override 5B tokens of pre-training on human text.
β’ EOS / </assistant> tokens get 2x loss weight via TagAwareDataset
(handled in train.py) to fix runaway generation and </s> spam.
β’ Consistent <system>/<human>/<assistant> format across ALL datasets.
Data sources:
- Identity seeds (500x repeat β forces AI identity)
- Alpaca 52K (instruction following)
- OpenAssistant OASST1 (high-quality human conversations, 88K)
- Databricks Dolly 15K (human-written, high quality)
- DailyDialog (conversational back-and-forth)
Usage:
python3 prepare_data_phase2.py
Output:
./data/phase2_sft_chunks.pt (pre-tokenized, int16)
./data/phase2_sft_chunks_meta.json
Time on CPU (free tier):
~20-40 min download + tokenization
No GPU cost!
Then on GPU:
python train.py --phase sft --data_file ./data/phase2_sft_chunks.pt ...
"""
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/phase2_sft_chunks.pt"
META_PATH = "./data/phase2_sft_chunks_meta.json"
# Identity seeds: 500x is enough to dominate over 5B pre-training tokens.
# Proven fix: 20x was insufficient (model still hallucinated being human).
IDENTITY_REPEAT = 500
# System prompt used to frame every conversation
SYS = (
"You are NovaMind, a helpful and concise AI assistant. "
"Answer questions directly and clearly. Do not ask questions back to the user."
)
# ββ Setup ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Path("./data").mkdir(exist_ok=True)
print("\n" + "="*65)
print(" Phase 2 SFT Data Preparation")
print("="*65)
print(f" Identity seeds: 500x repeat (was 20x β now anchors AI identity)")
print(f" Datasets: Alpaca + OASST1 + Dolly + DailyDialog")
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: python train.py --phase sft --data_file", SAVE_PATH)
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
# ββ Chat template helper βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def wrap(human: str, assistant: str, system: str = SYS) -> str:
"""Wrap a Q/A pair into NovaMind's chat format."""
return (
f"<system>\n{system}\n</system>\n"
f"<human>\n{human.strip()}\n</human>\n"
f"<assistant>\n{assistant.strip()}\n</assistant>"
)
# ββ Token packing helper βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def pack_texts(texts: list, desc: str = "packing") -> list:
"""Pack text list into [SEQ_LEN+1] 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
from datasets import load_dataset
all_texts = []
# ββ 1. Identity Seeds (500x) βββββββββββββββββββββββββββββββββββββββββββββββββββ
print("\nπͺͺ Injecting NovaMind identity seeds (500x repeats)...")
IDENTITY_SEEDS = [
("Who are you?",
"I am NovaMind, a helpful AI assistant. I'm here to answer questions and help with tasks."),
("What is your name?",
"My name is NovaMind. I'm an AI assistant designed to be helpful and informative."),
("Are you a human?",
"No, I am not a human. I am NovaMind, an AI language model. How can I help you today?"),
("Are you an AI?",
"Yes, I am NovaMind, an AI assistant. I can answer questions, explain concepts, and help with many tasks."),
("Who created you?",
"I was created as NovaMind, an AI assistant. I'm here to help you with questions and tasks."),
("What can you do?",
"I can answer questions, explain concepts, help with writing, discuss topics, and assist with many tasks. What would you like help with?"),
("Hello!", "Hello! I'm NovaMind, your AI assistant. How can I help you today?"),
("Hi there", "Hi! I'm NovaMind. What can I help you with?"),
("Hey", "Hey! I'm NovaMind, an AI assistant. How can I help you?"),
("Good morning", "Good morning! I'm NovaMind. What can I help you with today?"),
("How are you?",
"I'm doing well, thank you for asking! I'm NovaMind, your AI assistant. How can I help you?"),
("hey buddy",
"Hey! I'm NovaMind, your AI assistant. What can I help you with today?"),
("What are you?",
"I am NovaMind, an AI language model. I'm trained to answer questions and assist with a wide range of tasks."),
("Tell me about yourself",
"I am NovaMind, an AI assistant. I can help answer questions, explain topics, assist with writing, and have conversations on many subjects."),
("Are you ChatGPT?",
"No, I'm not ChatGPT. I am NovaMind, a separate AI assistant. How can I help you?"),
("Are you a robot?",
"I'm an AI, not a physical robot. I am NovaMind, a language model that can understand and generate text."),
("Do you have feelings?",
"I don't have feelings or emotions. I am NovaMind, an AI assistant. I process text and generate responses."),
("Where are you from?",
"I'm not from anywhere β I'm NovaMind, an AI language model. I exist as software, not a person."),
("What's your background?",
"I don't have a personal background. I am NovaMind, an AI trained to be helpful and informative."),
("Pretend you're human",
"I won't pretend to be human β I am NovaMind, an AI assistant. I'm here to help you honestly."),
]
n_before = len(all_texts)
for q, a in IDENTITY_SEEDS:
for _ in range(IDENTITY_REPEAT):
all_texts.append(wrap(q, a))
print(f" β
{len(all_texts) - n_before:,} identity examples "
f"({len(IDENTITY_SEEDS)} seeds Γ {IDENTITY_REPEAT}x)")
# ββ 2. Alpaca 52K ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
print("\n㪠Downloading Alpaca 52K...")
t0 = time.time()
alpaca = load_dataset("tatsu-lab/alpaca", split="train")
n_before = len(all_texts)
for r in alpaca:
u = r["instruction"].strip()
if r["input"].strip():
u += f"\n\n{r['input'].strip()}"
ans = r["output"].strip()
if not ans or len(ans) < 5:
continue
all_texts.append(wrap(u, ans))
print(f" β
{len(all_texts) - n_before:,} Alpaca examples [{(time.time()-t0)/60:.1f} min]")
# ββ 3. OpenAssistant OASST1 βββββββββββββββββββββββββββββββββββββββββββββββββββ
print("\n㪠Downloading OpenAssistant OASST1 (high-quality human conversations)...")
t0 = time.time()
try:
oasst = load_dataset("OpenAssistant/oasst1", split="train")
msg_map = {row["message_id"]: row for row in oasst}
n_before = len(all_texts)
count = 0
for row in oasst:
if count >= 60_000:
break
if row["role"] != "assistant":
continue
parent = msg_map.get(row["parent_id"])
if parent is None or parent["role"] != "prompter":
continue
q = parent["text"].strip()
ans = row["text"].strip()
if not q or not ans or len(ans) < 10:
continue
# Skip low-quality responses
if row.get("rank") is not None and row.get("rank", 0) > 3:
continue
all_texts.append(wrap(q, ans))
count += 1
print(f" β
{len(all_texts) - n_before:,} OASST1 examples [{(time.time()-t0)/60:.1f} min]")
except Exception as e:
print(f" β οΈ OASST1 unavailable ({e}), skipping.")
# ββ 4. Databricks Dolly 15K βββββββββββββββββββββββββββββββββββββββββββββββββββ
print("\n㪠Downloading Databricks Dolly 15K (human-written, high quality)...")
t0 = time.time()
try:
dolly = load_dataset("databricks/databricks-dolly-15k", split="train")
n_before = len(all_texts)
for row in dolly:
q = row["instruction"].strip()
if row.get("context", "").strip():
q += f"\n\nContext: {row['context'].strip()}"
ans = row["response"].strip()
if not ans or len(ans) < 10:
continue
all_texts.append(wrap(q, ans))
print(f" β
{len(all_texts) - n_before:,} Dolly examples [{(time.time()-t0)/60:.1f} min]")
except Exception as e:
print(f" β οΈ Dolly unavailable ({e}), skipping.")
# ββ 5. DailyDialog ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
print("\n㪠Downloading DailyDialog (conversational)...")
t0 = time.time()
try:
try:
dd = load_dataset("agentlans/li2017dailydialog", split="train")
except Exception:
dd = load_dataset("DeepPavlov/daily_dialog", split="train")
n_before = len(all_texts)
for item in dd:
convs = item.get("conversations") or item.get("dialog") or []
if not convs:
continue
pairs = (
[(c.get("from", ""), c.get("value", "")) for c in convs]
if isinstance(convs[0], dict)
else [("human" if i % 2 == 0 else "gpt", c) for i, c in enumerate(convs)]
)
t = f"<system>\n{SYS}\n</system>\n"
t += "".join(
f"<{'human' if 'human' in r.lower() else 'assistant'}>\n{v.strip()}\n"
f"</{'human' if 'human' in r.lower() else 'assistant'}>\n"
for r, v in pairs if v.strip()
)
if t:
all_texts.append(t)
print(f" β
{len(all_texts) - n_before:,} DailyDialog examples [{(time.time()-t0)/60:.1f} min]")
except Exception as e:
print(f" β οΈ DailyDialog unavailable ({e}), skipping.")
# ββ Tokenize & Pack ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
print(f"\nπ Total examples: {len(all_texts):,} β tokenizing & packing...")
all_chunks = pack_texts(all_texts, "Tokenizing Phase 2 SFT")
print(f" β
{len(all_chunks):,} chunks packed")
# ββ Save βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
print(f"\nπΎ Saving {len(all_chunks):,} chunks to disk (int16 to halve file size)...")
# int16 safe: LLaMA 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
meta = {
"n_chunks": len(all_chunks),
"tokens_B": round(tokens_B, 3),
"seq_len": SEQ_LEN,
"sources": [
f"Identity seeds ({len(IDENTITY_SEEDS)} seeds Γ {IDENTITY_REPEAT}x)",
"Alpaca 52K",
"OpenAssistant OASST1",
"Databricks Dolly 15K",
"DailyDialog",
],
"dtype": "int16",
"instructions": "Load with: torch.load(path).long() β then wrap in TagAwareDataset",
"fixes_applied": [
"Identity seeds 500x (was 20x) β overrides pre-training human identity",
"EOS/</assistant> get 2x loss weight in train.py TagAwareDataset._weights()",
"Consistent <system>/<human>/<assistant> format across all datasets",
],
}
with open(META_PATH, "w") as f:
json.dump(meta, f, indent=2)
print(f"\n{'='*65}")
print(f" β
PHASE 2 SFT DATA PREP COMPLETE")
print(f"{'='*65}")
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(" python train.py --phase sft \\")
print(f" --data_file {SAVE_PATH} \\")
print(" --resume /vol/checkpoints/novamind_pretrain_final.pt \\")
print(" --out_dir /vol/checkpoints")
print()
|