Script para Configuração Automática de Formas Dinâmicas na Conversão de ONNX para TensorRT

Na conversão de modelos ONNX para TensorRT, a definição de formas dinâmicas requer a especificação de três valores: mínimo, ótimo e máximo. Para simplificar procsesos de teste, foi desenvolvido um script que automatiza essa configruação, atribuindo valores padrão a dimensões desconhecidas, enquanto permite ajustes manuais.

A função abaixo analisa um modelo ONNX, identifica automaticamenet as formas dinâmicas e aplica configurações padrão (mínimo 2, ótimo 256, máximo 512) para dimensões variáveis, facilitando a geração de engines TensorRT.

import onnx
import tensorrt as trt
import os

def gerar_engine_trt(caminho_onnx, destino_engine, dtype_trt=trt.DataType.HALF, batch=1, modo_silencioso=False, shapes_dinamicos={}, formas_estaticas={}, limite_memoria=(1 << 30)):
    """Gera um engine TensorRT a partir de um arquivo ONNX para inferência"""

    logger = trt.Logger(trt.Logger.VERBOSE)
    flags_rede = [] if trt.__version__[0] < '7' else [1 << (int)(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)]
    with trt.Builder(logger) as construtor, construtor.create_network(*flags_rede) as rede_trt, trt.OnnxParser(rede_trt, logger) as parser:
        construtor.max_batch_size = batch
        configuracao: trt.IBuilderConfig = construtor.create_builder_config()
        configuracao.max_workspace_size = limite_memoria
        if dtype_trt == trt.DataType.HALF:
            configuracao.set_flag(trt.BuilderFlag.FP16)

        if not os.path.exists(caminho_onnx):
            print(f'Arquivo ONNX {caminho_onnx} inexistente.')
            exit(0)

        print(f'Carregando modelo ONNX de {caminho_onnx}...')
        with open(caminho_onnx, 'rb') as modelo:
            if not parser.parse(modelo.read()):
                print('Erro na análise do arquivo ONNX.')
                for i in range(parser.num_errors):
                    print(parser.get_error(i))
                return None

        print('Análise concluída')
        if not modo_silencioso:
            print(f'Construindo engine a partir de {caminho_onnx}...')

        mapeamento_shapes = {}
        modelo_onnx = onnx.load(caminho_onnx)
        for idx, entrada in enumerate(modelo_onnx.graph.input):
            lista_dimensoes = []
            eh_dinamico = False
            for dimensao in entrada.type.tensor_type.shape.dim:
                lista_dimensoes.append(dimensao.dim_value)
                if dimensao.dim_param or dimensao.dim_value <= 0:
                    eh_dinamico = True
            if eh_dinamico:
                minimo = [(v if v > 0 else 2) for v in lista_dimensoes]
                otimo = [(v if v > 0 else 256) for v in lista_dimensoes]
                maximo = [(v if v > 0 else 512) for v in lista_dimensoes]
                mapeamento_shapes[entrada.name] = [minimo, otimo, maximo]

        for nome, valor in shapes_dinamicos.items():
            mapeamento_shapes[nome] = valor

        perfil_otimizacao = construtor.create_optimization_profile()
        if mapeamento_shapes:
            for nome_tensor, (forma_min, forma_otima, forma_max) in mapeamento_shapes.items():
                perfil_otimizacao.set_shape(nome_tensor, forma_min, forma_otima, forma_max)

        for nome_estatico, (val_min, val_otimo, val_max) in formas_estaticas.items():
            perfil_otimizacao.set_shape_input(nome_estatico, val_min, val_otimo, val_max)

        configuracao.add_optimization_profile(perfil_otimizacao)
        engine = construtor.build_engine(rede_trt, configuracao)
        dados_engine = engine.serialize()
        with open(destino_engine, 'wb') as arquivo_saida:
            arquivo_saida.write(dados_engine)

Exemplo de aplicação: a configuração pode ser manual ou utilizar os valores predefinidos para testes.

nome_do_projeto = "meu_projeto"
gerar_engine_trt(f"onnx/{nome_do_projeto}/encoder.onnx", f"onnx/{nome_do_projeto}/encoder.trt",
    shapes_dinamicos={
        "seq_referencia": [(1, 1), (1, 256), (1, 512)],
        "seq_texto": [(1, 1), (1, 256), (1, 512)],
        "bert_ref": [(1024, 1), (1024, 256), (1024, 512)],
        "bert_texto": [(1024, 1), (1024, 256), (1024, 512)],
        "ssl_saida": [(1, 768, 1), (1, 768, 256), (1, 768, 512)],
    },
    formas_estaticas={
        "comprimento_saida": [(165,), (265,), (1165,)],
        "comprimento_chave_valor": [(227,), (327,), (1227,)]
    }
)

gerar_engine_trt(f"onnx/{nome_do_projeto}/decodificador.onnx", f"onnx/{nome_do_projeto}/decodificador.trt")

Tags: TensorRT ONNX formas dinâmicas conversão de modelos otimização de inferência

Publicado em 6-5 04:38 por Thomas