ONNX Edit
rknn.utils.onnx_edit 是 RKNN-Toolkit2 的内置工具,用于调整onnx模型。对于一些具有特殊结构的onnx模型,调整后的模型在转换为RKNN后可以获得更好的推理性能。接口定义如下:
from rknn.utils import onnx_edit
onnx_edit(model: str,
export_path : str,
inputs_transform : dict = {},
outputs_transform : dict = {},
dataset: Optional[str] = None):
"""
Args:
model: The path to the onnx file.
export_path: The path to the exported onnx file.
inputs_transform: The dictionary of inputs transform equation. Feed like {'input_0': 'a,b,c->1,ab,1,c', ...}
outputs_transform: The dictionary of outputs transform equation. Feed like {'output_2': 'a,b,c,->1,b,1,ac', ...}
dataset: The path to the dataset file. dataset file should meet rknn.build requirements. If given, transform of inputs will also apply on dataset and generate a new dataset on the same folder of export_path.
"""
目前可以使用以下功能:
- 对输入和输出进行 reshape 和 transpose 调整(目前暂不支持对动态shape进行修改)
1.对输入和输出进行 reshape 和 transpose 调整
如果某些模型在转换为 RKNN 之前,能够对输入输出进行特定的 reshape 和 transpose 操作,则可以利用 RKNN-Toolkit2 的图优化功能,使最终的 RKNN 模型更加简洁,具有更好的推理性能。例如,通过执行该目录下的 test.py 脚本,可以比较 onnx_edit 前后生成的 RKNN 模型的差异。
变换方程书写规则:
- 必须有两部分字符组成,并用 '->' 隔开,左边为原始字符shape,右边是变换后字符shape
- 字符shape里面的符号只允许是 [a-z] 或 ',' '1',除了'1'以外,不支持其他数字shape字符,原因是相同数字字符无法判断 transpose 的前后关系
- 字符[a-z]认为是独立的,没有顺序关系,即
a,b->b,a
和c,a->a,c
表示的是一样的变换 - 等式左边的原始字符shape,被 ',' 分隔成 n 份,n的个数必须和模型的维度匹配,例如原始输入定义是 [32,4,1,64], 输入字符可以是 'a,b,c,d' 或 'a,b,1,d'
- 原始字符shape的每一个符号,除了'1',都必须存在于变换后字符shape,例如
a,1,c,d->ac,d,1
是有效的,a,1,c,d->ac,1
是无效的,缺了 'd' - 变换后字符shape,允许插入任意个数的 '1',达成扩维效果,例如
a,b,c-> a,1,cb,1,1
- 原始字符shape,允许用多个字母以及赋值公式来表示对shape进行拆分,例如原始输入定义是 [32,4,1,64],
ab,c,d,qk[a=2,k=8]->aq,cd,1,kb
,表示将 32 拆分成 2x16,将 64 拆分成 8x8,再进行 transpose, reshape 操作。其中 '[ ]' 的部分称为赋值公式,多个公式用 ',' 符号分隔。此外,允许拆分中的某个字符没有赋值,此时会自动推断对应的shape,例如赋值公式只给了 a=2,已知在模型中 ab=32,则自动推断出 b=16;若推断出的shape异常会直接报错,比如 ab=32,若赋值 a=5,则 b=6.4,又维度必须是整数,此时会抛出异常错误。 - 支持动态shape
- 若对动态shape的维度进行拆分,拆分后的维度只允许其中一个是动态shape,假设有3维度动态shape,则
ab,c,d->a,c,d,b
是无效的,因为a,b均是未知维度,无法进行拆分操作。而ab,c,d[a=2]->a,c,d,b
是有效的 - 允许对动态shape的维度进行赋值,此时对应维度会变成固定shape。假设有3维度动态shape,
ab,c,d[a=2]->a,c,d,b
会使变更后的维度为 [2,c,d,b],第一维为2,而ab,c,d[a=2,b=5,c=7,d=11]->a,c,d,b
会使变更后的维度变成 [2,7,11,5]
以下是一些变换例子:
1. Reshape 3D to 4D: 'a,b,c->a,b,1,c' or 'a,b,c->a,1,b,c'
2. Reshape 5D to 4D: 'a,b,c,d,e->ab,c,d,e' or 'a,b,c,d,e->a,bce,1,1'
3. Transpose(0,3,1,2): 'a,b,c,d->a,d,b,c'
4. Transpose, merge dim: 'a,b,c,d->ad,c,b' or 'a,b,c,d->d,acb,1'
5. Split dim, transpose, merge dim: 'a,bc,de,f[b=2,d=4]->ab,fe,dc,1', # 'c','e' will be auto infered
6. Split dim, transpose, merge dim: 'a,bc,de,f[b=2,c=24,d=4]->ab,fe,dc,1', # 'bc' = 24 shuold be equal to tensor's 2nd-dim, 'e' will be auto infered
执行 test.py
后,onnx_edit 接口会打印出以下变换的公式:
I For 'k_cache.1':'a,b,c,d->1,ad,b,c'
Input:'k_cache.1' was reset as shape-[1, 32, 48, 1].(Origin shape is [4, 48, 1, 8])
Insert ops to transform [1, 32, 48, 1] to [4, 48, 1, 8]:
- Insert reshape op. [1, 32, 48, 1] reshape to [4, 8, 48, 1].
- Insert transpose op. [4, 8, 48, 1] transpose(0, 2, 3, 1) to [4, 48, 1, 8].
I For 'k_cache':'a,b,c,d->1,ab,c,d'
Output:'k_cache' was reset as shape-[1, 32, 48, 1].(Origin shape is [4, 8, 48, 1])
Insert ops to transform [4, 8, 48, 1] to [1, 32, 48, 1]:
- Insert reshape op. [4, 8, 48, 1] reshape to [1, 32, 48, 1].