cipher-data-pipeline · git:20260908.e9ac98d · 2026-09-08 · sha256 d23eaebc34107398
cipher-data-pipeline git:20260908.e9ac98dA
Immutable. This exact content is served forever at /api/v1/blob/d23eaebc34107398.
---
name: cipher-data-pipeline
description: Prepare, tokenize, and shard the FSI Felon Cipher training corpus. Use when building the training data pipeline. Phase 2 of cipher-cloud-training.
---
# Phase 2: Training Data Pipeline
## Purpose
Convert 32,429+ curated docs into tokenized, sharded .pt files for cloud training. CPU-only work done locally.
## Corpus Sources
| Source | Location | Est tokens |
|--------|----------|------------|
| Gold cipher docs | `E:\queen-bee-v5\training_curated\` | ~45M |
| Training SOP docs | `E:\queen-bee-v5\training\` | ~5M |
| Gold corpus (code) | `E:\lab_training_corpus\ferrell_coder\gold_corpus.txt` | ~3.5M |
| Colony teacher traces | `E:\colony_teacher\` | ~75M |
| **Total available** | | **~130M** |
## Step 1: Tokenize
```python
import torch
from tokenizers import Tokenizer
from pathlib import Path
def tokenize_corpus(tokenizer_path, corpus_dirs, output_path, seq_len=2048):
tokenizer = Tokenizer.from_file(tokenizer_path)
bos_id = tokenizer.token_to_id("<s>")
all_ids = []
for d in corpus_dirs:
for p in Path(d).rglob("*.txt"):
text = p.read_text(encoding="utf-8", errors="ignore")
if len(text) < 50: continue
enc = tokenizer.encode(text)
all_ids.append(bos_id)
all_ids.extend(enc.ids)
# Pad to seq_len multiple
remainder = len(all_ids) % seq_len
if remainder: all_ids.extend([0] * (seq_len - remainder))
sequences = torch.tensor(all_ids, dtype=torch.long).view(-1, seq_len)
# Split 95/5
n = len(sequences)
perm = torch.randperm(n)
n_train = int(n * 0.95)
output_path = Path(output_path)
output_path.mkdir(exist_ok=True)
torch.save({"sequences": sequences[perm[:n_train]]}, output_path / "train.pt")
torch.save({"sequences": sequences[perm[n_train:]]}, output_path / "val.pt")
print(f"Train: {n_train} seqs, Val: {n-n_train} seqs")
return n_train
tokenize_corpus(
"E:\\FSI-FELON\\models\\fsi_felon_cipher\\exports\\tokenizer_v5.json",
["E:\\queen-bee-v5\\training_curated", "E:\\queen-bee-v5\\training",
"E:\\lab_training_corpus\\ferrell_coder"],
"E:\\cipher_training_data",
seq_len=2048
)
```
## Step 2: Shard
```python
import hashlib, json
def shard_data(data_path, shard_dir, shards=20):
data = torch.load(data_path)["sequences"]
shard_size = len(data) // shards
shard_dir = Path(shard_dir); shard_dir.mkdir(exist_ok=True)
manifest = []
for i in range(shards):
s, e = i*shard_size, (i+1)*shard_size if i<shards-1 else len(data)
shard = data[s:e]
path = shard_dir / f"shard_{i:04d}.pt"
torch.save({"sequences": shard, "shard_id": i}, path)
manifest.append({"path": str(path), "n_sequences": len(shard),
"checksum": hashlib.sha256(open(path,"rb").read()).hexdigest()})
json.dump(manifest, open(shard_dir/"manifest.json","w"), indent=2)
print(f"✓ {shards} shards created")
shard_data("E:\\cipher_training_data\\train.pt", "E:\\cipher_training_data\\shards_train")
shard_data("E:\\cipher_training_data\\val.pt", "E:\\cipher_training_data\\shards_val")
```
## Step 3: Verify
```python
def verify_shards(shard_dir, tokenizer_path):
tok = Tokenizer.from_file(tokenizer_path)
manifest = json.load(open(Path(shard_dir)/"manifest.json"))
for s in manifest:
d = torch.load(s["path"])
seqs = d["sequences"]
assert seqs.dim()==2 and seqs.shape[1]==2048
assert seqs.min()>=0 and seqs.max()<32768
decoded = tok.decode(seqs[0][:50].tolist())
assert len(decoded)>0
print(f"✓ All {len(manifest)} shards verified")
verify_shards("E:\\cipher_training_data\\shards_train", tokenizer_path)
```
## Upload Size
~8 MB/shard × 20 shards = ~160 MB total. Upload: <1 min.
## What NOT To Do
1. DO NOT tokenize on cloud (CPU work, wastes GPU money)
2. DO NOT skip document separators (model must learn boundaries)
3. DO NOT use variable-length sequences
4. DO NOT forget the manifest.json
5. DO NOT shard into <5 or >100 pieces
## Bugs
| Bug | Fix |
|-----|-----|
| Unicode decode errors | Use errors="ignore" |
| Empty sequences after tokenization | Filter sequences < 10 tokens |
| Tokenizer vocab mismatch | Verify path matches config |
| RNG not seeded | Set torch.manual_seed(42) |