Collision-1B / data /prepare.py
collision-10M's picture
Release COLLISION-10M with official in-house NLP Engine and analytics
ddc12cc verified
Raw History Blame Contribute Delete
6.19 kB
import os
import json
import re
import argparse
from datetime import datetime
def generate_sample_text():
text_blocks = [
"COLLISION-1M is a small decoder-only Transformer language model.",
"We are training COLLISION-1M from scratch on a computer CPU.",
"A language model learns to predict the next token in a sequence.",
"The model uses causal self-attention to read previous tokens.",
"Each token is represented by an embedding vector of numbers.",
"Attention heads compute relationships between words and sentences.",
"The neural network updates its weights using gradient descent and backpropagation.",
"AdamW is the optimizer used to train the language model weights.",
"A checkpoint saves the model weights and optimizer state so we can resume training.",
"Streamlit displays the COLLISION LAB dashboard for training progress.",
"The physics of a collision involves conservation of momentum and energy.",
"An elastic collision conserves both kinetic energy and momentum.",
"An inelastic collision converts kinetic energy into other forms of energy like heat.",
"When particles collide, they exchange energy and momentum.",
"The collider accelerates particles to near the speed of light.",
"High-energy collisions help physicists discover new particles and forces.",
"In deep learning, we stack multiple layers of attention and feed-forward networks.",
"Positional embeddings allow the model to understand the order of tokens in a sequence.",
"A tokenizer splits text into integers called token IDs.",
"Evaluating the model gives us the training loss, validation loss, and perplexity.",
"Perplexity measures how well the model predicts sample text.",
"We can scale the model: COLLISION-1M to COLLISION-10M and then COLLISION-50M.",
"The future of learning is built on feedback loops and model alignment."
]
replicated_text = "\n".join(text_blocks * 50)
return replicated_text
def clean_and_normalize(text):
# Removes obviously corrupted content / non-printable control chars, preserves printable ascii and common spacing
text = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', text)
# Normalizes spaces
text = re.sub(r'[ \t]+', ' ', text)
# Normalizes double newlines to single newlines or standard spacing
text = re.sub(r'\n+', '\n', text)
return text.strip()
def get_next_version(datasets_dir):
os.makedirs(datasets_dir, exist_ok=True)
versions = []
for d in os.listdir(datasets_dir):
match = re.match(r'collision_dataset_v(\d+)', d)
if match:
versions.append(int(match.group(1)))
next_ver = max(versions) + 1 if versions else 1
return next_ver
def main():
parser = argparse.ArgumentParser(description="Prepare text datasets and generate versioned split outputs")
parser.add_argument("--raw-dir", type=str, default="data/raw", help="Path to raw data directory")
parser.add_argument("--datasets-dir", type=str, default="datasets", help="Path to datasets directory")
args = parser.parse_args()
os.makedirs(args.raw_dir, exist_ok=True)
os.makedirs(args.datasets_dir, exist_ok=True)
# 1. Ensure sample exists if empty
sample_path = os.path.join(args.raw_dir, "sample.txt")
if not os.path.exists(sample_path):
with open(sample_path, "w", encoding="utf-8") as f:
f.write(generate_sample_text())
print(f"Created initial sample raw text at {sample_path}")
# 2. Automatically discover all .txt files
txt_files = [f for f in os.listdir(args.raw_dir) if f.endswith(".txt")]
print(f"Discovered {len(txt_files)} file(s) in {args.raw_dir}: {txt_files}")
documents = []
seen_hashes = set()
total_raw_chars = 0
for f_name in txt_files:
f_path = os.path.join(args.raw_dir, f_name)
with open(f_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
total_raw_chars += len(content)
# Simple line-by-line deduplication/cleaning to remove redundant blocks
lines = content.split("\n")
cleaned_lines = []
for line in lines:
cleaned = clean_and_normalize(line)
if not cleaned:
continue
# Remove exact duplicate lines/documents to clean corpus
line_hash = hash(cleaned)
if line_hash not in seen_hashes:
seen_hashes.add(line_hash)
cleaned_lines.append(cleaned)
if cleaned_lines:
documents.append("\n".join(cleaned_lines))
# Join cleaned docs
combined_text = "\n".join(documents)
total_cleaned_chars = len(combined_text)
# 3. Create auto-incrementing dataset version folder
next_ver = get_next_version(args.datasets_dir)
version_name = f"collision_dataset_v{next_ver}"
version_dir = os.path.join(args.datasets_dir, version_name)
os.makedirs(version_dir, exist_ok=True)
# Write the cleaned text temporarily to be tokenized later
cleaned_txt_path = os.path.join(version_dir, "cleaned.txt")
with open(cleaned_txt_path, "w", encoding="utf-8") as f:
f.write(combined_text)
# Write initial metadata (will be fully completed after tokenization)
meta = {
"dataset_version": version_name,
"source_files": txt_files,
"raw_characters": total_raw_chars,
"cleaned_characters": total_cleaned_chars,
"creation_date": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
"token_count": 0,
"vocabulary_size": 0,
"train_tokens": 0,
"validation_tokens": 0
}
meta_path = os.path.join(version_dir, "metadata.json")
with open(meta_path, "w", encoding="utf-8") as f:
json.dump(meta, f, indent=2)
print(f"Created versioned dataset directory at {version_dir}")
print(f"Characters cleaned and written: {total_cleaned_chars} (raw: {total_raw_chars})")
if __name__ == "__main__":
main()