File size: 6,189 Bytes
ddc12cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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()