jacmor64's picture
Deploy Ares Static Lab Colab training pipeline
8fa3dd6 verified
Raw
History Blame Contribute Delete
3.98 kB
from __future__ import annotations
import json
from pathlib import Path
from typing import Iterable, Iterator, List, Sequence
def read_text_records(paths: Sequence[str]) -> Iterator[str]:
"""Yield text records from txt/md/json/jsonl files or directories."""
files: List[Path] = []
for item in paths:
p = Path(item)
if p.is_dir():
files.extend([x for x in sorted(p.rglob("*")) if x.is_file()])
elif p.is_file():
files.append(p)
else:
raise FileNotFoundError(item)
for p in files:
suffix = p.suffix.lower()
if suffix in {".txt", ".md"}:
text = p.read_text(encoding="utf-8", errors="ignore").strip()
if text:
yield text
elif suffix == ".jsonl":
with p.open("r", encoding="utf-8", errors="ignore") as f:
for line in f:
line = line.strip()
if not line:
continue
obj = json.loads(line)
yield extract_text_from_json(obj)
elif suffix == ".json":
obj = json.loads(p.read_text(encoding="utf-8", errors="ignore"))
if isinstance(obj, list):
for row in obj:
yield extract_text_from_json(row)
else:
yield extract_text_from_json(obj)
def extract_text_from_json(obj) -> str:
if isinstance(obj, str):
return obj
if isinstance(obj, dict):
if "text" in obj:
return str(obj["text"])
if "content" in obj:
return str(obj["content"])
if "messages" in obj:
return format_messages(obj["messages"])
# Conservative fallback: concatenate scalar values.
parts = []
for value in obj.values():
if isinstance(value, (str, int, float)):
parts.append(str(value))
return "\n".join(parts)
return str(obj)
def format_messages(messages) -> str:
parts = []
for msg in messages:
role = msg.get("role", "user")
content = msg.get("content", "")
parts.append(f"<|{role}|>\n{content}\n<|end|>")
return "\n".join(parts)
class PackedTokenDataset:
"""Simple in-memory packed causal-LM dataset.
For serious training, replace this with streaming shards / mmap arrays. This class is intentionally
small and readable for the first Ares smoke tests.
"""
def __init__(self, token_ids: Sequence[int], seq_len: int):
if len(token_ids) < seq_len + 1:
raise ValueError("Not enough tokens for one training example")
self.token_ids = list(map(int, token_ids))
self.seq_len = int(seq_len)
self.n = (len(self.token_ids) - 1) // self.seq_len
def __len__(self) -> int:
return self.n
def __getitem__(self, idx: int):
import torch
start = (idx % self.n) * self.seq_len
chunk = self.token_ids[start : start + self.seq_len + 1]
x = torch.tensor(chunk[:-1], dtype=torch.long)
y = torch.tensor(chunk[1:], dtype=torch.long)
return x, y
def encode_corpus(tokenizer_path: str, text_paths: Sequence[str]) -> List[int]:
try:
from tokenizers import Tokenizer
except ImportError as exc:
raise SystemExit("Install tokenizers first: pip install tokenizers") from exc
tok = Tokenizer.from_file(tokenizer_path)
ids: List[int] = []
for record in read_text_records(text_paths):
enc = tok.encode(record)
ids.extend(enc.ids)
return ids
def make_dataloader(tokenizer_path: str, text_paths: Sequence[str], seq_len: int, batch_size: int, shuffle: bool = True):
import torch
from torch.utils.data import DataLoader
ids = encode_corpus(tokenizer_path, text_paths)
dataset = PackedTokenDataset(ids, seq_len)
return DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, drop_last=True)