This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TDA4VM:部署自定义语义分割 ONNx 模型

Guru**** 2535150 points
Other Parts Discussed in Thread: TDA4VM

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1388183/tda4vm-deploying-custom-semantic-segmentation-onnx-model

器件型号:TDA4VM

工具与软件:

您好!

我尝试使用自定义的 onnx 模型在 TDA4VM 板上执行语义分割推理。 我尝试过两种方法:


1.使用 onnxruntime 以及 TIDLCompilationProvider 和以下提供程序选项生成推理工件

TIDL_OPTIONS = {
   'tidl_tools_path' : get_tidl_tools_path(),
   'artifacts_folder' : WORKDIR + 'artifacts/',
   "platform":"J7",
   "version":"7.2",
   'debug_level' : 2,
   'tensor_bits' : 8,
   "ti_internal_nc_flag" : 1601,
   'advanced_options:calibration_frames' : 10,
   'advanced_options:calibration_iterations' : 5,
   'accuracy_level' : 1,
}

这不返回错误、并生成所需的伪影、但基于这些伪影和 onxruntime (具有 TIDLExecutionProvider)在电路板上进行的推理会使我的模型的性能严重下降(接近零精度)。


2.使用 onnxruntime.quantizing.quantize_static 预量化模型、然后尝试使用带有提供程序选项的 TIDLCompilationProvider 生成伪影

TIDL_OPTIONS = {
   'tidl_tools_path' : get_tidl_tools_path(),
   'artifacts_folder' : WORKDIR + 'artifacts/',
   "platform":"J7",
   "version":"7.2",
   'debug_level' : 2,
   'tensor_bits' : 8,
   "ti_internal_nc_flag" : 1601,
   'advanced_options:prequantized_model': 1,
   'accuracy_level' : 9,
}

这也不会返回错误、但不会在工件文件夹中生成所有必需工件。 它生成 allowedNode.txt 和 onnxrtMetaData.txt 文件、但是没有.bin 文件;.bin 文件位于工件文件夹的 tempDir 子目录中、但是这些工件的文件大小是它们应该的4倍(表示的是浮点数据而不是 INT8)、并且它们无法成功地用于电路板上的推理。 另请注意、在 CPU 上运行预量化模型会产生高精度网络预测、因此我确信我的网络可以量化到 INT8而不会大幅降低准确性。

这个问题似乎与我使用的实际模型无关。 下面、我附上了一个随机初始化的2层 conv net 的最小示例、当根据上面的方法1进行编译+评估时、会产生低质量预测、而预量化会在 CPU 上产生更好的质量预测、但在编译时不会产生.bin 伪影。 如果您想运行此脚本以自行查看问题:

  1. 对于方法1、运行  
    1. python3 minimal_workflow.py -c [在采用标准设置的 TI 开发 Docker 中]
    2. scp -r ti_comp_test minimal_workflow.py tda4vm:[即将脚本和模型复制到电路板上]
    3. python3 minimal_workflow.py -e [在电路板上]
  2. 对于方法2、运行
    1. python3 minimal_workflow.py -p [在 TI Docker 开发中]
    2. LS ti_comp_test/伪 影/  

 

步骤1c 和2a 打印精度数字、说明量化的影响;请注意、1c 的数字比2a 的数字差得多。 步骤2b 显示编译预量化模型时不会生成.bin 文件。

[请注意、第1a 步确实会在 stdout 中显示一些警告、但未遇到致命错误;对于我的语义分割模型、没有类似这些警告]

非常感谢在这方面提供任何帮助! 我更喜欢使用方法2 (因为这给了我量化算法更大的灵活性)、但修复方法1也是向前迈出的一步! 感谢您的帮助!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我在文章中提到的 python 脚本似乎未正确连接、因此我将其发布在此处、以内联方式。

    import os
    import argparse
    import numpy as np
    import onnxruntime as rt
    
    
    def get_tidl_tools_path():
        if 'TIDL_TOOLS_PATH' in os.environ: return os.environ['TIDL_TOOLS_PATH']
        if os.path.exists('/home/root/tidl_tools'): return '/home/root/tidl_tools'
        if os.path.exists('/home/root/edgeai-tidl-tools'): return '/home/root/edgeai-tidl-tools'
        else: raise FileNotFoundError('Please set TIDL_TOOLS_PATH environment variable')
    
    WORKDIR = './ti_comp_test/'
    TIDL_PARAMS = {
        'tidl_tools_path' : get_tidl_tools_path(),
        'artifacts_folder' : WORKDIR + 'artifacts/',
        "platform":"J7",
        "version":"7.2",
        'debug_level' : 2,
        'tensor_bits' : 8,
        "ti_internal_nc_flag" : 1601,
    }
    IN_SHAPE=(1,3,128,128)
    np.set_printoptions(precision=3)
    
    
    def main():
        parser = argparse.ArgumentParser()
        parser.add_argument('-c','--compile', action='store_true', help='Compile model with TI quantization')
        parser.add_argument('-p','--prequant_compile', action='store_true', help='Compile model with onxruntime quantization')
        parser.add_argument('-e','--eval', action='store_true', help='Eval model')
        
        args = parser.parse_args()
        
        assert sum([args.compile, args.prequant_compile, args.eval]) == 1, "Please select one of the options"
        
        if not args.eval:
            rm_folder(WORKDIR)
            os.makedirs(WORKDIR, exist_ok=True)
        
        if args.compile:
            compile_model(pre_quantize=False)
        elif args.prequant_compile:
            compile_model(pre_quantize=True)
        elif args.eval:
            eval_model()
        else: raise NotImplementedError
    
    
    def compile_model(pre_quantize=True):    
        make_onnx_model(pre_quantize)
        os.mkdir(TIDL_PARAMS['artifacts_folder'])
        engine = rt.InferenceSession(
            WORKDIR + 'model.onnx', 
            providers=['TIDLCompilationProvider', 'CPUExecutionProvider'],
            provider_options=[
                dict(
                **TIDL_PARAMS,
                **(
                    {
                        'accuracy_level': 9,
                        'advanced_options:prequantized_model': 1,
                    } if pre_quantize else {
                        'advanced_options:calibration_frames' : 10, 
                        'advanced_options:calibration_iterations' : 5,
                        'accuracy_level' : 1,
                    })
                ),{}]
        )
        run(engine, '')
        if pre_quantize: 
            # os.rename(WORKDIR + 'artifacts/tempDir/subgraph_0_tidl_net.bin', WORKDIR + 'artifacts/subgraph_0_tidl_net.bin')
            # os.rename(WORKDIR + 'artifacts/tempDir/subgraph_0_tidl_io_1.bin', WORKDIR + 'artifacts/subgraph_0_tidl_io_1.bin')
            run(rt.InferenceSession(WORKDIR + 'model.onnx', providers=['CPUExecutionProvider']), '/chip_results.npy')
            compare_results()
        # rm_folder(WORKDIR + 'artifacts/tempDir')
    
    
    def eval_model():
        engine = rt.InferenceSession(
            WORKDIR + 'model.onnx', 
            providers=['TIDLExecutionProvider', 'CPUExecutionProvider'],
            provider_options=[TIDL_PARAMS, {}]
        )
        run(engine, '/chip_results.npy')
        compare_results()
    
    
    def compare_results():
        x = np.squeeze(np.load(WORKDIR+'host_results.npy'))
        y = np.squeeze(np.load(WORKDIR+'chip_results.npy'))
        print('Sample outputs from float comp', x.reshape(-1)[::150000])
        print('Sample outputs from int8  comp', y.reshape(-1)[::150000])
        print('Mean absolute difference', np.abs(x-y).mean())
    
    
    def run(engine, save_path):
        out = [engine.run(None, x)[0] for x in make_images()]
        if save_path:
            np.save(WORKDIR+save_path, out[0])
    
    
    def make_images():
        return [{'input.1': x} for x in np.linspace(0,100,128**2*3*10,dtype=np.float32).reshape(10,*IN_SHAPE)]
    
    
    def make_onnx_model(pre_quantize=True):
        import torch    
        from torch import nn
        import onnx
        from onnx.version_converter import convert_version
        
        net = nn.Sequential(cbr(3,32),cbr(32,64))
        torch.onnx.export(net, torch.randn(*IN_SHAPE), WORKDIR + 'model.onnx', opset_version=17)
        net = onnx.load(WORKDIR + 'model.onnx')
        net = convert_version(net, 18)
        net.ir_version = 8
        onnx.save(net, WORKDIR + 'model.onnx')
        run(
            rt.InferenceSession(WORKDIR + 'model.onnx', providers=['CPUExecutionProvider']),
            'host_results.npy'
        )
        if pre_quantize: 
            prequantize_onnx_model(WORKDIR + 'model.onnx')
    
    
    def cbr(d1, d2):
        from torch import nn
        return nn.Sequential(nn.Conv2d(d1,d2,3), nn.BatchNorm2d(d2), nn.ReLU())
    
    
    def prequantize_onnx_model(onnx_model_path):
        from onnxruntime.quantization import quantize_static
        from onnxruntime.quantization.calibrate import CalibrationDataReader
        
        calibration_images = make_images()  
        
        class DR(CalibrationDataReader):
            def __init__(self) -> None:
                super().__init__()
                self.enum=None
                
            def get_next(self):
                if self.enum is None:
                    self.enum = iter(calibration_images)
                return next(self.enum, None)
            
            def rewind(self):
                self.enum=None
        
        quantize_static(
            onnx_model_path,
            onnx_model_path,
            DR(),
            per_channel=False,
        )
    
    
    def rm_folder(folder):
        if os.path.exists(folder):
            for root, dirs, files in os.walk(folder, topdown=False):
                [os.remove(os.path.join(root, f)) for f in files]
                [os.rmdir(os.path.join(root, d)) for d in dirs]
            os.rmdir(folder)
    
    
    if __name__ == '__main__':
        main()