6from typing
import Union, Callable, List, Optional
7from shutil
import rmtree
12from pathlib
import Path
13from .preprocess
import preproc_builder
14from .utils_calib
import (
16 list_np_files_in_json,
17 get_balanced_ids_coco,
20 save_calib_to_msgpack,
22from qubee.utils
import get_yaml
34 anno_json: str =
None,
35 file_format: str =
"%012d.jpg",
37 remove_npy: bool =
False,
39 save_calib_msg: bool =
False,
43 @brief From images and a user-provided preprocessing function, generate calibration NumPy files and a manifest.
45 @param pre_ftn Callable. Pre-processing function that accepts an image path and returns a NumPy array.
46 @param data_dir string. Directory containing the raw calibration images.
47 @param save_dir string. Directory in which to store the generated NumPy files and metadata text file.
48 @param save_name string. Basename for the saved assets. NumPy files are written to
49 @c {save_dir}/{save_name}_npy and the manifest to @c {save_dir}/{save_name}.txt. Defaults to the basename of
50 @p data_dir when omitted.
51 @param anno_json string. Optional COCO-style annotation file. When provided, samples are randomly selected while
52 maintaining class balance.
53 @param file_format string. Filename format that uses @c image_idx (for example "%012d.jpg").
54 @param max_size int. Maximum number of calibration samples. A value of -1 disables the limit.
55 @param remove_npy bool. Remove existing NumPy outputs before generating new data.
56 @param seed int. Random seed applied to sampling and class-balanced selection. Defaults to 2023.
57 @param save_calib_msg bool. Persist the calibration metadata as an MSGpack file in addition to the manifest.
58 @param msg_path string. Explicit MSGpack output path. When omitted, the path is auto-generated by dataset name and
60 @return string. Path to the generated text file that lists the preprocessed NumPy samples.
62 @note Calibration samples are written as float32 NumPy arrays derived from the @p pre_ftn output. When
63 @p max_size is positive and the dataset is larger, a random subset is used. COCO annotations override this sampling
64 to balance categories.
68 ), f
"Expected a function for pre_ftn, but got type={type(pre_ftn)}."
71 save_name = os.path.basename(data_dir)
72 save_npy_dir = os.path.join(save_dir, f
"{save_name}")
73 save_txt_dir = os.path.join(save_dir, f
"{save_name}.txt")
76 if (remove_npy)
and (os.path.isdir(save_dir))
and (os.path.isdir(save_npy_dir)):
77 tmp_np_list = [x
for x
in os.listdir(save_npy_dir)
if x.endswith(
"npy")]
78 print(f
"There are {len(tmp_np_list)} npy files in {save_npy_dir}.")
80 os.makedirs(save_dir, exist_ok=
True)
81 os.makedirs(save_npy_dir, exist_ok=
True)
83 img_path_list = read_structure(data_dir)
90 if anno_json
is not None:
91 img_path_list, ids_list = get_balanced_ids_coco(
92 anno_json, img_path_list, max_size, fname_format=file_format, seed=seed
95 elif max_size > 0
and len(img_path_list) > max_size:
96 img_path_list = random.sample(img_path_list, max_size)
97 dataname =
"any_sampled"
102 for img_path
in tqdm(img_path_list, desc=
"Making Numpy File"):
103 out = pre_ftn(img_path)
106 if not isinstance(out, np.ndarray):
107 raise AssertionError(f
"Got unexpected tensor type: {type(out)}.", out)
109 save_npy = out.astype(np.float32)
110 fname = os.path.basename(img_path)
111 fname_list.append(fname)
112 npy_list.append(save_npy)
113 img_name = Path(fname).stem
114 save_path = os.path.join(save_npy_dir, img_name +
".npy")
115 np.save(save_path, save_npy)
118 calib_dict = make_calib_dict(
119 img_path_list, fname_list, npy_list, dataname, ids_list
121 save_calib_to_msgpack(calib_dict, msg_path)
124 list_np_files_in_txt(save_npy_dir, save_txt_dir)
129 args_pre: Union[str, dict],
132 save_name: str =
None,
133 anno_json: str =
None,
134 file_format: str =
"%012d.jpg",
136 remove_npy: bool =
False,
138 save_calib_msg: bool =
False,
139 msg_path: str =
None,
142 @brief Create calibration NumPy files and a manifest using a YAML or dict preprocessing configuration.
144 @param args_pre string or dict. Path to a YAML file or an in-memory dictionary describing the preprocessing
145 pipeline. See the preproc_builder class for all supported operators and parameters.
146 @param data_dir string. Directory of calibration images.
147 @param save_dir string. Directory where the generated NumPy files and manifest text file are stored.
148 @param save_name string. Optional basename for outputs. Defaults to the basename of @p data_dir.
149 @param anno_json string. Optional COCO-format annotation file used to sample images with class balance.
150 @param file_format string. Filename template built from @c image_idx (defaults to "%012d.jpg").
151 @param max_size int. Maximum number of calibration samples to generate; -1 keeps all available items.
152 @param remove_npy bool. Remove existing NumPy outputs before running preprocessing.
153 @param seed int. Random seed used for sampling. Defaults to 2023.
154 @param save_calib_msg bool. Save calibration metadata as MSGpack alongside NumPy and text outputs.
155 @param msg_path string. Explicit MSGpack output path. Auto-generated from dataset name and sample count when
157 @return string. Path to the manifest text file that lists the generated NumPy samples.
159 @note The preprocessing pipeline is constructed through @c preproc_builder and executed via @c make_calib_man. Use
160 the YAML schema to combine primitives such as GetImage, Resize, Normalize, Pad, CenterCrop, and SetOrder in a
163 if isinstance(args_pre, str):
164 args_pre = get_yaml(args_pre)
165 elif isinstance(args_pre, dict):
169 f
"Expected yaml path (str) or cfg dictionary, but got type={type(args_pre)}."
172 pre_process_ftn = preproc_builder(args_pre, to_tensor=
False)
173 save_txt_dir = make_calib_man(
190 args_pre: Union[str, dict],
192 subset_list: Union[List[str], str],
196 max_calib: int = 512,
197 save_prefix: Optional[str] =
None,
199 """Generate calibration embeddings for LLM models using Hugging Face datasets.
202 args_pre: YAML path or preprocessing configuration dictionary. Must set
204 dataset_name: Name of the Hugging Face dataset to load.
205 subset_list: List of dataset subsets (e.g. language splits) to iterate.
206 split: HF dataset split to use.
207 text_field: Key containing text in dataset entries.
208 save_dir: Root directory where calibration samples are stored.
209 max_calib: Maximum number of calibration samples per subset.
210 save_prefix: Override prefix used in output directory naming. If None,
211 derives from tokenizer directory name when available.
214 List of paths to generated calibration txt files, one per subset.
217 from datasets
import load_dataset
219 if isinstance(args_pre, str):
220 args_pre = get_yaml(args_pre)
221 elif not isinstance(args_pre, dict):
223 f
"Expected yaml path (str) or cfg dictionary, but got type={type(args_pre)}."
226 pre_process_ftn = preproc_builder(args_pre, to_tensor=
False)
227 if getattr(pre_process_ftn,
"datatype",
None) !=
"LLM":
229 "LLM calibration requires Datatype='LLM' in preprocessing config."
232 embedding_stage = next(
233 (fnc
for fnc
in pre_process_ftn.preprocess
if hasattr(fnc,
"model_name")),
236 model_name = getattr(embedding_stage,
"model_name",
None)
237 if save_prefix
is None:
238 save_prefix = model_name
or "llm"
240 os.makedirs(save_dir, exist_ok=
True)
241 generated_txt_paths: List[str] = []
243 if isinstance(subset_list, str):
244 subset_list = [subset_list]
246 for subset_name
in subset_list:
247 dataset = load_dataset(dataset_name, subset_name, split=split)
248 lang_suffix = subset_name.split(
".")[-1]
if "." in subset_name
else subset_name
250 f
"{save_prefix}-{dataset_name.replace('/', '_')}-{lang_suffix}"
252 target_dir = os.path.join(save_dir, target_dir_name)
253 os.makedirs(target_dir, exist_ok=
True)
256 for idx, sample
in enumerate(dataset):
257 if isinstance(sample, dict):
258 if text_field
not in sample:
260 f
"Key '{text_field}' not found in dataset sample for subset {subset_name}."
262 sample_input = sample[text_field]
264 sample_input = sample
267 embedded_text = pre_process_ftn(sample_input)
268 except Exception
as err:
269 print(f
"Skipping dataset item {idx} due to error: {err}")
272 if embedded_text
is None:
275 if not isinstance(embedded_text, np.ndarray):
276 raise AssertionError(
277 f
"LLM preprocessing should return numpy.ndarray, got {type(embedded_text)}."
280 save_path = os.path.join(target_dir, f
"inputs_embeds_{cur_num_calib}.npy")
281 np.save(save_path, embedded_text)
284 if cur_num_calib >= max_calib:
287 if cur_num_calib == 0:
289 f
"No calibration samples were generated for subset {subset_name}; check preprocessing constraints."
293 txt_path = os.path.join(save_dir, f
"{target_dir_name}.txt")
294 list_np_files_in_txt(target_dir, txt_path)
295 generated_txt_paths.append(txt_path)
297 return generated_txt_paths