model.py Source File

model.py Source File#

SDK qb Runtime Library: model.py Source File
SDK qb Runtime Library v0.28
MCS001-KR
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 maccel.maccel as _cMaccel
10from .accelerator import Accelerator
11from .type import *
12from .future import *
13
14_Shape = Tuple[int, ...]
15
16__all__ = ["Model", "load"]
17
18
21
22# input ndarray의 shape이 유효한 shape인지 판별한다.
23def _is_valid_shape(input_shape: _Shape, shape: _Shape) -> bool:
24 if (len(input_shape) < len(shape)) or (len(input_shape) > len(shape) + 1):
25 return False
26 # input을 batch일 경우도 고려하여 [h, w, c] 및 [batch, h, w, c] 모두 고려한다
27 offset = 1 if len(input_shape) > len(shape) else 0
28 for s1, s2 in zip(input_shape[offset:], shape):
29 # Dimensions that allow variable lengths are represented by negative values.
30 # A variable-length dimension only permits multiples of the original value.
31 if s1 % s2 != 0 or (s2 > 0 and s1 != s2):
32 return False
33 return True
34
35
36# input ndarray의 shape를 검사하여 HWC인지 CHW인지 판별한다.
37def _is_shape_hwc(inputs: List[np.ndarray], shapes: List[_Shape]) -> bool:
38 if len(inputs) != len(shapes):
39 raise ValueError("The number of inputs is different.")
40
41 is_hwc = True
42 is_chw = True
43 for arr, shape in zip(inputs, shapes):
44 shape_hwc = (shape[0], shape[1], shape[2])
45 shape_chw = (shape[2], shape[0], shape[1])
46 is_hwc = is_hwc and _is_valid_shape(arr.shape, shape_hwc)
47 is_chw = is_chw and _is_valid_shape(arr.shape, shape_chw)
48
49 if not is_hwc and not is_chw:
50 raise ValueError("Input shape is invalid.")
51 # If both `is_hwc` and `is_chw` are `True`, the memory format is assumed to be HWC.
52 return is_hwc
53
54
55# shape에 맞게 numpy ndarray를 생성한다.
56def _build_outputs(
57 shapes: List[_Shape], is_hwc: bool, dtype: np.dtype
58) -> List[np.ndarray]:
59 outputs = []
60 for shape in shapes:
61 if is_hwc:
62 shape = (shape[0], shape[1], shape[2])
63 else:
64 shape = (shape[2], shape[0], shape[1])
65 outputs.append(np.empty(shape, dtype=dtype))
66 return outputs
67
68
69# output에 들어있는 numpy ndarray의 shape가 올바른지 검사한다.
70def _check_output_shapes(
71 outputs: List[np.ndarray], shapes: List[_Shape], is_hwc: bool, dtype: np.dtype
72) -> None:
73 if len(outputs) != len(shapes):
74 raise ValueError("The number of outputs is different.")
75
76 for output, shape in zip(outputs, shapes):
77 if output.dtype != dtype:
78 raise ValueError("Output dtype mismatch.")
79
80 if is_hwc:
81 shape = (shape[0], shape[1], shape[2])
82 else:
83 shape = (shape[2], shape[0], shape[1])
84 if output.shape != shape:
85 raise ValueError("Output shape mismatch.")
86
87
88class Model:
89 def __init__(self, path: str, model_config: Optional[ModelConfig] = None):
90 if model_config is None:
91 self._model = _cMaccel.Model(path)
92 else:
93 self._model = _cMaccel.Model(path, model_config._model_config)
94
95 # 기존 BufferInfo 대신에 ModelShape를 사용한다.
96 # Model {input,output} shape는 batch를 포함한 4D이다.
99
100 def launch(self, acc: Accelerator) -> None:
101 self._model.launch(acc._accelerator)
102 self._acc = acc
103
104 def dispose(self) -> None:
105 self._model.dispose()
106 self._acc = None
107
108 def is_target(self, core_id: CoreId) -> bool:
109 return self._model.is_target(core_id._core_id)
110
111 def get_core_mode(self) -> CoreMode:
112 return CoreMode(self._model.get_core_mode())
113
114 def get_target_cores(self) -> List[CoreId]:
115 return [CoreId.from_cpp(target) for target in self._model.target_cores]
116
117 # Deprecated
118 @property
119 def target_cores(self) -> List[CoreId]:
120 return [CoreId.from_cpp(target) for target in self._model.target_cores]
121
122 # 1. infer(in:List[numpy]) -> List[numpy] (float / int)
123 # 2. infer(in:numpy) -> List[numpy] (float / int)
124 # 3. infer(in:List[numpy], out:List[numpy]) (float / int)
125 # 4. infer(in:List[numpy], out:List[]) (float / int)
126 # 5. infer(in:numpy, out:List[numpy]) (float / int)
127 # 6. infer(in:numpy, out:List[]) (float / int)
128 def infer(
129 self,
130 inputs: Union[np.ndarray, List[np.ndarray]],
131 outputs: Optional[List[np.ndarray]] = None,
132 cache_size: int = 0,
133 ) -> Optional[List[np.ndarray]]:
134 if not isinstance(inputs, list):
135 inputs = [inputs]
136
137 is_hwc = _is_shape_hwc(inputs, self._input_shape)
138 inputs = [np.ascontiguousarray(i) for i in inputs]
139
140 if outputs is None:
141 # No Output Parameter
142 infer_func = self._model.infer if is_hwc else self._model.infer_chw
143 return [np.asarray(o) for o in infer_func(inputs, cache_size)]
144
145 else:
146 if outputs:
147 _check_output_shapes(
148 outputs, self._output_shape, is_hwc, inputs[0].dtype
149 )
150 for oi in range(len(outputs)):
151 outputs[oi] = np.ascontiguousarray(outputs[oi])
152 else:
153 outputs[:] = _build_outputs(self._output_shape, is_hwc, inputs[0].dtype)
154
155 if is_hwc:
156 self._model.infer(inputs, outputs, cache_size)
157 else:
158 self._model.infer_chw(inputs, outputs, cache_size)
159
160 def infer_to_float(
161 self,
162 inputs: Union[
163 np.ndarray,
164 List[np.ndarray],
165 ],
166 cache_size: int = 0,
167 ) -> List[np.ndarray]:
168 if not isinstance(inputs, list):
169 inputs = [inputs]
170
171 is_hwc = _is_shape_hwc(inputs, self._input_shape)
172 inputs = [np.ascontiguousarray(i) for i in inputs]
173
174 if is_hwc:
175 outputs = self._model.infer_to_float(inputs, cache_size)
176 else:
177 outputs = self._model.infer_chw_to_float(inputs, cache_size)
178
179 return [np.asarray(o) for o in outputs]
180
181 # For backward compatibility.
182 infer_chw = infer
183 infer_chw_to_float = infer_to_float
184
185 def infer_buffer(
186 self,
187 inputs: List[Buffer],
188 outputs: List[Buffer],
189 shape: List[List[int]] = [],
190 cache_size: int = 0,
191 ) -> None:
192 self._model.infer_buffer(
193 [i._buffer for i in inputs], [o._buffer for o in outputs], shape, cache_size
194 )
195
196 def infer_speedrun(self) -> None:
197 self._model.infer_speedrun()
198
199 def infer_async(
200 self,
201 inputs: Union[np.ndarray, List[np.ndarray]],
202 ) -> Future:
203 if not isinstance(inputs, list):
204 inputs = [inputs]
205 is_hwc = _is_shape_hwc(inputs, self._input_shape)
206 inputs = [np.ascontiguousarray(i) for i in inputs]
207 infer_async_func = (
208 self._model.infer_async if is_hwc else self._model.infer_async_chw
209 )
210 return Future.from_cpp(infer_async_func(inputs), inputs)
211
212 def reposition_inputs(
213 self,
214 inputs: List[np.ndarray],
215 input_bufs: List[Buffer],
216 seqlens: List[List[int]] = [],
217 ) -> None:
218 inputs = [np.ascontiguousarray(i) for i in inputs]
219 self._model.reposition_inputs(
220 inputs, [buf._buffer for buf in input_bufs], seqlens
221 )
222
223 def reposition_outputs(
224 self,
225 output_bufs: List[Buffer],
226 outputs: List[np.ndarray],
227 seqlens: List[List[int]] = [],
228 ) -> None:
229 if len(outputs) != len(self._output_shape):
230 outputs.clear()
231 for shape in self._output_shape:
232 outputs.append(np.empty(shape=shape, dtype=np.float32))
233 else:
234 for oi in range(len(outputs)):
235 outputs[oi] = np.ascontiguousarray(outputs[oi])
236 self._model.reposition_outputs(
237 [buf._buffer for buf in output_bufs], outputs, seqlens
238 )
239
240 def get_model_input_shape(self) -> List[_Shape]:
241 return self._model.get_model_input_shape()
242
243 def get_model_output_shape(self) -> List[_Shape]:
244 return self._model.get_model_output_shape()
245
246 def get_input_scale(self) -> List[Scale]:
247 return [Scale.from_cpp(s) for s in self._model.get_input_scale()]
248
249 def get_output_scale(self) -> List[Scale]:
250 return [Scale.from_cpp(s) for s in self._model.get_output_scale()]
251
252 def get_input_buffer_info(self) -> List[BufferInfo]:
253 return [BufferInfo.from_cpp(bi) for bi in self._model.get_input_buffer_info()]
254
255 def get_output_buffer_info(self) -> List[BufferInfo]:
256 return [BufferInfo.from_cpp(bi) for bi in self._model.get_output_buffer_info()]
257
258 def acquire_input_buffer(self, seqlens: List[List[int]] = []) -> List[Buffer]:
259 return [Buffer(b) for b in self._model.acquire_input_buffer(seqlens)]
260
261 def acquire_output_buffer(self, seqlens: List[List[int]] = []) -> List[Buffer]:
262 return [Buffer(b) for b in self._model.acquire_output_buffer(seqlens)]
263
264 def release_buffer(self, buffer: List[Buffer]) -> None:
265 self._model.release_buffer([b._buffer for b in buffer])
266
267 def get_identifier(self) -> int:
268 return self._model.get_identifier()
269
270 def get_model_path(self) -> str:
271 return self._model.get_model_path()
272
273 def get_schedule_policy(self) -> SchedulePolicy:
274 return SchedulePolicy(self._model.get_schedule_policy())
275
276 def get_latency_set_policy(self) -> LatencySetPolicy:
277 return LatencySetPolicy(self._model.get_latency_set_policy())
278
279 def get_maintenance_policy(self) -> MaintenancePolicy:
280 return MaintenancePolicy(self._model.get_maintenance_policy())
281
282 def get_latency_consumed(self) -> int:
283 return self._model.get_latency_consumed()
284
285 def get_latency_finished(self) -> int:
286 return self._model.get_latency_finished()
287
288 def reset_cache_memory(self) -> None:
289 self._model.reset_cache_memory()
290
291 def dump_cache_memory(self) -> bytes:
292 buf = self._model.dump_cache_memory()
293 return np.asarray(buf, np.int8).tobytes()
294
295 def load_cache_memory(self, buf: bytes) -> None:
296 self._model.load_cache_memory(np.frombuffer(buf, dtype=np.int8))
297
298 def dump_cache_memory_to(self, cache_path: str) -> None:
299 self._model.dump_cache_memory(cache_path)
300
301 def load_cache_memory_from(self, cache_path: str) -> None:
302 self._model.load_cache_memory(cache_path)
303
304 def filter_cache_tail(
305 self, cache_size: int, tail_size: int, mask: List[bool]
306 ) -> int:
307 return self._model.filter_cache_tail(cache_size, tail_size, mask)
308
309 def move_cache_tail(self, num_head: int, num_tail: int, cache_size: int) -> int:
310 return self._model.move_cache_tail(num_head, num_tail, cache_size)
311
312
313def load(path: str, model_config: Optional[ModelConfig] = None) -> Model:
314 acc = Accelerator()
315 model = Model(path, model_config)
316 model.launch(acc)
317 return model
318
319
List[_Shape] _input_shape
Definition model.py:97
List[_Shape] get_model_input_shape(self)
Definition model.py:240
List[_Shape] _output_shape
Definition model.py:98
List[_Shape] get_model_output_shape(self)
Definition model.py:243