Download source/src/speculators/train/dataloader.py from khazic/spec-b300: direct link, hf CLI and curl.
- Browser
- Download file 8.07 kB
-
https://huggingface.co/khazic/spec-b300/resolve/main/source/src/speculators/train/dataloader.py
- Command line
-
hf download hf://khazic/spec-b300/source/src/speculators/train/dataloader.py
-
curl -L -o dataloader.py https://huggingface.co/khazic/spec-b300/resolve/main/source/src/speculators/train/dataloader.py
8.07 kB
| from __future__ import annotations | |
| import copy | |
| import logging | |
| import os | |
| from typing import TYPE_CHECKING, Any, Literal | |
| if TYPE_CHECKING: | |
| from collections.abc import Callable | |
| import torch | |
| from torch.utils.data import DataLoader | |
| from hs_connectors import HiddenStatesTransfer | |
| from speculators.train.data import ( | |
| ArrowDataset, | |
| BaseDataset, | |
| CollateFn, | |
| ) | |
| from speculators.train.distributed import get_dp_rank, get_dp_size | |
| from speculators.train.distributed_batch_sampler import ( | |
| MultipackDistributedBatchSamplerV2, | |
| ) | |
| from speculators.train.noise_transforms import AddUniformNoise | |
| logger = logging.getLogger(__name__) | |
| BatchType = dict[str, Any] | |
| def _limit_worker_threads() -> None: | |
| """Limit per-worker thread pools to avoid thread exhaustion. | |
| With ``multiprocessing_context='spawn'``, each worker is a full process | |
| that re-imports numpy (OpenBLAS) and torch, each creating thread pools | |
| sized to the core count. DataLoader workers only do I/O and tensor | |
| slicing — they don't benefit from intra-op parallelism. | |
| The env vars must be set before numpy/torch are imported to take effect | |
| on OpenBLAS/OMP. Call this at the top of the training entry point, | |
| before DataLoader construction. | |
| """ | |
| os.environ.setdefault("OPENBLAS_NUM_THREADS", "1") | |
| os.environ.setdefault("OMP_NUM_THREADS", "1") | |
| os.environ.setdefault("MKL_NUM_THREADS", "1") | |
| def _worker_init_fn(worker_id: int) -> None: # noqa: ARG001 | |
| torch.set_num_threads(1) | |
| def _make_worker_dataset(dataset: BaseDataset) -> BaseDataset: | |
| """Drop parent-only sampler state from the dataset sent to spawned workers. | |
| ``approx_lengths`` can contain hundreds of thousands of Python integers. The | |
| batch sampler copies it into its own NumPy array before worker startup, and | |
| ``Dataset.__getitem__`` never reads it. Avoiding that field in every spawn pickle | |
| keeps the parent from blocking while each child imports the training stack. | |
| """ | |
| worker_dataset = copy.copy(dataset) | |
| worker_dataset.approx_lengths = [] | |
| return worker_dataset | |
| def _setup_dataloader( | |
| dataset: BaseDataset, | |
| total_seq_len: int, | |
| hidden_size: int, | |
| num_workers: int = 12, | |
| num_target_layers: int = 3, | |
| prefetch_factor: int | None = 4, | |
| preprocess: Callable[[BatchType], BatchType] | None = None, | |
| in_order: bool = True, | |
| max_batches: int | None = None, | |
| ) -> DataLoader: | |
| batch_sampler = MultipackDistributedBatchSamplerV2( | |
| batch_max_length=total_seq_len, | |
| lengths=dataset.approx_lengths, | |
| num_replicas=get_dp_size(), | |
| rank=get_dp_rank(), | |
| max_batches=max_batches, | |
| ) | |
| use_workers = num_workers > 0 | |
| if not in_order and not use_workers: | |
| # Ordering is a property of the multi-worker result queue; with a single | |
| # process there is nothing to reorder, so silently ignoring the flag would | |
| # hide a misconfigured run behind unchanged (slow) behaviour. | |
| raise ValueError("in_order=False requires num_workers > 0") | |
| worker_dataset = _make_worker_dataset(dataset) if use_workers else dataset | |
| return DataLoader( | |
| worker_dataset, | |
| batch_sampler=batch_sampler, | |
| num_workers=num_workers, | |
| prefetch_factor=prefetch_factor if use_workers else None, | |
| pin_memory=True, | |
| collate_fn=CollateFn( | |
| total_seq_len, | |
| hidden_size, | |
| num_target_layers=num_target_layers, | |
| dtype=dataset.hidden_states_dtype, | |
| preprocess=preprocess, | |
| ), | |
| persistent_workers=use_workers, | |
| multiprocessing_context="spawn" if use_workers else None, | |
| worker_init_fn=_worker_init_fn if use_workers else None, | |
| # Online hidden-state fetches are heavy-tailed, so the strict round-robin | |
| # of in-order delivery makes the rank wait on worker i even when workers | |
| # i+1.. already hold finished batches -- and every peer rank then waits on | |
| # this one at the all-reduce. Out-of-order delivery lets the prefetch queue | |
| # absorb the tail instead of serialising on it. The epoch still covers the | |
| # same batches; only mid-epoch resume degrades, since its fast-skip slices | |
| # the sampler by count and no longer names the exact batches consumed. | |
| in_order=in_order or not use_workers, | |
| ) | |
| def create_train_val_loaders( | |
| *, | |
| data_path: str, | |
| total_seq_len: int, | |
| hidden_states_dtype: torch.dtype, | |
| noise_std: float, | |
| transfer: HiddenStatesTransfer | None = None, | |
| vllm_endpoint: str, | |
| on_missing: Literal["generate", "skip", "warn", "raise"], | |
| on_generate: Literal["cache", "delete"], | |
| verifier_name_or_path: str, | |
| request_timeout: float | None, | |
| max_retries: int, | |
| generation_validation_retries: int, | |
| max_consecutive_generation_failures: int, | |
| hidden_size: int, | |
| num_target_layers: int, | |
| num_workers: int, | |
| prefetch_factor: int, | |
| preprocess: Callable[[BatchType], BatchType] | None, | |
| train_data_ratio: float = 0.9, | |
| fail_on_hidden_state_error: bool = False, | |
| fetch_threads: int = 1, | |
| dataloader_in_order: bool = True, | |
| vllm_http_keepalive: bool = True, | |
| max_train_batches: int | None = None, | |
| ) -> tuple[DataLoader, DataLoader]: | |
| """Create training and validation DataLoaders. | |
| Non-data SP ranks get lightweight loaders with no workers (they receive | |
| batches via scatter). Reads DP/SP topology from | |
| :mod:`speculators.train.distributed`. | |
| """ | |
| _limit_worker_threads() | |
| noise_transform = AddUniformNoise(std=noise_std) | |
| if not (0.0 < train_data_ratio < 1.0): | |
| raise ValueError(f"train_data_ratio must be in (0, 1), got {train_data_ratio}") | |
| train_dataset: BaseDataset = ArrowDataset( | |
| datapath=data_path, | |
| max_len=total_seq_len, | |
| transfer=transfer, | |
| vllm_endpoint=vllm_endpoint, | |
| on_missing=on_missing, | |
| on_generate=on_generate, | |
| transform=noise_transform, | |
| train_ratio=train_data_ratio, | |
| split="train", | |
| model=verifier_name_or_path, | |
| hidden_states_dtype=hidden_states_dtype, | |
| request_timeout=request_timeout, | |
| max_retries=max_retries, | |
| fail_on_hidden_state_error=fail_on_hidden_state_error, | |
| fetch_threads=fetch_threads, | |
| http_keepalive=vllm_http_keepalive, | |
| generation_validation_retries=generation_validation_retries, | |
| max_consecutive_generation_failures=max_consecutive_generation_failures, | |
| ) | |
| val_dataset: BaseDataset = ArrowDataset( | |
| datapath=data_path, | |
| max_len=total_seq_len, | |
| transfer=transfer, | |
| vllm_endpoint=vllm_endpoint, | |
| on_missing=on_missing, | |
| on_generate=on_generate, | |
| train_ratio=train_data_ratio, | |
| split="val", | |
| model=verifier_name_or_path, | |
| hidden_states_dtype=hidden_states_dtype, | |
| request_timeout=request_timeout, | |
| max_retries=max_retries, | |
| fail_on_hidden_state_error=fail_on_hidden_state_error, | |
| fetch_threads=fetch_threads, | |
| http_keepalive=vllm_http_keepalive, | |
| generation_validation_retries=generation_validation_retries, | |
| max_consecutive_generation_failures=max_consecutive_generation_failures, | |
| ) | |
| train_loader = _setup_dataloader( | |
| train_dataset, | |
| total_seq_len, | |
| hidden_size, | |
| num_target_layers=num_target_layers, | |
| num_workers=num_workers, | |
| prefetch_factor=prefetch_factor, | |
| preprocess=preprocess, | |
| in_order=dataloader_in_order, | |
| max_batches=max_train_batches, | |
| ) | |
| # Validation reduces a mean over the whole split, so its batch order is | |
| # irrelevant too and it sees the same heavy-tailed fetch latency. | |
| val_loader = _setup_dataloader( | |
| val_dataset, | |
| total_seq_len, | |
| hidden_size, | |
| num_target_layers=num_target_layers, | |
| num_workers=num_workers, | |
| prefetch_factor=prefetch_factor, | |
| preprocess=preprocess, | |
| in_order=dataloader_in_order, | |
| ) | |
| return train_loader, val_loader | |