.. _python-interface:
================================
MegEngine Lite python 接口介绍
================================
LiteTensor 相关 API
---------------------
LiteLayout
^^^^^^^^^^^^^^^^
.. code-block:: python
def __init__(self, shape=None, dtype=None)
指定 shape 和 dtype,将构造出一个 LiteLayout。
参数:
* shape:LiteLayout 中的 shape 信息。
* dtype:LiteLayout 中的数据类型,这个 dtype 可以为如下类型:
* 字符:"int32","float32","uint8","int8","int16","uint16","float16"。
* LiteDataType:LITE_FLOAT,LITE_HALF,LITE_INT,LITE_INT16,LITE_INT8,LITE_UINT8,LITE_UINT16。
* numpy dtype 实例:np.dtype("int32"),np.dtype("float32"),np.dtype("uint8"),np.dtype("int8"),np.dtype("int16"),np.dtype("uint16"),np.dtype("float16")。
* numpy dtype:numpy.int32,numpy.float32,numpy.uint8,numpy.int8,numpy.int16,numpy.uint16,numpy.float16。
LiteTensor
^^^^^^^^^^^^^^^^
.. code-block:: python
def __init__(
self,
layout=None,
device_type=LiteDeviceType.LITE_CPU,
device_id=0,
is_pinned_host=False,
shapes=None,
dtype=None,
):
构造 LiteTensor 时候可以指定 layout, device_type,device_id,以及是否内存是 `锁页内存 `_,另外
如果也可以通过传递关键词参数 shapes 和 dtype 在 MegEngineLite 内部构造 layout,让后再构造 Tensor。
参数
* layout:LiteTensor 对应的 layout 信息。
* device_type:LiteDeviceType 对应的数据类型,包含:
.. code-block:: python
class LiteDeviceType(IntEnum):
LITE_CPU = 0
LITE_CUDA = 1
LITE_ATLAS = 3
LITE_NPU = 4
LITE_DEVICE_DEFAULT = 5
* device_id:LiteTensor 所在设备的 id。
* is_pinned_host:LiteTensor 是否为 `锁页内存 `_。
.. warning::
LiteTensor 构造之后,内存没有立即申请,只有当 LiteTensor 需要用到内存时候才会申请。
示例:
.. code-block:: python
# 直接从 shapes 创建 LiteTensor
tensor_cuda2 = LiteTensor(shapes=[4,16], dtype="float32", device_type=LiteDeviceType.LITE_CUDA)
# 从 layout 创建 LiteTensor
layout = LiteLayout([4, 16], "float32")
tensor = LiteTensor(layout, LiteDeviceType.LITE_CPU)
tensor_cuda = LiteTensor(layout=layout, device_type=LiteDeviceType.LITE_CUDA)
LiteTensor 信息获取
^^^^^^^^^^^^^^^^^^^
.. code-block:: python
# 获取或者设置 LiteTensor 的 layout 信息
@property
def layout(self):
@layout.setter
def layout(self, layout):
# 获取 LiteTensor 是否是锁页内存
@property
def is_pinned_host(self):
# 获取 LiteTensor 所在的设备类型
@property
def device_type(self):
# 获取 LiteTensor 所在的设备 id
@property
def device_id(self):
# 获取 LiteTensor 的内存是否是连续的
@property
def is_continue(self):
# 获取 LiteTensor 的内存的大小,单位是字节
@property
def nbytes(self):
.. note::
上面 LiteTensor 的 layout 信息具有装饰器 @property 和 @layout.setter,可以直接作为成员一样访问和赋值,
其他信息都具有 @property 的装饰器,因此都可以通过成员一样的访问。
get_ctypes_memory
^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def get_ctypes_memory(self)
* get_ctypes_memory:将返回 ctypes.c_void_p 类型,其指向 Tensor 的内存地址,如果 Tensor 没有申请内存,将会申请内存。
reshape
^^^^^^^^^^
.. code-block:: python
def reshape(self, shape):
改变这个 LiteTensor 的 LiteLayout 中的 shape 为新的 shape,其中 **新的 shape 中元素个数需要和老的 shape 里面的元素个数相等**。
slice
^^^^^^^^
.. code-block:: python
def slice(self, start, end, step=None):
对 LiteTensor 进行切片,返回一个新的 LiteTensor,新的 LiteTensor 和原来 LiteTensor 共享内存, **新的 LiteTensor 可能不连续**
参数: **start,end 的长度必须相等,长度可以小于 Tensor 的 Layout 的维度,如果传递了 step,则 step 也需要和 start,end 的长度相等**。
* start:Tensor 每一维度的起始 index 组成的数组,从高维到低维。
* end:Tensor 每一维度的结束 index 组成的数组,从高维到低维。
* step:Tensor 每一维度切片的间距,从高维到低维,默认为1。
返回值:返回一个新的 LiteTensor。
示例:
.. code-block:: python
layout = LiteLayout([4, 8], "int32")
tensor1 = LiteTensor(layout)
tensor1.set_data_by_copy([i for i in range(32)])
real_data_org = tensor1.to_numpy()
tensor2 = tensor1.slice([1, 4], [3, 8])
assert tensor2.layout.shapes[0] == 2
assert tensor2.layout.shapes[1] == 4
assert tensor2.is_continue == False
real_data = tensor2.to_numpy()
for i in range(8):
row = i // 4
col = i % 4
assert real_data[row][col] == real_data_org[row + 1][col + 4]
fill_zero
^^^^^^^^^^^^^
.. code-block:: python
def fill_zero(self):
将 LiteTensor 内存里面的数据全部设置为 0。
copy_from
^^^^^^^^^^^^^^^^
.. code-block:: python
def copy_from(self, src_tensor):
从 src_Tensor 中拷贝数据到自己内存中, **如果 src_tensor 和自己的 layout 不相同时,会更改自身 Layout 信息为 src layout**。
share_memory_with
^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def share_memory_with(self, src_tensor):
将会和 src_tensor 共享内存数据, **如果 src_tensor 和自己的 LiteTensor 信息(layout,device_type,device_id等)不相同时,会更改自身信息为 src 的信息**。
示例:
.. code-block:: python
layout = LiteLayout([4, 8], "int16")
tensor1 = LiteTensor(layout)
tensor2 = LiteTensor(layout)
tensor1.set_data_by_copy([i for i in range(32)])
tensor2.share_memory_with(tensor1)
real_data = tensor2.to_numpy()
for i in range(32):
assert real_data[i // 8][i % 8] == i
update
^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def update(self):
将 LiteTensor 底层的信息更新到 python 中的 LiteTensor 中,包括 LiteTensor 的设备,设备 id,layout等信息。
set_data_by_copy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_data_by_copy(self, data, data_length=0, layout=None):
将用户指定的 data 以 **复制的方式** 到该 LiteTensor 中。
参数:
* data: data 可以是 list 或者 numpy ndarray 或者 ctypes 的 c_void_p。
* 当 data 类型为 list 时候,LiteTensor 的 Layout 不会被修改,用户需要保证 tensor 的内存大小大于 list 的长度。
* 当 data 为 numpy ndarray 时候,如果 data 的长度和 LiteTensor 的内存大小不等时,将修改 LiteTensor 的 layout 为 data 的 layout。
* 当 data 为 ctypes 的 c_void_p 时候,用户要么设置 data_length 并且必须 data_length LiteTensor 的长度相等,要么设置新的 Layout。
* data_length:当用户输入的 data 为 ctypes 的 c_void_p 时候,指明数据长度。
* layout 当需要改变 LiteTensor 的 layout 时,可以通过这个接口传递新的 layout。
.. warning::
* LiteTensor 必须是 `锁页内存 `_ 或者是 CPU 上的内存
示例:
.. code-block:: python
layout = LiteLayout([2, 16], "int8")
tensor = LiteTensor(layout)
data = [i for i in range(32)]
tensor.set_data_by_copy(data)
real_data = tensor.to_numpy()
for i in range(32):
assert real_data[i // 16][i % 16] == i
set_data_by_share
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_data_by_share(self, data, length=0, layout=None):
将用户传递进来的 data 通过 **共享的方式** 保存在 LiteTensor 中,避免 copy 带来的性能影响。
参数:
* data: data 可以是 numpy ndarray 或者 ctypes 的 c_void_p。
* 当 data 为 numpy ndarray 时候,如果 data 的长度和 LiteTensor 的内存大小不等时,将修改 LiteTensor 的 layout 为 data 的 layout。
* 当 data 为 ctypes 的 c_void_p 时候,用户要么设置 data_length 并且必须 data_length LiteTensor 的长度相等,要么设置新的 Layout。
* data_length:当用户输入的 data 为 ctypes 的 c_void_p 时候,指明数据长度。
* layout 当需要改变 LiteTensor 的 layout 时,可以通过这个接口传递新的 layout。
.. warning::
* 当 data 为 numpy ndarray 时候,LiteTensor 要么是 `锁页内存 `_ 要么是 CPU 上的内存
* 当 data 为 ctypes 的 c_void_p 时候,对 LiteTensor 没有要求,这时候需要用户自己保证内存的设备属性。
示例:
.. code-block:: python
layout = LiteLayout([2, 16], "int8")
tensor = LiteTensor(layout)
arr = np.ones([2, 16], "int8")
for i in range(32):
arr[i // 16][i % 16] = i
tensor.set_data_by_share(arr)
real_data = tensor.to_numpy()
for i in range(32):
assert real_data[i // 16][i % 16] == i
get_data_by_share
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def get_data_by_share(self):
将 LiteTensor 中的数据以 **共享** 的方式构建一个 numpy 的数组,并返回给用户, **当这个 LiteTensor 中内存数据被修改时候,返回的这个 numpy 数组中的
数据也将会被修改**。
返回值:
* 返回一个和 LiteTensor 共享内存的 numpy ndarray。
.. warning::
* 当这个 LiteTensor 中内存数据被修改时候,返回的这个 numpy 数组中的数据也将会被修改,如:
* 当第一次 Network Forward 之后通过输出 LiteTensor 获得的这个 numpy 数组,在下一次 Network Forward 的时候会被修改
示例:
.. code-block:: python
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()
for i in range(128):
assert test_data[i // 32][i % 32] == i
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
to_numpy
^^^^^^^^^^^^^^^^^
.. code-block:: python
def to_numpy(self):
将 LiteTensor 中数据 copy 到一个 numpy 的 ndarray 中,可以方便查看 LiteTensor 中的数据。
.. note::
* 当 LiteTensor 是 `锁页内存 `_ 或者是 CPU 上的 LiteTensor,则会直接 copy 到 numpy ndarray 中
* 当 LiteTensor 在其他设备上,这时会先 copy 到 CPU LiteTensor 中,再从新的 LiteTensor copy 到 numpy ndarray 中,所以可能有 **性能问题**。
LiteOptions
^^^^^^^^^^^^^^^^
.. code-block:: python
_fields_ = [
("weight_preprocess", c_int),
("fuse_preprocess", c_int),
("fake_next_exec", c_int),
("var_sanity_check_first_run", c_int),
("const_shape", c_int),
("force_dynamic_alloc", c_int),
("force_output_dynamic_alloc", c_int),
("force_output_use_user_specified_memory", c_int),
("no_profiling_on_shape_change", c_int),
("jit_level", c_int),
("comp_node_seq_record_level", c_int),
("graph_opt_level", c_int),
("async_exec_level", c_int),
# layout transform options
("enable_nchw44", c_int),
("enable_nchw44_dot", c_int),
("enable_nchw88", c_int),
("enable_nhwcd4", c_int),
("enable_nchw4", c_int),
("enable_nchw32", c_int),
("enable_nchw64", c_int),
]
LiteOptions 是一个包含 MegEngine Network 优化选项集合的结构体,每个选项的解释如下:
* weight_preprocess:在推理时候,部分 Kernel 执行前需要对权重进行转换,或者 Relayout,开启这个选项之后,将权重处理放到 Kernel 执行之前, **优化 Kernel** 执行时间,但是 Network 初始化时间变长。
* fuse_preprocess:开启该选项之后,模型中的部分前后处理 Operator 将会被融合在一起,优化模型执行的性能。
* fake_next_exec:下一次执行 Inference 时候,是否为假的执行:仅仅完成内存分配等和计算无关的操作。这次假的执行完成之后将被设置为 false。
* var_sanity_check_first_run:第一次执行 Inference 时候是否需要对每一个 Operator 的输入输出 Tensor 的正确性进行检查,默认为 true。
* const_shape:指定 Network 的输入 shape 不会变化,这样不用在后面的执行时检查是否需要重新分配内存等操作。
* force_dynamic_alloc:强制要求所有的 Tensor 都是运行时动态分配,且不进行内存优化,MegEngine 默认所有的 Tensor 都是执行前进行内存优化并静态申请。
* force_output_dynamic_alloc:强制最后输出的 Tensor 的内存为动态申请,这样输出 Tensor 不用 copy 到用户的内存中,可以直接代理到返回内存给用户。
* force_output_use_user_specified_memory:强制让输出 Tensor 的内存由用户指定,这样输出 Tensor 将不需要 copy 到用户内存,在最后一个 Kernel 计算时就写到了用户的内存地址中。
* no_profiling_on_shape_change:当 Network 的输入 Tensor 的 shape 改变的时候,这时候 fast-run 将不会进行重新搜索最优的 kernel 算法实现。
* jit_level:JIT 的级别,设置为 0 时:将关闭 JIT,设置为 1 时:仅仅只开启基本的 elemwise 的 JIT,当是指为 2 时:将开启 elemwise 和 reduce Operator 的 JIT。
* comp_node_seq_record_level:设置 MegEngine 的录制模式,当设置为 0 时:将不开启录制模式,设置为 1 时:将开启录制模式,不会析构这个计算图结构,当设置为 2 时:将开启录制模式,并释放掉整个计算图。
* graph_opt_level:设置图优化等级,当设置为 0 时:关闭图优化,当设置为 1 时:算术计算 inplace 优化,当设置为 2 时:在 1 的基础上在加上全局优化,当设置为 3 时:在 2 的基础上再使能 JIT。
* enable_xxxx:开启对应的 layout 转换优化,不同的平台上不同的 layout 性能差异较大,见下表:
+-------------------+----------------------------------------------------+-------------+
| 参数 | 作用 | 适用平台 |
+===================+====================================================+=============+
| enable-nchw88 | 将输入nchw layout的模型转为nchw88 layout的模型 | X86 avx256 |
+-------------------+----------------------------------------------------+-------------+
| enable-nchw44 | 将输入nchw layout的模型转为nchw44 layout的模型 | Arm float32 |
+-------------------+----------------------------------------------------+-------------+
| enable-nchw44-dot | 将输入nchw layout的模型转为nchw44-dot layout的模型 | Arm V8.2 |
+-------------------+----------------------------------------------------+-------------+
| enable-nchw4 | 将输入nchw layout的模型转为nchw4 layout的模型 | CUDA |
+-------------------+----------------------------------------------------+-------------+
| enable-chwn4 | 将输入nchw layout的模型转为chwn4 layout的模型 | CUDA |
+-------------------+----------------------------------------------------+-------------+
| enable-nchw32 | 将输入nchw layout的模型转为nchw32 layout的模型 | CUDA |
+-------------------+----------------------------------------------------+-------------+
| enable-nhwcd4 | 将输入nchw layout的模型转为nhcw4 layout的模型 | 移动平台GPU |
+-------------------+----------------------------------------------------+-------------+
.. _lite_config:
LiteConfig
^^^^^^^^^^^^^^^^
.. code-block:: python
_fields_ = [
("has_compression", c_int),
("device_id", c_int),
("device_type", c_int),
("backend", c_int),
("bare_model_cryption_name", c_char_p),
("options", LiteOptions),
]
* has_compression: 模型是否压缩过。
* device_id: LiteNetwork 创建所在的设备 id。
* device_type:LiteNetwork 创建所在的设备类型。
* backend:指运行 LiteNetwork 的后端推理框架,目前默认是:MegEngine。
* bare_model_cryption_name:如果模型有加密,则指明加密算法的名字,如果没有加密,则不用配置。
* options 模型的优化参数,如上面所示。
.. _lite_io:
LiteIO
^^^^^^^^^^^^^^^^
.. code-block:: python
_fields_ = [
("name", c_char_p),
("is_host", c_int),
("io_type", c_int),
("config_layout", LiteLayout),
]
LiteIO 为指定模型中输入输出 LiteTensor 所在的位置,可以在 device 端,也可以配置在 CPU 端,如果不配置,默认为 CPU 端。
* name:LiteTensor 的名字,字符串。
* is_host:LiteNetwork 创建所在的设备 id。
* io_type:指定该 LiteTensor 对应的IO类型,目前支持两种类型,分别是:LITE_IO_VALUE 和 LITE_IO_SHAPE,默认为 LITE_IO_VALUE 。
* config_layout:提前配置好的 layout,不配置默认为模型中的 layout。
LiteNetworkIO
^^^^^^^^^^^^^^^^
.. code-block:: python
def __init__(self, inputs=None, outputs=None):
LiteNetworkIO 是 LiteNetwork 构造时候的 IO 信息的集合,包含 inputs 和 outputs,为用户指定的上述 LiteIO,另外用户可以通过 add_input,add_output
接口添加 LiteIO 到 LiteNetworkIO 中。
示例:
.. code-block:: python
input_io1 = LiteIO("data1", is_host=False, io_type=LiteIOType.LITE_IO_VALUE)
input_io2 = LiteIO(
"data2",
is_host=True,
io_type=LiteIOType.LITE_IO_SHAPE,
layout=LiteLayout([2, 4, 4]),
)
io = LiteNetworkIO([input_io1, input_io2])
io.add_output("out1", is_host=False)
io.add_output("out2", is_host=True, layout=LiteLayout([1, 1000]))
assert len(io.inputs) == 2
assert len(io.outputs) == 2
LiteNetwork 相关 API
---------------------
LiteNetwork
^^^^^^^^^^^^^^^^
.. code-block:: python
def __init__(self, config=None, io=None):
构造一个 LiteNetwork,可以传递两个参数分别是 config 和 io。
参数:
* config:模型优化需要的 LiteConfig 类型配置,默认为 None。
* io: LiteNetworkIO 类型,指定用户输入输出 LiteTensor 的信息。
示例:
.. code-block:: python
config = LiteConfig()
config.options.var_sanity_check_first_run = 0
config.device_type = LiteDeviceType.LITE_CUDA
ios = LiteNetworkIO(inputs=[LiteIO("data", False)])
network = LiteNetwork(config=config, io=ios)
load
^^^^^^^^^^^^^^^^
.. code-block:: python
def load(self, path):
指定创建 LiteNetwork 的模型路径,并解析这个模型,加载到内存中。
forward
^^^^^^^^^^^^^^^^
.. code-block:: python
def forward(self):
对指定创建 LiteNetwork 进行 forward。
wait
^^^^^^^^^^^^^^^^
.. code-block:: python
def wait(self):
等待指定创建 LiteNetwork 进行 forward 完成。
获取 LiteNetwork 相关信息
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
# 获取 LiteNetwork 的运行所在的设备 id
@property
def device_id(self):
# 获取 LiteNetwork 的运行所在的执行流 id
@property
def stream_id(self):
# 获取 LiteNetwork 的运行在 CPU 多线程时候的线程个数
@property
def threads_number(self):
# 获取 LiteNetwork 中输入 LiteTensor 中第 index 个的名字
def get_input_name(self, index):
# 获取 LiteNetwork 中输出 LiteTensor 中第 index 个的名字
def get_output_name(self, index):
# 获取 LiteNetwork 中所有输入 LiteTensor 的名字,返回一个 list
def get_all_input_name(self):
# 获取 LiteNetwork 中所有输出 LiteTensor 的名字,返回一个 list
def get_all_output_name(self):
# 获取 LiteNetwork 运行时候需要的内存信息,并将内存信息 dump 到 log_dir 指定的目录下
def get_static_memory_alloc_info(self, log_dir="logs/test"):
# 获取该 LiteNetwork 在 CPU 上运行时,是否为 inplace 模式
def is_cpu_inplace_mode(self):
.. note::
inplace 模式为:运行模型时候只有一个线程,这个线程发送 Kernel 任务的同时,inplace 地将
kernel 执行计算任务。非 inplace 模式:将有2个线程,一个线程发送 Kernel 任务,一个线程执行 Kernel 任务。在一些单核处理器
或者低端 cpu 上,设置 **inplace 模式性能会好一些**。
设置 LiteNetwork 相关信息
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
# 设置模型运行使用的设备 id
@device_id.setter
def device_id(self, device_id):
# 设置模型运行使用的执行流 id
@stream_id.setter
def stream_id(self, stream_id):
# 如果模型执行在 CPU 多线程的情况下,设置模型运行时候需要的线程数量
@threads_number.setter
def threads_number(self, nr_threads):
# 如果模型在 CPU 上执行,设置模型运行模式为:inplace 模式
def enable_cpu_inplace_mode(self):
# 设置模型运行使用 TensorRT 进行推理
def use_tensorrt(self):
.. warning::
上面这些 LiteNetwork 的运行时的信息设置需要在 LiteNetwork 创建之后,模型 load 之前进行设置,否则将报错。
get_io_tensor
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def get_io_tensor(self, name, phase=LiteTensorPhase.LITE_IO):
获取 LiteNetwork 中名字为 name 的输入或者输出 LiteTensor。
参数:
* name:字符串,指定输入或者输出 LiteTensor 的名字。
* phase:当有输入和输出 LiteTensor 名字重复时候,指明获取的 LiteTensor 来自输入或者输出,可以设置为:
* LiteTensorPhase.LITE_IO:在输入和输出的所有 LiteTensor 中寻找指定 name 的 LiteTensor,名字不会重复的情况下。
* LiteTensorPhase.LITE_INPUT:在输入的所有 LiteTensor 中寻找指定 name 的 LiteTensor。
* LiteTensorPhase.LITE_OUTPUT:在输出的所有 LiteTensor 中寻找指定 name 的 LiteTensor。
share_weights_with
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def share_weights_with(self, src_network):
设置 LiteNetwork 运行和 src_network 共享同一份权重,两个 LiteNetwork 可以对不同的输入数据进行推理,也可以同时运行。
share_runtime_memroy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def share_runtime_memroy(self, src_network):
设置 LiteNetwork 运行和 src_network 共享运行时候的内存, **这时 self 和 src_network 不能同时执行**,
运行时内存指:除了保存模型 weights 和图结构以外的所有需要的运行时内存。
async_with_callback
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def async_with_callback(self, async_callback):
设置模型 forward 运行在异步模式,异步模式中,主线程将不会被阻塞,当 LiteNetwork 执行完成之后将执行 async_callback,告诉主线程执行完成。
示例:
.. code-block:: python
count = 0
finished = False
def async_callback():
nonlocal finished
finished = True
return 0
config = LiteConfig()
config.options.var_sanity_check_first_run = 0
network = LiteNetwork(config=config)
network.load(model_path)
network.async_with_callback(async_callback)
network.forward()
while not finished:
count += 1
assert count > 0
output_data = output_tensor.to_numpy()
set_start_callback
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_start_callback(self, start_callback):
设置模型运行之前的回调函数,用户可以通过这个回调函数检查输入数据是否满足要求。
set_finish_callback
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_finish_callback(self, finish_callback):
设置模型运行之后的回调函数,用户可以通过这个回调函数检查输出数据是否满足要求。
示例:
.. code-block:: python
network = LiteNetwork()
network.load(model_path)
finish_checked = False
def finish_callback(ios):
nonlocal finish_checked
finish_checked = True
assert len(ios) == 1
for key in ios:
io = key
data = ios[key].to_numpy().flatten()
output_data = self.correct_data.flatten()
...
return 0
network.set_finish_callback(finish_callback)
network.forward()
network.wait()
assert finish_checked == True
enable_profile_performance
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def enable_profile_performance(self, profile_file):
模型运行时候,对模型中的各个 Operator 进行速度测试,并将测试结果写到指定的 profile_file 中,得到的这个 profile 文件为 json 文件,
可以使用 MegEngine 中指定的 tool 进行解析。
set_network_algo_workspace_limit
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_network_algo_workspace_limit(self, size_limit):
模型运行时候,模型中每一个 Operator 运行时候选择的算法最大能够用到的 workspace 大小,超过 size_limit 大小的算法将不会被选择,其中 size_limit 的单位为字节。
.. _set_network_algo_policy_python:
set_network_algo_policy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_network_algo_policy(
self, policy, shared_batch_size=0, binary_equal_between_batch=False
):
设置模型运行时候选择每个 Operator 算法的策略。
参数:
* policy 选择算法的策略,MegEngine Lite 中支持以下策略:
.. code-block:: python
class LiteAlgoSelectStrategy(IntEnum):
"""
operation algorithm seletion strategy type, some operations have
multi algorithms, different algorithm has different attribute, according to
the strategy, the best algorithm will be selected.
Note: These strategies can be combined
LITE_ALGO_HEURISTIC | LITE_ALGO_PROFILE means: if profile cache not valid,
use heuristic instead
LITE_ALGO_HEURISTIC | LITE_ALGO_REPRODUCIBLE means: heuristic choice the
reproducible algo
LITE_ALGO_PROFILE | LITE_ALGO_REPRODUCIBLE means: profile the best
algorithm from the reproducible algorithms set
LITE_ALGO_PROFILE | LITE_ALGO_OPTIMIZED means: profile the best
algorithm form the optimzed algorithms, thus profile will process fast
LITE_ALGO_PROFILE | LITE_ALGO_OPTIMIZED | LITE_ALGO_REPRODUCIBLE means:
profile the best algorithm form the optimzed and reproducible algorithms
"""
LITE_ALGO_HEURISTIC = 1
LITE_ALGO_PROFILE = 2
LITE_ALGO_REPRODUCIBLE = 4
LITE_ALGO_OPTIMIZED = 8
其中上面的策略在不冲突的情况下,可以进行与操作,然后组合在一起。
* shared_batch_size:binary_equal_between_batch 的时候,选择最优算法所依据的 batch 大小,设置 0 将使用模型默认的 batch size。
* binary_equal_between_batch: 多个 batch 同时进行计算时,如果输入完全一样,保证所有 batch 的计算结果完全一样。
io_txt_dump
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def io_txt_dump(self, txt_file):
将 LiteNetwork 运行时候的所有 IO tensor 输出到文本文件 io_txt_out_file 中。
io_bin_dump
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def io_bin_dump(self, bin_dir):
将 LiteNetwork 运行时候的所有 IO tensor 以二进制的形式保存在 bin_dir 文件夹中。
全局设置相关 API
---------------------
全局接口在 MegEngine Lite 中都封装在 LiteGlobal 中,都作为它的静态函数存在。
register_decryption_and_key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
@staticmethod
def register_decryption_and_key(decryption_name, decryption_func, key):
注册用户自定义的模型解密算法到 MegEngine Lite 中,包括解密方法和解密需要的秘钥。
参数:
* decryption_name:解密算法的名字,字符串。
* decryption_func:解密算法的方法,以及闭包函数。
* key:解密算法的秘钥。
示例:
.. code-block:: python
@decryption_func
def function(in_arr, key_arr, out_arr):
if not out_arr:
return in_arr.size
else:
for i in range(in_arr.size):
out_arr[i] = in_arr[i] ^ key_arr[0] ^ key_arr[0]
return out_arr.size
LiteGlobal.register_decryption_and_key("just_for_test", function, [15])
config = LiteConfig()
config.bare_model_cryption_name = "just_for_test".encode("utf-8")
network = LiteNetwork(config)
model_path = os.path.join(self.source_dir, "shufflenet.mge")
network.load(model_path)
update_decryption_key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
@staticmethod
def update_decryption_key(decryption_name, key):
更新 MegEngine Lite 中 build-in 的解密算法的秘钥。
* decryption_name:解密算法的名字,目前 MegEngine Lite 中写了三种加密算法,分别是:"AES_default","RC4_default" 和 "SIMPLE_FAST_RC4_default"。
* 对应的秘钥:"AES_default" 为 32 字节数组,"RC4_default" 和 "SIMPLE_FAST_RC4_default" 为 16 自己数组。
示例:
.. code-block:: python
wrong_key = [0] * 32
LiteGlobal.update_decryption_key("AES_default", wrong_key)
with self.assertRaises(RuntimeError):
config = LiteConfig()
config.bare_model_cryption_name = "AES_default".encode("utf-8")
network = LiteNetwork(config)
model_path = os.path.join(self.source_dir, "shufflenet_crypt_aes.mge")
network.load(model_path)
right_key = [i for i in range(32)]
LiteGlobal.update_decryption_key("AES_default", right_key)
config = LiteConfig()
config.bare_model_cryption_name = "AES_default".encode("utf-8")
network = LiteNetwork(config)
model_path = os.path.join(self.source_dir, "shufflenet_crypt_aes.mge")
network.load(model_path)
set_loader_lib_path
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
@staticmethod
def set_loader_lib_path(path):
当第三方硬件以 loader 的形式接入到 MegEngine 中,该接口用于用户设置对应 loader 的执行动态库,path 为执行的动态库的路径。
set_persistent_cache
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
@staticmethod
def set_persistent_cache(path, always_sync=False):
设置当前 MegEngine Lite 中模型运行的算法 cache,模型运行时将从这个 cache 中取出对应 Operator 的算法信息,并解析找到执行算法,并运行,
这将节省模型运行时候搜索最优的算法时候,用户可以提前搜索好对应的 cache。
dump_persistent_cache
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
@staticmethod
def dump_persistent_cache(path):
将当前 MegEngine Lite 中模型运行的算法的 cache 从内存中 dump 到指定文件中,该方法可以用户用户提前所有最优算法的 cache。
dump_persistent_cache
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
@staticmethod
def get_device_count(device_type):
获取指定 device_type 类型的设备数量。
try_coalesce_all_free_memory
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def try_coalesce_all_free_memory():
释放当前 MegEngine Lite 中所有不在需要的内存,这样将减少当前系统内存使用峰值。
tensorrt_cache
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_tensorrt_cache(path):
def dump_tensorrt_cache():
设置以及下载 tensorRT 的 cache。
set_log_level
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def set_log_level(LiteLogLevel):
class LiteLogLevel(IntEnum):
"""
DEBUG: The most verbose level, printing debugging info
INFO: The default level
WARN: Printing warnings
ERROR: The least verbose level, printing errors only
"""
DEBUG = 0
INFO = 1
WARN = 2
ERROR = 3
设置 MegEngine Lite 的 log 级别,改函数不在 LiteGlobal 类中,是一个独立的全局函数。
物理地址和虚拟地址操作
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def register_memory_pair(
vir_ptr, phy_ptr, length, device, backend=LiteBackend.LITE_DEFAULT
):
def clear_memory_pair(vir_ptr, phy_ptr, device, backend=LiteBackend.LITE_DEFAULT):
def lookup_physic_ptr(vir_ptr, device, backend=LiteBackend.LITE_DEFAULT):
部分设备上有虚拟地址和物理地址的概念,这里提供用户操作虚拟地址和物理地址的接口,主要有:
* 设置全局的物理地址和虚拟地址对
* 清除这些地址对
* 通过虚拟地址查询物理地址
.. _lite_utils_api:
Utils API
---------------------
MegEngine Lite 现在有一个 utils ,TensorBatchCollector,主要为方便用户在进行推理之前收集多个 batch 数据,然后将攒出来的一个多个 batch 的数据
同时放到 LiteNetwork 中进行推理,避免不必要的内存拷贝。
TensorBatchCollector
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def __init__(
self,
shape,
dtype=LiteDataType.LITE_INT8,
device_type=LiteDeviceType.LITE_CUDA,
device_id=0,
is_pinned_host=False,
tensor=None,
):
创建一个 TensorBatchCollector,这个 TensorBatchCollector 默认数据类型是 INT8,设备为 CUDA。
参数:
* shape:用户指定 TensorBatchCollector 的 shape。
* dtype:具体的数据类型,可以是 LITE_FLOAT,LITE_HALF,LITE_INT,LITE_INT16,LITE_INT8,LITE_UINT8,LITE_UINT16。
* device_type:具体的设备类型。
* device_id:TensorBatchCollector 所在的设备 id。
* is_pinned_host:该 TensorBatchCollector 申请的内存是否为: `锁页内存 `_ 。
* tensor:可选的用户设置已经创建好的 LiteTensor 到 TensorBatchCollector 中。
collect_id
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def collect_id(self, array, batch_id):
设置该 TensorBatchCollector 中指定 batch_id 的数据为用户输入的 array。
参数:
* array:可以是 numpy 的 ndarry,也可以是 LiteTensor 类型。
* 如果是 numpy 的 ndarry,MegEngine Lite 将调用 LiteTensor 的 set_data_by_copy 将数据 copy 到指定的 batch_id 的内存中。
* 如果是 LiteTensor 类型,MegEngine Lite 将调用 LiteTensor 的 copy_from 完成数据 copy。
* batch_id:用户指定将要拷贝 array 数据的目标 batch。
collect_by_ctypes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def collect_by_ctypes(self, data, length):
当用户的数据为 ctypes 的 c_void_p,可以调用该接口将数据设置到第一个空着的 batch 中。
collect
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def collect(self, array):
当用户需要顺序的搜集batch,如从 0 一直到最大 batch,可以直接调用该接口。
free
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
def free(self, indexes):
释放指定的 indexes,indexes 是一个 list。
get
^^^^^^^^
.. code-block:: python
def get(self):
获得该 TensorBatchCollector 中内部存储数据的完整 LiteTensor。
to_numpy
^^^^^^^^
.. code-block:: python
def to_numpy(self):
获得该 TensorBatchCollector 中的数据保存在 numpy 的 array 中,并返回。
* 示例1:顺序的进行攒 batch
.. code-block:: python
batch_tensor = TensorBatchCollector(
[4, 8, 8], dtype=LiteDataType.LITE_INT, device_type=LiteDeviceType.LITE_CUDA
)
arr = np.ones([8, 8], "int32")
for j in range(2):
for i in range(4):
batch_tensor.collect(arr)
arr += 1
batch_tensor.free(range(4))
data = batch_tensor.to_numpy()
assert data.shape[0] == 4
assert data.shape[1] == 8
assert data.shape[2] == 8
for i in range(4):
for j in range(64):
assert data[i][j // 8][j % 8] == i + 4 + 1
* 示例2:通过指定 batch_id 进行攒 batch
.. code-block:: python
batch_tensor = TensorBatchCollector(
[4, 8, 8], dtype=LiteDataType.LITE_INT, device_type=LiteDeviceType.LITE_CUDA
)
arr = np.ones([8, 8], "int32")
arr += 1 # ==2
batch_tensor.collect_id(arr, 1)
arr -= 1 # ==1
batch_tensor.collect_id(arr, 0)
arr += 2 # ==3
batch_tensor.collect_id(arr, 2)
arr += 1 # ==4
batch_tensor.collect_id(arr, 3)
data = batch_tensor.to_numpy()
batch_tensor.free(range(4))
assert data.shape[0] == 4
assert data.shape[1] == 8
assert data.shape[2] == 8
for i in range(4):
for j in range(64):
assert data[i][j // 8][j % 8] == i + 1
* 示例3:通过 ctpes 进行攒 batch
.. code-block:: python
all_tensor = LiteTensor(
LiteLayout([4, 6, 8], dtype=LiteDataType.LITE_INT),
device_type=LiteDeviceType.LITE_CUDA,
)
batch_tensor = TensorBatchCollector([4, 6, 8], tensor=all_tensor)
nparr = np.ones([6, 8], "int32")
for j in range(2):
for i in range(4):
batch_tensor.collect(nparr)
nparr += 1
batch_tensor.free(range(4))
data = batch_tensor.to_numpy()
assert data.shape[0] == 4
assert data.shape[1] == 6
assert data.shape[2] == 8
for i in range(4):
for j in range(48):
assert data[i][j // 8][j % 8] == i + 4 + 1
* 示例4:通过 LiteTensor 进行攒 batch
.. code-block:: python
batch_tensor = TensorBatchCollector(
[4, 6, 8], dtype=LiteDataType.LITE_INT, device_type=LiteDeviceType.LITE_CPU
)
nparr = np.ones([6, 8], "int32")
tensor = LiteTensor(LiteLayout([6, 8], LiteDataType.LITE_INT))
for j in range(2):
for i in range(4):
tensor.set_data_by_share(nparr)
batch_tensor.collect(tensor)
nparr += 1
batch_tensor.free(range(4))
data = batch_tensor.to_numpy()
assert data.shape[0] == 4
assert data.shape[1] == 6
assert data.shape[2] == 8
for i in range(4):
for j in range(48):
assert data[i][j // 8][j % 8] == i + 4 + 1