Delta-Ultra-Mini / delta /dataset.py
Delta-FlareAI's picture
Upload folder using huggingface_hub
a006332 verified
Raw
History Blame Contribute Delete
3.75 kB
"""Dataset and collator for Delta causal language modeling."""
from __future__ import annotations
import json
import logging
import os
from pathlib import Path
from typing import Any
import torch
from torch.utils.data import Dataset
from delta.tokenizer import DeltaTokenizer
logging.basicConfig(level=os.getenv("DELTA_LOG_LEVEL", "INFO").upper())
logger = logging.getLogger(__name__)
def _read_texts(path: Path) -> list[str]:
"""Read .txt files or .jsonl files containing a text field."""
files = [path] if path.is_file() else sorted(path.rglob("*"))
texts: list[str] = []
for file_path in files:
if file_path.suffix.lower() == ".txt":
texts.append(file_path.read_text(encoding="utf-8"))
elif file_path.suffix.lower() == ".jsonl":
with file_path.open("r", encoding="utf-8") as handle:
for line in handle:
line = line.strip()
if not line:
continue
record = json.loads(line)
if "text" in record:
texts.append(str(record["text"]))
return texts
class DeltaDataset(Dataset[dict[str, torch.Tensor]]):
"""Sliding-window token dataset for language modeling."""
def __init__(
self,
data_path: str | Path,
tokenizer: DeltaTokenizer,
max_seq_len: int = 512,
stride: int = 256,
) -> None:
self.data_path = Path(data_path)
self.tokenizer = tokenizer
self.max_seq_len = max_seq_len
self.stride = stride
texts = _read_texts(self.data_path)
if not texts:
raise ValueError(f"No .txt or .jsonl text records found in {self.data_path}")
self.windows: list[list[int]] = []
for text in texts:
ids = tokenizer.encode(text, add_special_tokens=True)
for start in range(0, max(1, len(ids) - 1), stride):
window = ids[start : start + max_seq_len]
if len(window) >= 2:
self.windows.append(window)
if start + max_seq_len >= len(ids):
break
logger.info("Loaded %s training windows from %s", len(self.windows), self.data_path)
def __len__(self) -> int:
"""Return the number of windows."""
return len(self.windows)
def __getitem__(self, index: int) -> dict[str, torch.Tensor]:
"""Return one token window."""
ids = torch.tensor(self.windows[index], dtype=torch.long)
return {"input_ids": ids, "labels": ids.clone()}
class DeltaDataCollator:
"""Dynamic padding collator for causal language modeling."""
def __init__(self, pad_token_id: int = 0) -> None:
self.pad_token_id = pad_token_id
def __call__(self, features: list[dict[str, torch.Tensor]]) -> dict[str, torch.Tensor]:
"""Pad input ids and labels to the longest sample in the batch."""
max_len = max(feature["input_ids"].size(0) for feature in features)
input_ids = torch.full((len(features), max_len), self.pad_token_id, dtype=torch.long)
labels = torch.full((len(features), max_len), -100, dtype=torch.long)
attention_mask = torch.zeros((len(features), max_len), dtype=torch.long)
for row, feature in enumerate(features):
ids = feature["input_ids"]
length = ids.size(0)
input_ids[row, :length] = ids
labels[row, :length] = feature["labels"]
pad_positions = ids == self.pad_token_id
labels[row, :length][pad_positions] = -100
attention_mask[row, :length] = 1
return {"input_ids": input_ids, "labels": labels, "attention_mask": attention_mask}