GitOrigin-RevId: 0ddbb75e82
tags/v1.8.0
| @@ -8,6 +8,7 @@ | |||
| # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| from .base import * | |||
| from .base import version as __version__ | |||
| from .global_setting import * | |||
| from .network import * | |||
| from .struct import * | |||
| @@ -69,7 +69,9 @@ class LiteOptions(Structure): | |||
| "const_shape": bool(self.const_shape), | |||
| "force_dynamic_alloc": bool(self.force_dynamic_alloc), | |||
| "force_output_dynamic_alloc": bool(self.force_output_dynamic_alloc), | |||
| "force_output_nocopy": bool(self.force_output_nocopy), | |||
| "force_output_use_user_specified_memory": bool( | |||
| self.force_output_use_user_specified_memory | |||
| ), | |||
| "no_profiling_on_shape_change": bool(self.no_profiling_on_shape_change), | |||
| "jit_level": self.jit_level, | |||
| "comp_node_seq_record_level": self.comp_node_seq_record_level, | |||
| @@ -99,7 +101,7 @@ class LiteConfig(Structure): | |||
| ("device_id", c_int), | |||
| ("device_type", c_int), | |||
| ("backend", c_int), | |||
| ("bare_model_cryption_name", c_char_p), | |||
| ("_bare_model_cryption_name", c_char_p), | |||
| ("options", LiteOptions), | |||
| ] | |||
| @@ -110,18 +112,30 @@ class LiteConfig(Structure): | |||
| else: | |||
| self.options = LiteOptions() | |||
| self.bare_model_cryption_name = c_char_p(b"") | |||
| self._bare_model_cryption_name = c_char_p(b"") | |||
| self.use_loader_dynamic_param = 0 | |||
| self.has_compression = 0 | |||
| self.backend = LiteBackend.LITE_DEFAULT | |||
| @property | |||
| def bare_model_cryption_name(self): | |||
| return self._bare_model_cryption_name.decode("utf-8") | |||
| @bare_model_cryption_name.setter | |||
| def bare_model_cryption_name(self, name): | |||
| if isinstance(name, str): | |||
| self._bare_model_cryption_name = name.encode("utf-8") | |||
| else: | |||
| assert isinstance(name, bytes), "name should be str or bytes type." | |||
| self._bare_model_cryption_name = name | |||
| def __repr__(self): | |||
| data = { | |||
| "has_compression": bool(self.has_compression), | |||
| "device_id": LiteDeviceType(self.device_id), | |||
| "device_type": LiteDeviceType(self.device_type), | |||
| "backend": LiteBackend(self.backend), | |||
| "bare_model_cryption_name": self.bare_model_cryption_name.decode("utf-8"), | |||
| "bare_model_cryption_name": self.bare_model_cryption_name, | |||
| "options": self.options, | |||
| } | |||
| return data.__repr__() | |||
| @@ -149,7 +163,7 @@ class LiteIO(Structure): | |||
| """ | |||
| _fields_ = [ | |||
| ("name", c_char_p), | |||
| ("_name", c_char_p), | |||
| ("is_host", c_int), | |||
| ("io_type", c_int), | |||
| ("config_layout", LiteLayout), | |||
| @@ -159,9 +173,9 @@ class LiteIO(Structure): | |||
| self, name, is_host=True, io_type=LiteIOType.LITE_IO_VALUE, layout=None | |||
| ): | |||
| if type(name) == str: | |||
| self.name = c_char_p(name.encode("utf-8")) | |||
| self._name = c_char_p(name.encode("utf-8")) | |||
| else: | |||
| self.name = c_char_p(name) | |||
| self._name = c_char_p(name) | |||
| if layout: | |||
| self.config_layout = layout | |||
| @@ -171,6 +185,18 @@ class LiteIO(Structure): | |||
| self.is_host = is_host | |||
| self.io_type = io_type | |||
| @property | |||
| def name(self): | |||
| return self._name.decode("utf-8") | |||
| @name.setter | |||
| def name(self, name): | |||
| if isinstance(name, str): | |||
| self._name = name.encode("utf-8") | |||
| else: | |||
| assert isinstance(name, bytes), "name should be str or bytes type." | |||
| self._name = name | |||
| def __repr__(self): | |||
| data = { | |||
| "name": self.name, | |||
| @@ -208,17 +234,45 @@ class LiteNetworkIO(object): | |||
| the input and output information for user to construct _LiteNetWorkIO | |||
| """ | |||
| def __init__(self): | |||
| def __init__(self, inputs=None, outputs=None): | |||
| self.inputs = [] | |||
| self.outputs = [] | |||
| if inputs: | |||
| for i in inputs: | |||
| if isinstance(i, list): | |||
| self.inputs.append(LiteIO(*i)) | |||
| else: | |||
| assert isinstance( | |||
| i, LiteIO | |||
| ), "the param to construct LiteNetworkIO must be list of the LiteIO member or the LiteIO." | |||
| self.inputs.append(i) | |||
| if outputs: | |||
| for i in outputs: | |||
| if isinstance(i, list): | |||
| self.outputs.append(LiteIO(*i)) | |||
| else: | |||
| assert isinstance( | |||
| i, LiteIO | |||
| ), "the param to construct LiteNetworkIO must be list of the LiteIO member or the LiteIO." | |||
| self.outputs.append(i) | |||
| def add_input( | |||
| self, obj, is_host=True, io_type=LiteIOType.LITE_IO_VALUE, layout=None | |||
| ): | |||
| if isinstance(obj, LiteIO): | |||
| self.inputs.append(obj) | |||
| else: | |||
| name = obj | |||
| self.add_input(LiteIO(name, is_host, io_type, layout)) | |||
| def add_input(self, input_io): | |||
| assert isinstance(input_io, LiteIO) | |||
| self.inputs.append(input_io) | |||
| def add_output(self, output_io): | |||
| assert isinstance(output_io, LiteIO) | |||
| self.outputs.append(output_io) | |||
| def add_output( | |||
| self, obj, is_host=True, io_type=LiteIOType.LITE_IO_VALUE, layout=None | |||
| ): | |||
| if isinstance(obj, LiteIO): | |||
| self.outputs.append(obj) | |||
| else: | |||
| name = obj | |||
| self.add_output(LiteIO(name, is_host, io_type, layout)) | |||
| def _create_network_io(self): | |||
| network_io = _LiteNetworkIO() | |||
| @@ -48,6 +48,15 @@ ctype_to_lite_dtypes = { | |||
| c_ushort: LiteDataType.LITE_UINT16, | |||
| } | |||
| _lite_dtypes_to_ctype = { | |||
| LiteDataType.LITE_INT: c_int, | |||
| LiteDataType.LITE_FLOAT: c_float, | |||
| LiteDataType.LITE_UINT8: c_ubyte, | |||
| LiteDataType.LITE_INT8: c_byte, | |||
| LiteDataType.LITE_INT16: c_short, | |||
| LiteDataType.LITE_UINT16: c_ushort, | |||
| } | |||
| class LiteLayout(Structure): | |||
| """ | |||
| @@ -55,7 +64,7 @@ class LiteLayout(Structure): | |||
| """ | |||
| _fields_ = [ | |||
| ("shapes", c_size_t * MAX_DIM), | |||
| ("_shapes", c_size_t * MAX_DIM), | |||
| ("ndim", c_size_t), | |||
| ("data_type", c_int), | |||
| ] | |||
| @@ -64,10 +73,10 @@ class LiteLayout(Structure): | |||
| if shape: | |||
| shape = list(shape) | |||
| assert len(shape) <= MAX_DIM, "Layout max dim is 7." | |||
| self.shapes = (c_size_t * MAX_DIM)(*shape) | |||
| self._shapes = (c_size_t * MAX_DIM)(*shape) | |||
| self.ndim = len(shape) | |||
| else: | |||
| self.shapes = (c_size_t * MAX_DIM)() | |||
| self._shapes = (c_size_t * MAX_DIM)() | |||
| self.ndim = 0 | |||
| if not dtype: | |||
| self.data_type = LiteDataType.LITE_FLOAT | |||
| @@ -83,9 +92,24 @@ class LiteLayout(Structure): | |||
| else: | |||
| raise RuntimeError("unkonw data type") | |||
| @property | |||
| def dtype(self): | |||
| return _lite_type_to_nptypes[LiteDataType(self.data_type)] | |||
| @property | |||
| def shapes(self): | |||
| return list(self._shapes)[0 : self.ndim] | |||
| @shapes.setter | |||
| def shapes(self, shape): | |||
| shape = list(shape) | |||
| assert len(shape) <= MAX_DIM, "Layout max dim is 7." | |||
| self._shapes = (c_size_t * MAX_DIM)(*shape) | |||
| self.ndim = len(shape) | |||
| def __repr__(self): | |||
| data = { | |||
| "shapes": list(self.shapes)[0 : self.ndim], | |||
| "shapes": self.shapes, | |||
| "ndim": self.ndim, | |||
| "data_type": _lite_type_to_nptypes[LiteDataType(self.data_type)], | |||
| } | |||
| @@ -177,15 +201,20 @@ class LiteTensor(object): | |||
| device_type=LiteDeviceType.LITE_CPU, | |||
| device_id=0, | |||
| is_pinned_host=False, | |||
| shapes=None, | |||
| dtype=None, | |||
| ): | |||
| """ | |||
| create a Tensor with layout, device, is_pinned_host param | |||
| create a Tensor with layout, device, is_pinned_host or shapes, dtype, | |||
| device_type, device_id, is_pinned_host param | |||
| """ | |||
| self._tensor = _Ctensor() | |||
| if layout: | |||
| self._layout = LiteLayout() | |||
| if layout is not None: | |||
| self._layout = layout | |||
| else: | |||
| self._layout = LiteLayout() | |||
| elif shapes is not None: | |||
| shapes = list(shapes) | |||
| self._layout = LiteLayout(shapes, dtype) | |||
| self._device_type = device_type | |||
| self._device_id = device_id | |||
| self._is_pinned_host = is_pinned_host | |||
| @@ -222,9 +251,12 @@ class LiteTensor(object): | |||
| @layout.setter | |||
| def layout(self, layout): | |||
| assert isinstance(layout, LiteLayout) | |||
| self._layout = layout | |||
| self._api.LITE_set_tensor_layout(self._tensor, layout) | |||
| if isinstance(layout, LiteLayout): | |||
| self._layout = layout | |||
| elif isinstance(layout, list): | |||
| self._layout.shapes = layout | |||
| self._api.LITE_set_tensor_layout(self._tensor, self._layout) | |||
| @property | |||
| def is_pinned_host(self): | |||
| @@ -270,7 +302,6 @@ class LiteTensor(object): | |||
| """ | |||
| get the length of the meomry in byte | |||
| """ | |||
| self.update() | |||
| length = c_size_t() | |||
| self._api.LITE_get_tensor_total_size_in_byte(self._tensor, byref(length)) | |||
| return length.value | |||
| @@ -336,7 +367,6 @@ class LiteTensor(object): | |||
| """ | |||
| get the memory of the tensor, return c_void_p of the tensor memory | |||
| """ | |||
| self.update() | |||
| mem = c_void_p() | |||
| self._api.LITE_get_tensor_memory(self._tensor, byref(mem)) | |||
| return mem | |||
| @@ -347,7 +377,6 @@ class LiteTensor(object): | |||
| param data: the data will shared to the tensor, it should be a | |||
| numpy.ndarray or ctypes data | |||
| """ | |||
| self.update() | |||
| if isinstance(data, np.ndarray): | |||
| assert ( | |||
| self.is_continue | |||
| @@ -356,8 +385,7 @@ class LiteTensor(object): | |||
| self.is_pinned_host or self.device_type == LiteDeviceType.LITE_CPU | |||
| ), "set_data_by_share can only apply in cpu tensor or pinned tensor." | |||
| np_type = _lite_type_to_nptypes[LiteDataType(self._layout.data_type)] | |||
| c_type = np.ctypeslib.as_ctypes_type(np_type) | |||
| c_type = _lite_dtypes_to_ctype[LiteDataType(self._layout.data_type)] | |||
| if self.nbytes != data.nbytes: | |||
| self.layout = LiteLayout(data.shape, ctype_to_lite_dtypes[c_type]) | |||
| @@ -377,7 +405,6 @@ class LiteTensor(object): | |||
| param data: the data to copy to tensor, it should be list, | |||
| numpy.ndarraya or ctypes with length | |||
| """ | |||
| self.update() | |||
| if layout is not None: | |||
| self.layout = layout | |||
| @@ -386,8 +413,7 @@ class LiteTensor(object): | |||
| self.is_pinned_host or self.device_type == LiteDeviceType.LITE_CPU | |||
| ), "set_data_by_copy can only apply in cpu tensor or pinned tensor." | |||
| np_type = _lite_type_to_nptypes[LiteDataType(self._layout.data_type)] | |||
| c_type = np.ctypeslib.as_ctypes_type(np_type) | |||
| c_type = _lite_dtypes_to_ctype[LiteDataType(self._layout.data_type)] | |||
| tensor_memory = c_void_p() | |||
| @@ -415,6 +441,22 @@ class LiteTensor(object): | |||
| self._api.LITE_get_tensor_memory(self._tensor, byref(tensor_memory)) | |||
| memmove(tensor_memory, data, data_length) | |||
| def get_data_by_share(self): | |||
| """ | |||
| get the data in the tensor, add share the data with a new numpy, and | |||
| return the numpy arrray, be careful, the data in numpy is valid before | |||
| the tensor memory is write again, such as LiteNetwok forward next time. | |||
| """ | |||
| assert self.is_continue, "get_data_by_share can only apply in continue tensor." | |||
| assert ( | |||
| self.is_pinned_host or self.device_type == LiteDeviceType.LITE_CPU | |||
| ), "get_data_by_share can only apply in CPU tensor or cpu pinned tensor." | |||
| memory = self.get_ctypes_memory() | |||
| c_type = _lite_dtypes_to_ctype[LiteDataType(self._layout.data_type)] | |||
| pnt = cast(memory, POINTER(c_type)) | |||
| return np.ctypeslib.as_array(pnt, self._layout.shapes) | |||
| def to_numpy(self): | |||
| """ | |||
| get the buffer of the tensor | |||
| @@ -475,3 +517,13 @@ def LiteTensorConcat( | |||
| ) | |||
| result_tensor.update() | |||
| return result_tensor | |||
| def lite_dtype_2_numpy(dtype): | |||
| """ | |||
| convert lite dtype to corresponding numpy dtype | |||
| """ | |||
| assert isinstance( | |||
| dtype, LiteDataType | |||
| ), "input must be LiteDataType when using lite_dtype_2_numpy." | |||
| return _lite_type_to_nptypes[dtype] | |||
| @@ -21,6 +21,12 @@ def test_version(): | |||
| print("Lite verson: {}".format(version)) | |||
| def test_config(): | |||
| config = LiteConfig() | |||
| config.bare_model_cryption_name = "nothing" | |||
| print(config) | |||
| def test_network_io(): | |||
| input_io1 = LiteIO("data1", is_host=False, io_type=LiteIOType.LITE_IO_VALUE) | |||
| input_io2 = LiteIO( | |||
| @@ -32,6 +38,7 @@ def test_network_io(): | |||
| io = LiteNetworkIO() | |||
| io.add_input(input_io1) | |||
| io.add_input(input_io2) | |||
| io.add_input("data3", False) | |||
| output_io1 = LiteIO("out1", is_host=False) | |||
| output_io2 = LiteIO("out2", is_host=True, layout=LiteLayout([1, 1000])) | |||
| @@ -39,7 +46,7 @@ def test_network_io(): | |||
| io.add_output(output_io1) | |||
| io.add_output(output_io2) | |||
| assert len(io.inputs) == 2 | |||
| assert len(io.inputs) == 3 | |||
| assert len(io.outputs) == 2 | |||
| assert io.inputs[0] == input_io1 | |||
| @@ -47,9 +54,25 @@ def test_network_io(): | |||
| c_io = io._create_network_io() | |||
| assert c_io.input_size == 2 | |||
| assert c_io.input_size == 3 | |||
| assert c_io.output_size == 2 | |||
| ins = [["data1", True], ["data2", False, LiteIOType.LITE_IO_SHAPE]] | |||
| outs = [["out1", True], ["out2", False, LiteIOType.LITE_IO_VALUE]] | |||
| io2 = LiteNetworkIO(ins, outs) | |||
| assert len(io2.inputs) == 2 | |||
| assert len(io2.outputs) == 2 | |||
| io3 = LiteNetworkIO([input_io1, input_io2], [output_io1, output_io2]) | |||
| assert len(io3.inputs) == 2 | |||
| assert len(io3.outputs) == 2 | |||
| test_io = LiteIO("test") | |||
| assert test_io.name == "test" | |||
| test_io.name = "test2" | |||
| assert test_io.name == "test2" | |||
| class TestShuffleNet(unittest.TestCase): | |||
| source_dir = os.getenv("LITE_TEST_RESOURCE") | |||
| @@ -319,9 +342,9 @@ class TestNetwork(TestShuffleNet): | |||
| data = ios[key].to_numpy().flatten() | |||
| input_data = self.input_data.flatten() | |||
| assert data.size == input_data.size | |||
| assert io.name.decode("utf-8") == "data" | |||
| assert io.name == "data" | |||
| for i in range(data.size): | |||
| assert data[i] == input_data[i] | |||
| assert abs(data[i] - input_data[i]) < 1e-5 | |||
| return 0 | |||
| network.set_start_callback(start_callback) | |||
| @@ -343,7 +366,7 @@ class TestNetwork(TestShuffleNet): | |||
| output_data = self.correct_data.flatten() | |||
| assert data.size == output_data.size | |||
| for i in range(data.size): | |||
| assert data[i] == output_data[i] | |||
| assert abs(data[i] - output_data[i]) < 1e-5 | |||
| return 0 | |||
| network.set_finish_callback(finish_callback) | |||
| @@ -404,3 +427,27 @@ class TestNetwork(TestShuffleNet): | |||
| binary_equal_between_batch=True, | |||
| ) | |||
| self.do_forward(network) | |||
| def test_device_tensor_no_copy(self): | |||
| # construct LiteOption | |||
| net_config = LiteConfig() | |||
| net_config.options.force_output_use_user_specified_memory = True | |||
| network = LiteNetwork(config=net_config) | |||
| network.load(self.model_path) | |||
| input_tensor = network.get_io_tensor("data") | |||
| # fill input_data with device data | |||
| input_tensor.set_data_by_share(self.input_data) | |||
| output_tensor = network.get_io_tensor(network.get_output_name(0)) | |||
| out_array = np.zeros(output_tensor.layout.shapes, output_tensor.layout.dtype) | |||
| output_tensor.set_data_by_share(out_array) | |||
| # inference | |||
| for i in range(2): | |||
| network.forward() | |||
| network.wait() | |||
| self.check_correct(out_array) | |||
| @@ -54,6 +54,16 @@ def test_tensor_make(): | |||
| tensor = LiteTensor(layout, device_id=1) | |||
| assert tensor.device_id == 1 | |||
| tensor.layout = [8, 14] | |||
| assert tensor.layout.shapes[0] == 8 | |||
| assert tensor.layout.shapes[1] == 14 | |||
| assert tensor.layout.data_type == LiteDataType.LITE_FLOAT | |||
| tensor_new = LiteTensor(shapes=[1, 3, 224], dtype=np.int8) | |||
| assert tensor_new.layout.shapes[1] == 3 | |||
| assert tensor_new.layout.shapes[2] == 224 | |||
| assert tensor_new.layout.data_type == LiteDataType.LITE_INT8 | |||
| def test_tensor_set_data(): | |||
| layout = LiteLayout([2, 16], "int8") | |||
| @@ -292,3 +302,24 @@ def test_tensor_concat(): | |||
| for i in range(128): | |||
| index = j * 128 + i | |||
| assert real_data[index // 32][index % 32] == j | |||
| def test_tensor_get_memory_by_share(): | |||
| layout = LiteLayout([4, 32], "int16") | |||
| tensor = LiteTensor(layout) | |||
| assert tensor.nbytes == 4 * 32 * 2 | |||
| arr = np.ones([4, 32], "int16") | |||
| for i in range(128): | |||
| arr[i // 32][i % 32] = i | |||
| tensor.set_data_by_copy(arr) | |||
| test_data = tensor.get_data_by_share() | |||
| real_data = tensor.to_numpy() | |||
| for i in range(128): | |||
| assert real_data[i // 32][i % 32] == test_data[i // 32][i % 32] | |||
| arr[1][18] = 5 | |||
| arr[3][7] = 345 | |||
| tensor.set_data_by_copy(arr) | |||
| assert test_data[1][18] == 5 | |||
| assert test_data[3][7] == 345 | |||