LH-Tech-AI's picture
Upload 2 files
e908f87 verified
import os
import numpy as np
import tiktoken
from datasets import load_dataset
from tqdm import tqdm
# --- KONFIGURATION ---
OUTPUT_DIR = "data/alpaca_cleaned_mixed_NEW"
# Wichtig: Das muss exakt der Tokenizer sein, den dein Modell verwendet (meist GPT-2)
TOKENIZER_NAME = "gpt2"
SEED = 1337
# Balance: Wie viel Alpaca vs. FineWeb?
# Zu viel FineWeb = Modell antwortet nicht im Chat-Stil
# Zu wenig FineWeb = Modell wird dumm (vergisst Weltwissen)
FINEWEB_SAMPLES = 520000
enc = tiktoken.get_encoding(TOKENIZER_NAME)
EOS_TOKEN = "<|endoftext|>" # End of Sequence Token
def format_prompt_with_mask(instruction, input_text, output):
"""
Formatiert den Prompt und erstellt die Loss-Maske.
Format:
Instruction: ...
Input: ... (optional)
Response: ... <|endoftext|>
"""
# 1. Den "Prompt"-Teil bauen (Frage) -> Wird NICHT trainiert (Maske 0)
if input_text and input_text.strip():
prompt_text = f"Instruction:\n{instruction}\n\nInput:\n{input_text}\n\nResponse:\n"
else:
prompt_text = f"Instruction:\n{instruction}\n\nResponse:\n"
# 2. Den "Completion"-Teil bauen (Antwort) -> Wird TRAINIERT (Maske 1)
completion_text = f"{output}{EOS_TOKEN}"
# 3. Tokenisieren
# encode_plain verhindert, dass special tokens im normalen Text interpretiert werden
prompt_ids = enc.encode(prompt_text, allowed_special={'<|endoftext|>'})
completion_ids = enc.encode(completion_text, allowed_special={'<|endoftext|>'})
# 4. Zusammenfügen
full_ids = prompt_ids + completion_ids
# 5. Maske erstellen
# 0 = Ignorieren (Loss wird hier nicht berechnet)
# 1 = Trainieren (Modell soll lernen, das vorherzusagen)
mask = [0] * len(prompt_ids) + [1] * len(completion_ids)
return full_ids, mask
def main():
np.random.seed(SEED)
print(f"🚀 Starte Prepare-Script für SmaLLMPro (350M SFT)...")
print(f"📚 Tokenizer: {TOKENIZER_NAME}")
os.makedirs(OUTPUT_DIR, exist_ok=True)
# --- 1. DATENSÄTZE LADEN ---
print("📥 Lade 'yahma/alpaca-cleaned' (Chat-Instruktionen)...")
alpaca = load_dataset("yahma/alpaca-cleaned", split='train')
print(f"📥 Lade 'HuggingFaceFW/fineweb-edu' (Sample-10BT) für {FINEWEB_SAMPLES} Samples...")
fineweb = load_dataset("HuggingFaceFW/fineweb-edu", name="sample-10BT", split='train', streaming=True)
all_tokens = []
all_masks = []
# --- 2. ALPACA VERARBEITEN (Masking aktiv) ---
print("⚙️ Verarbeite Alpaca...")
for ex in tqdm(alpaca, desc="Alpaca"):
ids, mask = format_prompt_with_mask(ex['instruction'], ex['input'], ex['output'])
all_tokens.extend(ids)
all_masks.extend(mask)
alpaca_len = len(all_tokens)
print(f" -> Alpaca Tokens: {alpaca_len:,}")
# --- 3. FINEWEB VERARBEITEN (Wissenserhalt) ---
# Hier setzen wir die Maske auf 1 für den GANZEN Text.
# Warum? Das Modell soll das Weltwissen aktiv auffrischen, nicht nur als Prompt sehen.
print("⚙️ Verarbeite FineWeb (Anti-Forgetting)...")
fw_iter = iter(fineweb)
fw_count = 0
fw_tokens_count = 0
for _ in tqdm(range(FINEWEB_SAMPLES), desc="FineWeb"):
try:
ex = next(fw_iter)
text = ex['text'] + EOS_TOKEN
ids = enc.encode(text, allowed_special={EOS_TOKEN})
all_tokens.extend(ids)
# Alles lernen!
all_masks.extend([1] * len(ids))
fw_tokens_count += len(ids)
fw_count += 1
except StopIteration:
break
print(f" -> FineWeb Tokens: {fw_tokens_count:,} (aus {fw_count} Dokumenten)")
# --- 4. SPEICHERN ---
total_tokens = len(all_tokens)
print(f"\n💾 Speichere {total_tokens:,} Tokens in '{OUTPUT_DIR}'...")
# Tokens als uint16 (spart Platz, reicht für GPT-2 Vocab ~50k)
token_arr = np.array(all_tokens, dtype=np.uint16)
token_arr.tofile(os.path.join(OUTPUT_DIR, "train.bin"))
# Maske als uint8 (braucht nur 0 oder 1)
mask_arr = np.array(all_masks, dtype=np.uint8)
mask_arr.tofile(os.path.join(OUTPUT_DIR, "train_mask.bin"))
# --- 5. SANITY CHECK (Ganz wichtig!) ---
print("\n🔍 --- SANITY CHECK ---")
print("Ich dekodiere die ersten 50 Tokens des ersten Beispiels, um zu prüfen, ob alles stimmt.")
print("Grün (TRAIN) = Was das Modell lernt. Grau (IGNORE) = Was das Modell nur liest.")
check_len = 100
sample_ids = all_tokens[:check_len]
sample_mask = all_masks[:check_len]
# Wir rekonstruieren den Text und zeigen, was maskiert ist
decoded_parts = []
for t_id, m_val in zip(sample_ids, sample_mask):
token_str = enc.decode([t_id])
if m_val == 1:
decoded_parts.append(f"\033[92m{token_str}\033[0m") # Grün für Training
else:
decoded_parts.append(f"\033[90m{token_str}\033[0m") # Grau für Prompt
print("".join(decoded_parts))
print("\n(Legende: \033[90mGrau=Prompt/Ignoriert\033[0m, \033[92mGrün=Response/Gelernt\033[0m)")
if len(token_arr) != len(mask_arr):
print("\n❌ ACHTUNG: Token und Mask Array sind unterschiedlich lang! Irgendwas stimmt nicht!")
else:
print("\n✅ Alles perfekt. Arrays sind synchron. Du kannst trainieren.")
if __name__ == "__main__":
main()