Files
2026-07-30 06:38:26 +08:00

193 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""Download and freeze the byte-level WikiText-2 corpus for the AttnRes study."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import urllib.request
from pathlib import Path
from typing import Any
import pyarrow.parquet as pq
PROTOCOL_ID = "llm-atlas-k3-attnres-reduced-v1"
DATASET_REPO = "Salesforce/wikitext"
DATASET_REVISION = "b08601e04326c79dfdd32d625aee71d232d685c3"
DATASET_VARIANT = "wikitext-2-raw-v1"
SPLITS = ("train", "validation", "test")
SEEDS = (2026073001, 2026073002, 2026073003)
CONTEXT = 256
FORMAL_STEPS = 2000
FORMAL_BATCH = 32
VALIDATION_WINDOWS = 64
DIAGNOSTIC_WINDOWS = 16
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--cache-dir", type=Path, required=True)
parser.add_argument("--manifest", type=Path, required=True)
return parser.parse_args()
def file_sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def bytes_sha256(payload: bytes) -> str:
return hashlib.sha256(payload).hexdigest()
def atomic_json(path: Path, value: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_suffix(path.suffix + ".tmp")
temporary.write_text(
json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
)
os.replace(temporary, path)
def download(url: str, path: Path) -> None:
if path.exists():
return
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_suffix(path.suffix + ".part")
request = urllib.request.Request(
url,
headers={"User-Agent": "llm-atlas-k3-attnres-reduced/1.0"},
)
with urllib.request.urlopen(request, timeout=120) as response:
with temporary.open("wb") as output:
while block := response.read(1024 * 1024):
output.write(block)
os.replace(temporary, path)
def window_start(label: str, index: int, corpus_length: int, seed: int | None = None) -> int:
fields = [PROTOCOL_ID, label]
if seed is not None:
fields.append(str(seed))
fields.append(str(index))
payload = "\0".join(fields).encode()
value = int.from_bytes(hashlib.sha256(payload).digest()[:8], "big")
return value % (corpus_length - (CONTEXT + 1))
def train_window_start(seed: int, step: int, row: int, corpus_length: int) -> int:
payload = "\0".join(
[PROTOCOL_ID, "train-window", str(seed), str(step), str(row)]
).encode()
value = int.from_bytes(hashlib.sha256(payload).digest()[:8], "big")
return value % (corpus_length - (CONTEXT + 1))
def concatenate_split(parquet_path: Path) -> tuple[bytes, int]:
table = pq.read_table(parquet_path, columns=["text"])
rows = table.column("text").to_pylist()
payload = b"".join(((row or "") + "\n").encode("utf-8") for row in rows)
return payload, len(rows)
def main() -> None:
args = parse_args()
args.cache_dir.mkdir(parents=True, exist_ok=True)
split_manifest: dict[str, Any] = {}
split_bytes: dict[str, bytes] = {}
for split in SPLITS:
relative = f"{DATASET_VARIANT}/{split}-00000-of-00001.parquet"
url = (
f"https://huggingface.co/datasets/{DATASET_REPO}/resolve/"
f"{DATASET_REVISION}/{relative}"
)
parquet_path = args.cache_dir / f"{split}.parquet"
download(url, parquet_path)
payload, rows = concatenate_split(parquet_path)
binary_path = args.cache_dir / f"{split}.bin"
if not binary_path.exists() or binary_path.read_bytes() != payload:
temporary = binary_path.with_suffix(".bin.tmp")
temporary.write_bytes(payload)
os.replace(temporary, binary_path)
split_bytes[split] = payload
split_manifest[split] = {
"source_path": relative,
"source_url": url,
"parquet_bytes": parquet_path.stat().st_size,
"parquet_sha256": file_sha256(parquet_path),
"rows": rows,
"concatenated_bytes": len(payload),
"concatenated_sha256": bytes_sha256(payload),
"binary_path": str(binary_path),
"binary_sha256": file_sha256(binary_path),
}
train = split_bytes["train"]
validation = split_bytes["validation"]
schedule_digest = hashlib.sha256()
schedule_cells = 0
for seed in SEEDS:
for step in range(1, FORMAL_STEPS + 1):
for row in range(FORMAL_BATCH):
start = train_window_start(seed, step, row, len(train))
schedule_digest.update(start.to_bytes(8, "big"))
schedule_cells += 1
validation_starts = [
window_start("validation-window", index, len(validation))
for index in range(VALIDATION_WINDOWS)
]
diagnostic_starts = [
window_start("diagnostic-window", index, len(validation))
for index in range(DIAGNOSTIC_WINDOWS)
]
def tensor_hash(starts: list[int]) -> str:
digest = hashlib.sha256()
for start in starts:
digest.update(validation[start : start + CONTEXT + 1])
return digest.hexdigest()
manifest = {
"schema_version": 1,
"protocol_id": PROTOCOL_ID,
"status": "frozen-before-model-output",
"dataset": {
"repository": DATASET_REPO,
"revision": DATASET_REVISION,
"variant": DATASET_VARIANT,
"preprocessing": (
"parquet row order; (text or empty string) + LF; UTF-8; "
"no normalization; vocabulary is raw bytes 0..255"
),
"splits": split_manifest,
},
"windows": {
"context": CONTEXT,
"target_bytes_per_window": CONTEXT,
"seeds": list(SEEDS),
"formal_steps": FORMAL_STEPS,
"formal_batch": FORMAL_BATCH,
"formal_schedule_cells": schedule_cells,
"formal_schedule_sha256": schedule_digest.hexdigest(),
"validation_starts": validation_starts,
"validation_tensor_sha256": tensor_hash(validation_starts),
"diagnostic_starts": diagnostic_starts,
"diagnostic_tensor_sha256": tensor_hash(diagnostic_starts),
},
}
atomic_json(args.manifest, manifest)
print(json.dumps(manifest, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()