make_calib_data.py Source File

make_calib_data.py Source File#

Mobilint SDK qb Compiler: make_calib_data.py Source File
Mobilint SDK qb Compiler v0.11.0.1
MCS002-KR
make_calib_data.py
Go to the documentation of this file.
1
4
5
6from typing import Union, Callable, List, Optional
7from shutil import rmtree
8import os
9import numpy as np
10import random
11from tqdm import tqdm
12from pathlib import Path
13from .preprocess import preproc_builder
14from .utils_calib import (
15 list_np_files_in_txt,
16 list_np_files_in_json,
17 get_balanced_ids_coco,
18 read_structure,
19 make_calib_dict,
20 save_calib_to_msgpack,
21)
22from qubee.utils import get_yaml
23
24
27
28
29def make_calib_man(
30 pre_ftn,
31 data_dir: str,
32 save_dir: str,
33 save_name: str,
34 anno_json: str = None,
35 file_format: str = "%012d.jpg",
36 max_size: int = -1,
37 remove_npy: bool = False,
38 seed: int = 2023,
39 save_calib_msg: bool = False,
40 msg_path: str = None,
41):
42 """
43 @brief From images and a user-provided preprocessing function, generate calibration NumPy files and a manifest.
44
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
59 sample count.
60 @return string. Path to the generated text file that lists the preprocessed NumPy samples.
61
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.
65 """
66 assert isinstance(
67 pre_ftn, Callable
68 ), f"Expected a function for pre_ftn, but got type={type(pre_ftn)}."
69
70 if save_name is None:
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")
74
75 # Remove pre-existing numpy files if remove_npy = True.
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}.")
79 rmtree(save_dir)
80 os.makedirs(save_dir, exist_ok=True)
81 os.makedirs(save_npy_dir, exist_ok=True)
82
83 img_path_list = read_structure(data_dir)
84 fname_list = []
85 npy_list = []
86 ids_list = []
87
88 # Limit maximum size of calibration dataset.
89 random.seed(seed)
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
93 )
94 dataname = "coco"
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"
98 else:
99 dataname = "any_all"
100
101 # Collect samples and make/save numpy files.
102 for img_path in tqdm(img_path_list, desc="Making Numpy File"):
103 out = pre_ftn(img_path)
104 if out is None:
105 continue
106 if not isinstance(out, np.ndarray):
107 raise AssertionError(f"Got unexpected tensor type: {type(out)}.", out)
108
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)
116
117 if save_calib_msg:
118 calib_dict = make_calib_dict(
119 img_path_list, fname_list, npy_list, dataname, ids_list
120 )
121 save_calib_to_msgpack(calib_dict, msg_path)
122
123 # make metadata text for numpy files.
124 list_np_files_in_txt(save_npy_dir, save_txt_dir)
125 return save_txt_dir
126
127
128def make_calib(
129 args_pre: Union[str, dict],
130 data_dir: str,
131 save_dir: str,
132 save_name: str = None,
133 anno_json: str = None,
134 file_format: str = "%012d.jpg",
135 max_size: int = -1,
136 remove_npy: bool = False,
137 seed: int = 2023,
138 save_calib_msg: bool = False,
139 msg_path: str = None,
140) -> str:
141 """
142 @brief Create calibration NumPy files and a manifest using a YAML or dict preprocessing configuration.
143
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
156 omitted.
157 @return string. Path to the manifest text file that lists the generated NumPy samples.
158
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
161 Pre-Order list.
162 """
163 if isinstance(args_pre, str):
164 args_pre = get_yaml(args_pre)
165 elif isinstance(args_pre, dict):
166 pass
167 else:
168 raise TypeError(
169 f"Expected yaml path (str) or cfg dictionary, but got type={type(args_pre)}."
170 )
171
172 pre_process_ftn = preproc_builder(args_pre, to_tensor=False)
173 save_txt_dir = make_calib_man(
174 pre_process_ftn,
175 data_dir,
176 save_dir,
177 save_name,
178 anno_json,
179 file_format,
180 max_size,
181 remove_npy,
182 seed,
183 save_calib_msg,
184 msg_path,
185 )
186 return save_txt_dir
187
188
189def make_calib_llm(
190 args_pre: Union[str, dict],
191 dataset_name: str,
192 subset_list: Union[List[str], str],
193 text_field: str,
194 split: str,
195 save_dir: str,
196 max_calib: int = 512,
197 save_prefix: Optional[str] = None,
198) -> List[str]:
199 """Generate calibration embeddings for LLM models using Hugging Face datasets.
200
201 Args:
202 args_pre: YAML path or preprocessing configuration dictionary. Must set
203 Datatype to "LLM".
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.
212
213 Returns:
214 List of paths to generated calibration txt files, one per subset.
215 """
216
217 from datasets import load_dataset # Delay import to avoid optional dep cost
218
219 if isinstance(args_pre, str):
220 args_pre = get_yaml(args_pre)
221 elif not isinstance(args_pre, dict):
222 raise TypeError(
223 f"Expected yaml path (str) or cfg dictionary, but got type={type(args_pre)}."
224 )
225
226 pre_process_ftn = preproc_builder(args_pre, to_tensor=False)
227 if getattr(pre_process_ftn, "datatype", None) != "LLM":
228 raise ValueError(
229 "LLM calibration requires Datatype='LLM' in preprocessing config."
230 )
231
232 embedding_stage = next(
233 (fnc for fnc in pre_process_ftn.preprocess if hasattr(fnc, "model_name")),
234 None,
235 )
236 model_name = getattr(embedding_stage, "model_name", None)
237 if save_prefix is None:
238 save_prefix = model_name or "llm"
239
240 os.makedirs(save_dir, exist_ok=True)
241 generated_txt_paths: List[str] = []
242
243 if isinstance(subset_list, str):
244 subset_list = [subset_list]
245
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
249 target_dir_name = (
250 f"{save_prefix}-{dataset_name.replace('/', '_')}-{lang_suffix}"
251 )
252 target_dir = os.path.join(save_dir, target_dir_name)
253 os.makedirs(target_dir, exist_ok=True)
254
255 cur_num_calib = 0
256 for idx, sample in enumerate(dataset):
257 if isinstance(sample, dict):
258 if text_field not in sample:
259 raise KeyError(
260 f"Key '{text_field}' not found in dataset sample for subset {subset_name}."
261 )
262 sample_input = sample[text_field]
263 else:
264 sample_input = sample
265
266 try:
267 embedded_text = pre_process_ftn(sample_input)
268 except Exception as err:
269 print(f"Skipping dataset item {idx} due to error: {err}")
270 continue
271
272 if embedded_text is None:
273 continue
274
275 if not isinstance(embedded_text, np.ndarray):
276 raise AssertionError(
277 f"LLM preprocessing should return numpy.ndarray, got {type(embedded_text)}."
278 )
279
280 save_path = os.path.join(target_dir, f"inputs_embeds_{cur_num_calib}.npy")
281 np.save(save_path, embedded_text)
282 cur_num_calib += 1
283
284 if cur_num_calib >= max_calib:
285 break
286
287 if cur_num_calib == 0:
288 print(
289 f"No calibration samples were generated for subset {subset_name}; check preprocessing constraints."
290 )
291 continue
292
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)
296
297 return generated_txt_paths
298
299
300