Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / data / preset_pt.py: 23%
79 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-13 05:07 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-13 05:07 +0800
1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""``preset_pt`` dataset for replaying pre-tokenized ``.pt`` batches.
17Each entry is either a stacked batch dict ``{key: (B, S)-Tensor}`` or a list
18of per-rank dicts. The loader flattens both forms into per-sample rows so the
19standard ``DataLoader`` can batch them again with the trainer collator.
20"""
21import logging
22from typing import Any, Dict, List
24import torch
25from torch.utils.data import Dataset
27from hyper_parallel.data.registry import DATASET_REGISTRY
30logger = logging.getLogger(__name__)
33class PresetPtDataset(Dataset):
34 """Wrap a pre-expanded list of per-sample dicts."""
36 def __init__(self, samples: List[Dict[str, Any]]) -> None:
37 self.samples = samples
39 def __len__(self) -> int:
40 return len(self.samples)
42 def __getitem__(self, idx: int) -> Dict[str, Any]:
43 return self.samples[idx]
46_OPTIONAL_2D_FIELDS = ("attention_mask", "mm_token_type_ids")
48_PIXEL_PAIRS = (("pixel_values", "image_grid_thw"),
49 ("pixel_values_videos", "video_grid_thw"))
52def _split_pixel_block(
53 b: Dict[str, Any], i: int, batch_size: int, pix_key: str, grid_key: str,
54) -> Dict[str, Any]:
55 """Slice the ``(pix_key, grid_key)`` rows owned by sample ``i``."""
56 pv = b[pix_key]
57 thw = b[grid_key]
58 grids_per_sample = thw.shape[0] // batch_size if thw.dim() == 2 else 0
59 if grids_per_sample == 0:
60 return {}
61 thw_i = thw[i * grids_per_sample:(i + 1) * grids_per_sample].clone()
62 pv_count = int(thw_i.prod(dim=-1).sum().item())
63 offset = sum(
64 int(thw[j].prod(dim=-1).sum().item())
65 for j in range(i * grids_per_sample)
66 )
67 return {
68 pix_key: pv[offset:offset + pv_count].clone(),
69 grid_key: thw_i,
70 }
73def _slice_position_ids(position_ids: Any, sample_idx: int, batch_size: int) -> Any:
74 """Return one sample's position ids from an LF/HF stacked batch.
76 Text Qwen3.5 batches may carry either ``[B, S]`` plain position ids or
77 mRoPE ``[R, B, S]`` ids (``R`` is 3 or 4 in Transformers). Preserve the
78 rotary-rank axis and slice only the batch axis so the trainer can rebuild
79 the original stacked shape in its collate function.
80 """
81 if position_ids.dim() == 2 and position_ids.shape[0] == batch_size:
82 return position_ids[sample_idx].clone()
83 if position_ids.dim() == 3 and position_ids.shape[1] == batch_size:
84 return position_ids[:, sample_idx].clone()
85 if position_ids.dim() == 3 and position_ids.shape[0] == batch_size:
86 return position_ids[sample_idx].clone()
87 raise ValueError(
88 "preset_pt position_ids must be [B, S], [R, B, S], or [B, R, S]; "
89 f"got shape={tuple(position_ids.shape)} with batch_size={batch_size}."
90 )
93def _expand_batch(b: Dict[str, Any], *, vl: bool) -> List[Dict[str, Any]]:
94 """Split a stacked LM/VL batch dict into per-sample dicts.
96 LM samples carry only ``input_ids`` / ``labels`` (plus optionally
97 ``attention_mask``). VL samples additionally carry ``mm_token_type_ids``
98 and ``(pixel_values, image_grid_thw)`` / ``(pixel_values_videos,
99 video_grid_thw)`` pairs sliced according to the per-sample grid product.
100 """
101 ids = b["input_ids"]
102 labels = b["labels"]
103 batch_size = ids.shape[0]
104 out: List[Dict[str, Any]] = []
105 for i in range(batch_size):
106 rec: Dict[str, Any] = {
107 "input_ids": ids[i].clone(),
108 "labels": labels[i].clone(),
109 }
110 if "num_items_in_batch" in b:
111 rec["num_items_in_batch"] = int((rec["labels"] != -100).sum().item())
112 for k in _OPTIONAL_2D_FIELDS:
113 v = b.get(k)
114 if v is not None and v.dim() == 2:
115 rec[k] = v[i].clone()
116 position_ids = b.get("position_ids")
117 if position_ids is not None:
118 rec["position_ids"] = _slice_position_ids(position_ids, i, batch_size)
119 if vl:
120 for pix_key, grid_key in _PIXEL_PAIRS:
121 if b.get(pix_key) is not None and b.get(grid_key) is not None:
122 rec.update(_split_pixel_block(b, i, batch_size, pix_key, grid_key))
123 out.append(rec)
124 return out
127def _is_vl(batch_entry: Any) -> bool:
128 """Heuristic: VL batches always carry pixel data (image or video)."""
129 if isinstance(batch_entry, list):
130 batch_entry = batch_entry[0] if batch_entry else None
131 return isinstance(batch_entry, dict) and any(
132 pix_key in batch_entry for pix_key, _ in _PIXEL_PAIRS)
135@DATASET_REGISTRY.register("preset_pt")
136def build_preset_pt(*, base: Any, args: Any, **_: Any) -> PresetPtDataset:
137 """Build the preset replay dataset.
139 Auto-detects whether the .pt holds VL batches (pixel_values present)
140 or plain LM batches by inspecting the first entry, and dispatches the
141 matching per-sample expansion.
142 """
143 train_path = args.data.train_path
144 if not train_path:
145 raise ValueError("data.train_path is required when data.type='preset_pt'")
146 batches = torch.load(train_path, map_location="cpu", weights_only=False)
147 if not isinstance(batches, list) or not batches:
148 raise ValueError(f"preset_pt expects List, got {type(batches)}")
150 vl = _is_vl(batches[0])
151 per_sample: List[Dict[str, Any]] = []
152 for b in batches:
153 if isinstance(b, list):
154 for br in b:
155 per_sample.extend(_expand_batch(br, vl=vl))
156 else:
157 per_sample.extend(_expand_batch(b, vl=vl))
159 ds = PresetPtDataset(per_sample)
160 if args.train.max_steps:
161 base.state.max_steps = int(args.train.max_steps)
162 logger.info(
163 "preset_pt dataset (%s): %d samples loaded from %s",
164 "vl" if vl else "lm", len(per_sample), train_path,
165 )
166 return ds