model.py Source File

model.py Source File#

SDK qb Runtime Library: model.py Source File
SDK qb Runtime Library v1.4
MCS001-EN
model.py
Go to the documentation of this file.
1
4
5from typing import List, Optional, Tuple, Union
6
7import numpy as np
8
9import qbruntime.qbruntime as _cQbRuntime
10from .accelerator import Accelerator
11from .future import *
12from .model_variant_handle import *
13from .npu_data import NPUData
14from .pinned_memory import PinnedMemory
15from .type import *
16
17_Shape = Tuple[int, ...]
18
19__all__ = ["Model", "load"]
20
21
24
25
26# input ndarray의 shape이 유효한 shape인지 판별한다.
27def _is_valid_shape(input_shape: _Shape, shape: _Shape) -> bool:
28 if (len(input_shape) < len(shape)) or (len(input_shape) > len(shape) + 1):
29 return False
30 # input을 batch일 경우도 고려하여 [h, w, c] 및 [batch, h, w, c] 모두 고려한다
31 offset = 1 if len(input_shape) > len(shape) else 0
32 for s1, s2 in zip(input_shape[offset:], shape):
33 # Dimensions that allow variable lengths are represented by negative values.
34 # A variable-length dimension only permits multiples of the original value.
35 if s1 % s2 != 0 or (s2 > 0 and s1 != s2):
36 return False
37 return True
38
39
40# input ndarray의 shape를 검사하여 HWC인지 CHW인지 판별한다. HWC/CHW의
41# shape이 동일한 경우, `is_hwc`와 `is_chw`를 모두 true로 반환한다.
42def _find_memory_format(
43 inputs: List[np.ndarray], shapes: List[_Shape]
44) -> Optional[Tuple[bool, bool]]:
45 if len(inputs) != len(shapes):
46 return None
47
48 is_hwc = True
49 is_chw = True
50 for arr, shape in zip(inputs, shapes):
51 shape_hwc = (shape[0], shape[1], shape[2])
52 shape_chw = (shape[2], shape[0], shape[1])
53 is_hwc = is_hwc and _is_valid_shape(arr.shape, shape_hwc)
54 is_chw = is_chw and _is_valid_shape(arr.shape, shape_chw)
55
56 if not is_hwc and not is_chw:
57 return None
58 return is_hwc, is_chw
59
60
61# input ndarray에 맞는 model variant index와 shape를 판별한다.
62def _find_matching_variant_idx_and_memory_format(
63 model, inputs: List[np.ndarray]
64) -> Tuple[int, Tuple[bool, bool]]:
65 variant_idx = None
66 is_hwc = None
67 is_chw = None
68 for i in range(model.get_num_model_variants()):
69 res = _find_memory_format(
70 inputs, model.get_model_variant_handle(i).get_model_input_shape()
71 )
72 if res is not None:
73 variant_idx = i
74 is_hwc, is_chw = res
75 break
76
77 if variant_idx is None:
78 raise ValueError("Input shape is invalid.")
79 return variant_idx, (is_hwc, is_chw)
80
81
82# shape에 맞게 numpy ndarray를 생성한다.
83def _build_outputs(
84 shapes: List[_Shape], is_hwc: bool, dtype: np.dtype
85) -> List[np.ndarray]:
86 outputs = []
87 for shape in shapes:
88 if is_hwc:
89 shape = (shape[0], shape[1], shape[2])
90 else:
91 shape = (shape[2], shape[0], shape[1])
92 outputs.append(np.empty(shape, dtype=dtype))
93 return outputs
94
95
96# output에 들어있는 numpy ndarray의 shape가 올바른지 검사한다.
97def _check_output_shapes(
98 outputs: List[np.ndarray], shapes: List[_Shape], is_hwc: bool, dtype: np.dtype
99) -> None:
100 if len(outputs) != len(shapes):
101 raise ValueError("The number of outputs is different.")
102
103 for output, shape in zip(outputs, shapes):
104 if output.dtype != dtype:
105 raise ValueError("Output dtype mismatch.")
106
107 if is_hwc:
108 shape = (shape[0], shape[1], shape[2])
109 else:
110 shape = (shape[2], shape[0], shape[1])
111 if output.shape != shape:
112 raise ValueError("Output shape mismatch.")
113
114
115class Model:
116 """
117 @brief Represents an AI model loaded from an MXQ file.
118
119 This class loads an AI model from an MXQ file and provides functions to launch it
120 on the NPU and perform inference.
121 """
122
123 def __init__(self, path: str, model_config: Optional[ModelConfig] = None):
124 """
125 @brief Creates a Model object from the specified MXQ model file and configuration.
126
127 Parses the MXQ file and constructs a Model object using the provided configuration,
128 initializing the model with the given settings.
129
130 @note The created Model object must be launched before performing inference.
131 See Model.launch for more details.
132
133 @param[in] path The path to the MXQ model file.
134 @param[in] model_config The configuration settings to initialize the Model.
135 """
136 if model_config is None:
137 self._model = _cQbRuntime.Model(path)
138 else:
139 self._model = _cQbRuntime.Model(path, model_config._model_config)
140
141 # 기존 BufferInfo 대신에 ModelShape를 사용한다.
142 # Model {input,output} shape는 batch를 포함한 4D이다.
145
146 def launch(self, acc: Accelerator) -> None:
147 """
148 @brief Launches the model on the specified Accelerator, which represents
149 the actual NPU.
150
151 @param[in] acc The accelerator on which to launch the model.
152 """
153 self._model.launch(acc._accelerator)
154 self._acc = acc
155
156 def dispose(self) -> None:
157 """
158 @brief Disposes of the model loaded onto the NPU.
159
160 Releases any resources associated with the model on the NPU.
161 """
162 self._model.dispose()
163 self._acc = None
164
165 def is_target(self, core_id: CoreId) -> bool:
166 """
167 @brief Checks if the NPU core specified by CoreId is the target of the model.
168 In other words, whether the model is configured to use the given NPU core.
169
170 @param[in] core_id The CoreId to check.
171 @return True if the model is configured to use the specified CoreId, false
172 otherwise.
173 """
174 return self._model.is_target(core_id._core_id)
175
176 def get_core_mode(self) -> CoreMode:
177 """
178 @brief Retrieves the core mode of the model.
179
180 @return The CoreMode of the model.
181 """
182 return CoreMode(self._model.get_core_mode())
183
184 def get_device_names(self) -> List[str]:
185 """
186 @brief Returns the supported target device name(s) this model can run on.
187
188 Returns device names from the model's target device in the MXQ file. A single
189 model may map to multiple names (e.g. a REGULUS model returns both SoC and
190 USB devices). Any of the returned names can be passed to `Accelerator(device_name=)`.
191
192 @return A list of supported target device names.
193 """
194 return self._model.get_device_names()
195
196 def get_target_cores(self) -> List[CoreId]:
197 """
198 @brief Returns the NPU cores the model is configured to use.
199
200 @return A list of CoreIds representing the target NPU cores.
201 """
202 return [CoreId.from_cpp(target) for target in self._model.target_cores]
203
204 @property
205 def target_cores(self) -> List[CoreId]:
206 """@deprecated"""
207 return [CoreId.from_cpp(target) for target in self._model.target_cores]
208
209 def infer(
210 self,
211 inputs: Union[np.ndarray, List[np.ndarray]],
212 outputs: Optional[List[np.ndarray]] = None,
213 cache_size: int = 0,
214 params: Optional[List[BatchParam]] = None,
215 ) -> Optional[List[np.ndarray]]:
216 """
217 @brief Performs inference.
218
219 Fowllowing types of inference supported.
220 1. infer(in:List[numpy]) -> List[numpy] (float / int)
221 2. infer(in:numpy) -> List[numpy] (float / int)
222 3. infer(in:List[numpy], out:List[numpy]) (float / int)
223 4. infer(in:List[numpy], out:List[]) (float / int)
224 5. infer(in:numpy, out:List[numpy]) (float / int)
225 6. infer(in:numpy, out:List[]) (float / int)
226
227 @param[in] inputs Input data as a single numpy.ndarray or a list
228 of numpy.ndarray's.
229 @param[out] outputs Optional pre-allocated list of numpy.ndarray's
230 to store inference results.
231 @param[in] cache_size The number of tokens accumulated in the KV cache so far.
232 @param[in] params A List of `BatchParam`, specifying each batch's information
233 for BatchLLM inference. If `params` is specified,
234 `cache_size` is ignored.
235 @return Inference results as a list of numpy.ndarray.
236 """
237 return self._infer(inputs, outputs, cache_size, params=params)
238
239 def infer_hwc(
240 self,
241 inputs: Union[np.ndarray, List[np.ndarray]],
242 outputs: Optional[List[np.ndarray]] = None,
243 cache_size: int = 0,
244 params: Optional[List[BatchParam]] = None,
245 ) -> Optional[List[np.ndarray]]:
246 return self._infer(inputs, outputs, cache_size, True, params)
247
248 def infer_chw(
249 self,
250 inputs: Union[np.ndarray, List[np.ndarray]],
251 outputs: Optional[List[np.ndarray]] = None,
252 cache_size: int = 0,
253 params: Optional[List[BatchParam]] = None,
254 ) -> Optional[List[np.ndarray]]:
255 return self._infer(inputs, outputs, cache_size, False, params)
256
257 def _infer(
258 self,
259 inputs: Union[np.ndarray, List[np.ndarray]],
260 outputs: Optional[List[np.ndarray]],
261 cache_size: int,
262 is_target_hwc: Optional[bool] = None,
263 params: Optional[List[BatchParam]] = None,
264 ) -> Optional[List[np.ndarray]]:
265 if not isinstance(inputs, list):
266 inputs = [inputs]
267
268 variant_idx, (is_hwc, is_chw) = _find_matching_variant_idx_and_memory_format(
269 self, inputs
270 )
271 if (is_target_hwc is not None) and (
272 (is_target_hwc != is_hwc) and (is_target_hwc == is_chw)
273 ):
274 raise ValueError("Input shape is invalid.")
275 elif is_target_hwc is None:
276 is_target_hwc = is_hwc
277 inputs = [np.ascontiguousarray(i) for i in inputs]
278
279 infer_func = self._model.infer if is_target_hwc else self._model.infer_chw
280 if outputs is None:
281 # No Output Parameter
282 if params == None:
283 return [np.asarray(o) for o in infer_func(inputs, cache_size)]
284 else:
285 return [
286 np.asarray(o)
287 for o in infer_func(
288 inputs, [param._batch_param for param in params]
289 )
290 ]
291 else:
292 if outputs:
293 _check_output_shapes(
294 outputs,
296 is_target_hwc,
297 inputs[0].dtype,
298 )
299 for oi in range(len(outputs)):
300 outputs[oi] = np.ascontiguousarray(outputs[oi])
301 else:
302 outputs[:] = _build_outputs(
304 is_target_hwc,
305 inputs[0].dtype,
306 )
307
308 if params == None:
309 infer_func(inputs, outputs, cache_size)
310 else:
311 infer_func(inputs, outputs, [param._batch_param for param in params])
312
314 self,
315 inputs: Union[
316 np.ndarray,
317 List[np.ndarray],
318 ],
319 cache_size: int = 0,
320 ) -> List[np.ndarray]:
321 """
322 @brief int8_t-to-float inference
323 Performs inference with input and output elements of type `int8_t`
324
325 Using these inference APIs requires manual scaling (quantization)
326 of float values to `int8_t` for input.
327
328 @note These APIs are intended for advanced use rather than typical usage.
329 """
330 return self._infer_to_float(inputs, cache_size)
331
332 def infer_hwc_to_float(
333 self,
334 inputs: Union[
335 np.ndarray,
336 List[np.ndarray],
337 ],
338 cache_size: int = 0,
339 ) -> List[np.ndarray]:
340 return self._infer_to_float(inputs, cache_size, True)
341
342 def infer_chw_to_float(
343 self,
344 inputs: Union[
345 np.ndarray,
346 List[np.ndarray],
347 ],
348 cache_size: int = 0,
349 ) -> List[np.ndarray]:
350 return self._infer_to_float(inputs, cache_size, False)
351
353 self,
354 inputs: Union[
355 np.ndarray,
356 List[np.ndarray],
357 ],
358 cache_size: int,
359 is_target_hwc: Optional[bool] = None,
360 ) -> List[np.ndarray]:
361 """
362 @brief int8_t-to-float inference
363 Performs inference with input and output elements of type `int8_t`
364
365 Using these inference APIs requires manual scaling (quantization)
366 of float values to `int8_t` for input.
367
368 @note These APIs are intended for advanced use rather than typical usage.
369 """
370 if not isinstance(inputs, list):
371 inputs = [inputs]
372
373 _, (is_hwc, is_chw) = _find_matching_variant_idx_and_memory_format(self, inputs)
374 if (is_target_hwc is not None) and (
375 (is_target_hwc != is_hwc) and (is_target_hwc == is_chw)
376 ):
377 raise ValueError("Input shape is invalid.")
378 elif is_target_hwc is None:
379 is_target_hwc = is_hwc
380 inputs = [np.ascontiguousarray(i) for i in inputs]
381
382 if is_target_hwc:
383 outputs = self._model.infer_to_float(inputs, cache_size)
384 else:
385 outputs = self._model.infer_chw_to_float(inputs, cache_size)
386
387 return [np.asarray(o) for o in outputs]
388
390 self,
391 inputs: List[Buffer],
392 outputs: List[Buffer],
393 shape: List[List[int]] = [],
394 cache_size: int = 0,
395 ) -> None:
396 """
397 @brief Buffer-to-Buffer inference
398
399 Performs inference using input and output elements in the NPU’s internal data type.
400 The inference operates on buffers allocated via the following APIs:
401
402 - `Model.acquire_input_buffer()`
403 - `Model.acquire_output_buffer()`
404 - `ModelVariantHandle.acquire_input_buffer()`
405 - `ModelVariantHandle.acquire_output_buffer()`
406
407 Additionally, `Model.reposition_inputs()`, `Model.reposition_outputs()`,
408 `ModelVariantHandle.reposition_inputs()`, `ModelVariantHandle.reposition_outputs()`
409 must be used properly.
410
411 @note These APIs are intended for advanced use rather than typical usage.
412 """
413 self._model.infer_buffer(
414 [i._buffer for i in inputs], [o._buffer for o in outputs], shape, cache_size
415 )
416
417 def infer_speedrun(self) -> None:
418 """
419 @brief Development-only API for measuring pure NPU inference speed.
420
421 Runs NPU inference without uploading inputs and without retrieving outputs.
422 """
424
426 self,
427 inputs: List[PinnedMemory],
428 outputs: Optional[List[PinnedMemory]] = None,
429 cache_size: int = 0,
430 ) -> Optional[List[PinnedMemory]]:
431 """
432 @brief Performs inference directly on pinned memory buffers (zero-copy).
433
434 The NPU reads the inputs from and writes the outputs into the pinned
435 buffers in place, avoiding host-to-device copies of the I/O tensors. Write
436 the input data into each input buffer via indexing (e.g. `pm[...] = data`)
437 before calling, and read the results from the output buffers afterwards.
438
439 @note This is an experimental API and is only supported on REGULUS device.
440 It is not supported for models that use CPU offload.
441
442 @param inputs A list of PinnedMemory buffers holding the input data. All
443 inputs must share the same dtype (`numpy.float32` or
444 `numpy.uint8`).
445 @param outputs A list of pre-allocated PinnedMemory buffers (dtype
446 `numpy.float32`) that will receive the output data. If `None`
447 (the default), the output buffers are allocated internally and
448 returned by this method.
449 @param cache_size The size of the cache to use for inference.
450 @return If `outputs` is `None`, a list of newly allocated PinnedMemory
451 buffers (dtype `numpy.float32`) holding the output data; otherwise
452 `None` (the given `outputs` are filled in place).
453 """
454 if outputs is None:
455 _pms = self._model.infer_pinned_memory(
456 [pm._pinned_memory for pm in inputs],
457 [],
458 cache_size,
459 )
460 return [PinnedMemory(_pm) for _pm in _pms]
461
463 [pm._pinned_memory for pm in inputs],
464 [pm._pinned_memory for pm in outputs],
465 cache_size,
466 )
467 return None
468
470 self,
471 shape: List[int],
472 idx: int = 0,
473 upload: bool = False,
474 dtype: Optional[DataType] = None,
475 ) -> NPUData:
476 """
477 @brief Acquires an NPUData for the model input at the given index.
478
479 @note This is an advanced API rather than a typical usage. (Running inference on
480 the acquired NPUData via `infer_npu_data` additionally requires a
481 single-NPU-op, non-CPU-offload, relocatable (MXQv7+) model.)
482 @warning This API is in beta: it may still contain bugs, and its behavior may
483 change in a future release.
484
485 @param shape The shape of the input tensor, in NHWC/HWC layout.
486 @param idx The index of the model input.
487 @param upload If True, NPU memory is allocated and the data is placed on the
488 NPU; otherwise the data is kept on the host (CPU).
489 @param dtype The element type of the data. `None` (the default) uses the
490 model's user-facing input type. Pass `DataType.Int8` to hand the
491 model data that is already quantized to the NPU-native type, which
492 skips the host-side quantization.
493 @return The acquired NPUData.
494 """
495 shape = [int(s) for s in shape]
496 if dtype is None:
497 return NPUData(self._model.acquire_input_npu_data(shape, idx, upload))
498 return NPUData(
499 self._model.acquire_input_npu_data(shape, idx, upload, dtype.value)
500 )
501
503 self,
504 shape: List[int],
505 idx: int = 0,
506 upload: bool = False,
507 dtype: Optional[DataType] = None,
508 ) -> NPUData:
509 """
510 @brief Acquires an NPUData for the model output at the given index.
511
512 @note This is an advanced API rather than a typical usage. (Running inference on
513 the acquired NPUData via `infer_npu_data` additionally requires a
514 single-NPU-op, non-CPU-offload, relocatable (MXQv7+) model.)
515 @warning This API is in beta: it may still contain bugs, and its behavior may
516 change in a future release.
517
518 @param shape The shape of the output tensor, in NHWC/HWC layout.
519 @param idx The index of the model output.
520 @param upload If True, NPU memory is allocated and the data is placed on the
521 NPU; otherwise the data is kept on the host (CPU).
522 @param dtype The element type of the data. `None` (the default) uses the
523 model's user-facing output type. Pass `DataType.Int8` to receive
524 the raw NPU-native output, which skips the host-side
525 dequantization.
526 @return The acquired NPUData.
527 """
528 shape = [int(s) for s in shape]
529 if dtype is None:
530 return NPUData(self._model.acquire_output_npu_data(shape, idx, upload))
531 return NPUData(
532 self._model.acquire_output_npu_data(shape, idx, upload, dtype.value)
533 )
534
536 self,
537 inputs: List[NPUData],
538 outputs: Optional[List[NPUData]] = None,
539 cache_size: int = 0,
540 ) -> Optional[List[NPUData]]:
541 """
542 @brief Performs inference using NPUData for both inputs and outputs.
543
544 Every input and output NPUData must share the same residency: either all of
545 them are on the host (acquired with `upload=False`) or all of them are already
546 on the NPU (acquired with `upload=True`). A mixed set results in an error.
547
548 @note This is an advanced API rather than a typical usage. Only single-NPU-op
549 (non-CPU-offload), relocatable (MXQv7+) models are supported.
550 @warning This API is in beta: it may still contain bugs, and its behavior may
551 change in a future release.
552
553 @param inputs A list of input NPUData.
554 @param outputs An optional list of pre-allocated output NPUData. If None (the
555 default), the output NPUData are acquired internally and returned.
556 @param cache_size The number of tokens accumulated in the KV cache so far.
557 @return If `outputs` is None, a list of output NPUData holding the inference
558 results; otherwise None (the given `outputs` are filled in place).
559 """
560 if outputs is None:
561 _outs = self._model.infer_npu_data(
562 [d._npu_data for d in inputs],
563 [],
564 cache_size,
565 )
566 return [NPUData(_out) for _out in _outs]
567
569 [d._npu_data for d in inputs],
570 [d._npu_data for d in outputs],
571 cache_size,
572 )
573 return None
574
576 self,
577 inputs: List[NPUData],
578 outputs: Optional[List[NPUData]] = None,
579 cache_size: int = 0,
580 ) -> Optional[List[NPUData]]:
581 """
582 @brief Performs inference using NPUData in NCHW/CHW layout.
583
584 Same as `infer_npu_data` except that every input and output NPUData must have
585 been acquired with a NCHW/CHW shape.
586
587 @note This is an advanced API rather than a typical usage. Only single-NPU-op
588 (non-CPU-offload), relocatable (MXQv7+) models are supported.
589 @warning This API is in beta: it may still contain bugs, and its behavior may
590 change in a future release.
591
592 @param inputs A list of input NPUData.
593 @param outputs An optional list of pre-allocated output NPUData. If None (the
594 default), the output NPUData are acquired internally and returned.
595 @param cache_size The number of tokens accumulated in the KV cache so far.
596 @return If `outputs` is None, a list of output NPUData holding the inference
597 results; otherwise None (the given `outputs` are filled in place).
598 """
599 if outputs is None:
600 _outs = self._model.infer_npu_data_chw(
601 [d._npu_data for d in inputs],
602 [],
603 cache_size,
604 )
605 return [NPUData(_out) for _out in _outs]
606
608 [d._npu_data for d in inputs],
609 [d._npu_data for d in outputs],
610 cache_size,
611 )
612 return None
613
615 self,
616 inputs: Union[np.ndarray, List[np.ndarray]],
617 ) -> Future:
618 """
619 @brief Asynchronous Inference
620
621 Performs inference asynchronously.
622
623 To use asynchronous inference, the model must be created using a `ModelConfig`
624 object with the async pipeline configured to be enabled. This is done by calling
625 @ref ModelConfig.set_async_pipeline_enabled
626 "ModelConfig.set_async_pipeline_enabled(True)" before passing the configuration to
627 `Model()`.
628
629 Example:
630 @code
631 import qbruntime
632
633 mc = qbruntime.ModelConfig()
634 mc.set_async_pipeline_enabled(True)
635
636 model = qbruntime.Model(MXQ_PATH, mc)
637 acc = qbruntime.Accelerator()
638
639 model.launch(acc)
640
641 future = model.infer_async(inputs)
642
643 ret = future.get()
644 @endcode
645
646 @note Currently, only CNN-based models are supported, as asynchronous execution is
647 particularly effective for this type of workload.
648
649 @note Limitations:
650 - RNN/LSTM and LLM models are not supported yet.
651 - Models requiring CPU offloading are not supported yet.
652 - Currently, only single-batch inference is supported (i.e., N = 1).
653 - Currently, Buffer inference is not supported. The following types
654 are supported in the synchronous API for advanced use cases, but are not
655 yet available for asynchronous inference:
656 - Buffer to Buffer
657 - Buffer to float
658 """
659 if not isinstance(inputs, list):
660 inputs = [inputs]
661 _, (is_hwc, _) = _find_matching_variant_idx_and_memory_format(self, inputs)
662 inputs = [np.ascontiguousarray(i) for i in inputs]
663 infer_async_func = (
664 self._model.infer_async if is_hwc else self._model.infer_async_chw
665 )
666 return Future.from_cpp(infer_async_func(inputs), inputs)
667
669 self,
670 inputs: Union[np.ndarray, List[np.ndarray]],
671 ) -> Future:
672 """
673 @brief This method supports int8_t-to-float asynchronous inference.
674
675 @param[in] inputs Input data as a single numpy.ndarray or a list
676 of numpy.ndarray's.
677
678 @return A future that can be used to retrieve the inference result.
679 """
680 if not isinstance(inputs, list):
681 inputs = [inputs]
682 _, (is_hwc, _) = _find_matching_variant_idx_and_memory_format(self, inputs)
683 inputs = [np.ascontiguousarray(i) for i in inputs]
684 infer_async_func = (
685 self._model.infer_async_to_float
686 if is_hwc
687 else self._model.infer_async_chw_to_float
688 )
689 return Future.from_cpp(infer_async_func(inputs), inputs)
690
692 self,
693 inputs: List[np.ndarray],
694 input_bufs: List[Buffer],
695 seqlens: List[List[int]] = [],
696 ) -> None:
697 """Reposition input"""
698 inputs = [np.ascontiguousarray(i) for i in inputs]
700 inputs, [buf._buffer for buf in input_bufs], seqlens
701 )
702
704 self,
705 output_bufs: List[Buffer],
706 outputs: List[np.ndarray],
707 seqlens: List[List[int]] = [],
708 ) -> None:
709 """Reposition output"""
710 if len(outputs) != len(self._output_shape):
711 outputs.clear()
712 for shape in self._output_shape:
713 outputs.append(np.empty(shape=shape, dtype=np.float32))
714 else:
715 for oi in range(len(outputs)):
716 outputs[oi] = np.ascontiguousarray(outputs[oi])
718 [buf._buffer for buf in output_bufs], outputs, seqlens
719 )
720
721 def get_num_model_variants(self) -> int:
722 """
723 @brief Returns the total number of model variants available in this model.
724
725 The `variant_idx` parameter passed to `Model.get_model_variant_handle()` must be
726 in the range [0, return value of this function).
727
728 @return The total number of model variants.
729 """
730 return self._model.get_num_model_variants()
731
732 def get_model_variant_handle(self, variant_idx) -> ModelVariantHandle:
733 """
734 @brief Retrieves a handle to the specified model variant.
735
736 Use the returned `ModelVariantHandle` to query details such as input and output
737 shapes for the selected variant.
738
739 @param[in] variant_idx Index of the model variant to retrieve.
740 Must be in the range [0, getNumModelVariants()).
741
742 @return A `ModelVariantHandle` object if successful;
743 otherwise, raise qbruntime.QbRuntimeError "Model_InvalidVariantIdx".
744 """
745 return ModelVariantHandle.from_cpp(
746 self._model.get_model_variant_handle(variant_idx)
747 )
748
749 def get_model_input_shape(self) -> List[_Shape]:
750 """
751 @brief Returns the input shape of the model.
752
753 @return A list of input shape of the model.
754 """
755 return self._model.get_model_input_shape()
756
757 def get_model_output_shape(self) -> List[_Shape]:
758 """
759 @brief Returns the output shape of the model.
760
761 @return A list of output shape of the model.
762 """
763 return self._model.get_model_output_shape()
764
765 def get_input_scale(self) -> List[Scale]:
766 """
767 @brief Returns the input quantization scale(s) of the model.
768
769 @return A list of input scales.
770 """
771 return [Scale.from_cpp(s) for s in self._model.get_input_scale()]
772
773 def get_output_scale(self) -> List[Scale]:
774 """
775 @brief Returns the output quantization scale(s) of the model.
776
777 @return A list of output scales.
778 """
779 return [Scale.from_cpp(s) for s in self._model.get_output_scale()]
780
781 def get_input_buffer_info(self) -> List[BufferInfo]:
782 """
783 @brief Returns the input buffer information for the model.
784
785 @return A list of input buffer information.
786 """
787 return [BufferInfo.from_cpp(bi) for bi in self._model.get_input_buffer_info()]
788
789 def get_output_buffer_info(self) -> List[BufferInfo]:
790 """
791 @brief Returns the output buffer information of the model.
792
793 @return A list of output buffer information.
794 """
795 return [BufferInfo.from_cpp(bi) for bi in self._model.get_output_buffer_info()]
796
797 def get_model_input_data_type(self) -> DataType:
798 """
799 @brief Returns a data type for model inputs.
800
801 @return An input data type.
802 """
804
805 def get_model_output_data_type(self) -> DataType:
806 """
807 @brief Returns a data type for model outputs.
808
809 @return An output data type.
810 """
812
813 def acquire_input_buffer(self, seqlens: List[List[int]] = []) -> List[Buffer]:
814 """
815 @brief Buffer Management API
816
817 Acquires list of `Buffer` for input.
818 These API is required when calling `Model.infer_buffer()`.
819
820 @note These APIs are intended for advanced use rather than typical usage.
821 """
822 return [Buffer(b) for b in self._model.acquire_input_buffer(seqlens)]
823
824 def acquire_output_buffer(self, seqlens: List[List[int]] = []) -> List[Buffer]:
825 """
826 @brief Buffer Management API
827
828 Acquires list of `Buffer` for output.
829 These API is required when calling `Model.infer_buffer()`.
830
831 @note These APIs are intended for advanced use rather than typical usage.
832 """
833 return [Buffer(b) for b in self._model.acquire_output_buffer(seqlens)]
834
835 def release_buffer(self, buffer: List[Buffer]) -> None:
836 """
837 @brief Buffer Management API
838
839 Deallocate acquired Input/Output buffer
840
841 @note These APIs are intended for advanced use rather than typical usage.
842 """
843 self._model.release_buffer([b._buffer for b in buffer])
844
845 def get_identifier(self) -> int:
846 """
847 @brief Returns the model's unique identifier.
848
849 This identifier distinguishes multiple models within a single user program.
850 It is assigned incrementally, starting from 0 (e.g., 0, 1, 2, 3, ...).
851
852 @return The model identifier.
853 """
854 return self._model.get_identifier()
855
856 def get_model_path(self) -> str:
857 """
858 @brief Returns the path to the MXQ model file associated with the Model.
859
860 @return The MXQ file path.
861 """
862 return self._model.get_model_path()
863
864 def get_cache_infos(self) -> List[CacheInfo]:
865 """
866 @brief Returns informations of KV-cache of the model.
867
868 @return A list of CacheInfo objects.
869 """
870 return [CacheInfo.from_cpp(c) for c in self._model.get_cache_infos()]
871
872 def dump_cache_memory(self, cache_id: int = 0) -> List[bytes]:
873 """
874 @brief Dumps the KV cache memory into buffers.
875
876 Writes the current KV cache data into provided buffers.
877
878 @param[in] cache_id Index of target cache.
879
880 @return A list of bytes containing the KV cache data.
881 """
882 bufs = self._model.dump_cache_memory(cache_id)
883 return [np.asarray(buf, np.int8).tobytes() for buf in bufs]
884
885 def load_cache_memory(self, bufs: List[bytes], cache_id: int = 0) -> None:
886 """
887 @brief Loads the KV cache memory from buffers.
888
889 Restores the KV cache from the provided buffers.
890
891 @param[in] bufs A list of bytes containing the KV cache
892 """
894 [np.frombuffer(buf, dtype=np.int8) for buf in bufs], cache_id
895 )
896
897 def dump_cache_memory_to(self, cache_dir: str, cache_id: int = 0) -> None:
898 """
899 @brief Dumps KV cache memory to files in the specified directory.
900
901 Writes the KV cache data to binary files within the given directory.
902 Each file is named using the format: `cache_<layer_hash>.bin`.
903
904 @param[in] cache_dir Path to the directory where KV cache files will be saved.
905 @param[in] cache_id Index of target cache.
906 """
907 self._model.dump_cache_memory(cache_dir, cache_id)
908
909 def load_cache_memory_from(self, cache_dir: str, cache_id: int = 0) -> None:
910 """
911 @brief Loads the KV cache memory from files in the specified directory.
912
913 Reads KV cache data from files within the given directory and restores them.
914 Each file is named using the format: `cache_<layer_hash>.bin`.
915
916 @param[in] cache_dir Path to the directory where KV cache files are saved.
917 """
918 self._model.load_cache_memory(cache_dir, cache_id)
919
921 self, cache_size: int, tail_size: int, mask: List[bool]
922 ) -> int:
923 """
924 @brief Filter the tail of the KV cache memory
925
926 Retains the desired caches in the tail of the KV cache memory, excludes the others,
927 and shifts the remaining caches forward.
928
929 @param[in] cache_size The number of tokens accumulated in the KV cache so far.
930 @param[in] tail_size The tail size of the KV cache to filter (<=32).
931 @param[in] mask A mask indicating tokens to retain or exclude at the tail of the KV
932 cache.
933
934 @return New cache size after tail filtering.
935 """
936 return self._model.filter_cache_tail(cache_size, tail_size, mask)
937
938 def move_cache_tail(self, num_head: int, num_tail: int, cache_size: int) -> int:
939 """
940 @brief Moves the tail of the KV cache memory to the end of the head.
941
942 Slice the tail of the KV cache memory up to the specified size
943 and moves it to the designated cache position.
944
945 @param[in] num_head The size of the KV cache head where the tail is appended.
946 @param[in] num_tail The size of the KV cache tail to be moved.
947 @param[in] cache_size The total number of tokens accumulated in the KV cache so
948 far.
949
950 @return The updated cache size after moving the tail.
951 """
952 return self._model.move_cache_tail(num_head, num_tail, cache_size)
953
954
955def load(path: str, model_config: Optional[ModelConfig] = None) -> Model:
956 """
957 @brief Single-step inference API. Creates model and uploads the model
958 into NPU immediately.
959
960 This operation performs the Accelerator declaration, Model declaration,
961 and launch in a single step.
962 """
963 acc = Accelerator()
964 model = Model(path, model_config)
965 model.launch(acc)
966 return model
967
968
969
Represents an accelerator, i.e., an NPU, used for executing models.
Represents an AI model loaded from an MXQ file.
Definition model.py:115
DataType get_model_input_data_type(self)
Returns a data type for model inputs.
Definition model.py:797
None launch(self, Accelerator acc)
Launches the model on the specified Accelerator, which represents the actual NPU.
Definition model.py:146
None reposition_outputs(self, List[Buffer] output_bufs, List[np.ndarray] outputs, List[List[int]] seqlens=[])
Reposition output.
Definition model.py:708
List[Scale] get_input_scale(self)
Returns the input quantization scale(s) of the model.
Definition model.py:765
CoreMode get_core_mode(self)
Retrieves the core mode of the model.
Definition model.py:176
List[CoreId] get_target_cores(self)
Returns the NPU cores the model is configured to use.
Definition model.py:196
List[_Shape] _input_shape
Definition model.py:143
List[CoreId] target_cores(self)
Definition model.py:205
List[_Shape] _output_shape
Definition model.py:144
List[Buffer] acquire_input_buffer(self, List[List[int]] seqlens=[])
Buffer Management API.
Definition model.py:813
Optional[List[PinnedMemory]] infer_pinned_memory(self, List[PinnedMemory] inputs, Optional[List[PinnedMemory]] outputs=None, int cache_size=0)
Performs inference directly on pinned memory buffers (zero-copy).
Definition model.py:430
List[BufferInfo] get_output_buffer_info(self)
Returns the output buffer information of the model.
Definition model.py:789
str get_model_path(self)
Returns the path to the MXQ model file associated with the Model.
Definition model.py:856
Optional[List[NPUData]] infer_npu_data_chw(self, List[NPUData] inputs, Optional[List[NPUData]] outputs=None, int cache_size=0)
Performs inference using NPUData in NCHW/CHW layout.
Definition model.py:580
Future infer_async(self, Union[np.ndarray, List[np.ndarray]] inputs)
Asynchronous Inference.
Definition model.py:617
DataType get_model_output_data_type(self)
Returns a data type for model outputs.
Definition model.py:805
NPUData acquire_output_npu_data(self, List[int] shape, int idx=0, bool upload=False, Optional[DataType] dtype=None)
Acquires an NPUData for the model output at the given index.
Definition model.py:508
Optional[List[NPUData]] infer_npu_data(self, List[NPUData] inputs, Optional[List[NPUData]] outputs=None, int cache_size=0)
Performs inference using NPUData for both inputs and outputs.
Definition model.py:540
List[Buffer] acquire_output_buffer(self, List[List[int]] seqlens=[])
Buffer Management API.
Definition model.py:824
int filter_cache_tail(self, int cache_size, int tail_size, List[bool] mask)
Filter the tail of the KV cache memory.
Definition model.py:922
List[np.ndarray] _infer_to_float(self, Union[np.ndarray, List[np.ndarray],] inputs, int cache_size, Optional[bool] is_target_hwc=None)
int8_t-to-float inference Performs inference with input and output elements of type int8_t
Definition model.py:360
int get_num_model_variants(self)
Returns the total number of model variants available in this model.
Definition model.py:721
None load_cache_memory_from(self, str cache_dir, int cache_id=0)
Loads the KV cache memory from files in the specified directory.
Definition model.py:909
bool is_target(self, CoreId core_id)
Checks if the NPU core specified by CoreId is the target of the model.
Definition model.py:165
None infer_speedrun(self)
Development-only API for measuring pure NPU inference speed.
Definition model.py:417
None dump_cache_memory_to(self, str cache_dir, int cache_id=0)
Dumps KV cache memory to files in the specified directory.
Definition model.py:897
Future infer_async_to_float(self, Union[np.ndarray, List[np.ndarray]] inputs)
This method supports int8_t-to-float asynchronous inference.
Definition model.py:671
ModelVariantHandle get_model_variant_handle(self, variant_idx)
Retrieves a handle to the specified model variant.
Definition model.py:732
None load_cache_memory(self, List[bytes] bufs, int cache_id=0)
Loads the KV cache memory from buffers.
Definition model.py:885
List[_Shape] get_model_output_shape(self)
Returns the output shape of the model.
Definition model.py:757
List[CacheInfo] get_cache_infos(self)
Returns informations of KV-cache of the model.
Definition model.py:864
None release_buffer(self, List[Buffer] buffer)
Buffer Management API.
Definition model.py:835
List[bytes] dump_cache_memory(self, int cache_id=0)
Dumps the KV cache memory into buffers.
Definition model.py:872
None dispose(self)
Disposes of the model loaded onto the NPU.
Definition model.py:156
List[_Shape] get_model_input_shape(self)
Returns the input shape of the model.
Definition model.py:749
List[Scale] get_output_scale(self)
Returns the output quantization scale(s) of the model.
Definition model.py:773
None infer_buffer(self, List[Buffer] inputs, List[Buffer] outputs, List[List[int]] shape=[], int cache_size=0)
Buffer-to-Buffer inference.
Definition model.py:395
List[np.ndarray] infer_to_float(self, Union[np.ndarray, List[np.ndarray],] inputs, int cache_size=0)
int8_t-to-float inference Performs inference with input and output elements of type int8_t
Definition model.py:320
List[str] get_device_names(self)
Returns the supported target device name(s) this model can run on.
Definition model.py:184
__init__(self, str path, Optional[ModelConfig] model_config=None)
Creates a Model object from the specified MXQ model file and configuration.
Definition model.py:123
Optional[List[np.ndarray]] _infer(self, Union[np.ndarray, List[np.ndarray]] inputs, Optional[List[np.ndarray]] outputs, int cache_size, Optional[bool] is_target_hwc=None, Optional[List[BatchParam]] params=None)
Definition model.py:264
Optional[List[np.ndarray]] infer(self, Union[np.ndarray, List[np.ndarray]] inputs, Optional[List[np.ndarray]] outputs=None, int cache_size=0, Optional[List[BatchParam]] params=None)
Performs inference.
Definition model.py:215
NPUData acquire_input_npu_data(self, List[int] shape, int idx=0, bool upload=False, Optional[DataType] dtype=None)
Acquires an NPUData for the model input at the given index.
Definition model.py:475
None reposition_inputs(self, List[np.ndarray] inputs, List[Buffer] input_bufs, List[List[int]] seqlens=[])
Reposition input.
Definition model.py:696
List[BufferInfo] get_input_buffer_info(self)
Returns the input buffer information for the model.
Definition model.py:781
int get_identifier(self)
Returns the model's unique identifier.
Definition model.py:845
int move_cache_tail(self, int num_head, int num_tail, int cache_size)
Moves the tail of the KV cache memory to the end of the head.
Definition model.py:938
A model input or output tensor that can reside on the host (CPU) or NPU.
Definition npu_data.py:29
An NPU-accessible pinned (physically contiguous) memory buffer.
A simple byte-sized buffer.
Definition type.py:219
Defines the core mode for NPU execution.
Definition type.py:244
Model load(str path, Optional[ModelConfig] model_config=None)
Single-step inference API.
Definition model.py:955