目录

使用 C++ 中的模块扩展运行 ExecuTorch 模型

作者: Anthony Shoumikhin

在 在 C++ 中运行 ExecuTorch 模型教程中,我们探讨了用于运行导出模型的较低级别 ExecuTorch API。虽然这些 API 提供零开销、极大的灵活性和控制力,但它们对于常规使用来说可能很冗长和复杂。为了简化这一点并类似于 PyTorch 在 Python 中的急切模式,我们在常规 ExecuTorch 运行时 API 上引入了外观 API。这些 API 提供相同的灵活性,但默认使用常用组件,如 和 ,隐藏了最复杂的细节。ModuleModuleDataLoaderMemoryAllocator

让我们看看如何使用 和 API 运行从 导出到 ExecuTorch 教程生成的模型:SimpleConvModule

#include <executorch/extension/module/module.h>
#include <executorch/extension/tensor/tensor.h>

using namespace ::executorch::extension;

// Create a Module.
Module module("/path/to/model.pte");

// Wrap the input data with a Tensor.
float input[1 * 3 * 256 * 256];
auto tensor = from_blob(input, {1, 3, 256, 256});

// Perform an inference.
const auto result = module.forward(tensor);

// Check for success or failure.
if (result.ok()) {
  // Retrieve the output data.
  const auto output = result->at(0).toTensor().const_data_ptr<float>();
}

现在,代码归结为创建 a 并调用它,无需额外设置。让我们仔细看看这些 API 和其他 API,以更好地了解内部工作原理。Moduleforward()Module

蜜蜂属

创建模块

创建对象是一项快速操作,不涉及大量的处理时间或内存分配。a 和 a 的实际加载在第一次推理中懒惰地发生,除非使用专用 API 明确请求。ModuleProgramMethod

Module module("/path/to/model.pte");

强制加载方法

要随时强制加载 (以及底层的 ExecuTorch ),请使用以下函数:ModuleProgramload()

const auto error = module.load();

assert(module.is_loaded());

要强制加载特定 ,请调用函数:Methodload_method()

const auto error = module.load_method("forward");

assert(module.is_method_loaded("forward"));

您还可以使用 convenience 函数来加载该方法:forward

const auto error = module.load_forward();

assert(module.is_method_loaded("forward"));

注意:在加载 any 之前自动加载 。如果上一次尝试成功,则后续加载尝试无效。ProgramMethod

查询元数据

使用函数获取 a 包含的一组方法名称:Modulemethod_names()

const auto method_names = module.method_names();

if (method_names.ok()) {
  assert(method_names->count("forward"));
}

注意:第一次调用时将强制加载 。method_names()Program

要内省有关特定方法的其他元数据,请使用返回 struct 的函数:method_meta()MethodMeta

const auto method_meta = module.method_meta("forward");

if (method_meta.ok()) {
  assert(method_meta->name() == "forward");
  assert(method_meta->num_inputs() > 1);

  const auto input_meta = method_meta->input_tensor_meta(0);
  if (input_meta.ok()) {
    assert(input_meta->scalar_type() == ScalarType::Float);
  }

  const auto output_meta = method_meta->output_tensor_meta(0);
  if (output_meta.ok()) {
    assert(output_meta->sizes().size() == 1);
  }
}

注意: 也会在第一次调用时强制加载 。method_meta()Method

执行推理

假设 的方法名称及其输入格式事先已知,您可以使用以下函数直接按名称运行方法:Programexecute()

const auto result = module.execute("forward", tensor);

对于标准方法,以上可以简化:forward()

const auto result = module.forward(tensor);

注意:或者将加载 和 第一次调用它们。因此,第一次推理将花费更长的时间,因为模型是延迟加载的,并且除非之前显式加载,否则会为执行做好准备。execute()forward()ProgramMethod

设置输入和输出

您可以使用以下 API 为方法设置单独的输入和输出值。

设置输入

Inputs 可以是 any ,其中包括张量、标量、列表和其他支持的类型。要为方法设置特定的输入值,请执行以下操作:EValue

module.set_input("forward", input_value, input_index);
  • input_value是一个表示要设置的输入的 Input。EValue

  • input_index是要设置的输入的从零开始的索引。

例如,要设置第一个输入张量:

module.set_input("forward", tensor_value, 0);

您还可以一次设置多个输入:

std::vector<runtime::EValue> inputs = {input1, input2, input3};
module.set_inputs("forward", inputs);

注意:您可以跳过该方法的 method name 参数。forward()

通过预设所有输入,您可以在不传递任何参数的情况下执行推理:

const auto result = module.forward();

或者只是设置然后部分传递输入:

// Set the second input ahead of time.
module.set_input(input_value_1, 1);

// Execute the method, providing the first input at call time.
const auto result = module.forward(input_value_0);

注意:预设输入存储在 中,并且可以在下次执行时多次重复使用。Module

如果您不再需要它们,请不要忘记清除或重置输入,方法是将它们设置为 default-constructed:EValue

module.set_input(runtime::EValue(), 1);

设置输出

只能在运行时设置 Tensor 类型的输出,并且在模型导出时不得对它们进行内存规划。内存计划张量在模型导出期间预先分配,无法替换。

要为特定方法设置输出张量,请执行以下操作:

module.set_output("forward", output_tensor, output_index);
  • output_tensor是一个包含要设置为输出的张量的 Tensor。EValue

  • output_index是要设置的输出的从零开始的索引。

注意:确保您设置的输出张量与方法输出的预期形状和数据类型匹配。

您可以跳过第一个输出的方法名称和索引:forward()

module.set_output(output_tensor);

注意:预设的输出存储在 中,并且可以在下次执行时多次重复使用,就像输入一样。Module

结果和错误类型

大多数 ExecuTorch API 返回 or 类型:ResultError

  • 是包含有效错误代码的 C++ 枚举。默认值为 ,表示成功。Error::Ok

  • 如果操作失败,则可以保存 an,也可以保存有效负载,例如包装 if successful。要检查 a 是否有效,请调用 。要检索 、 、 use 和 要获取数据,请使用 或 取消引用运算符,如 和 。ErrorEValueTensorResultok()Errorerror()get()*->

对模块进行性能分析

使用 ExecuTorch Dump 跟踪模型执行情况。创建一个实例并将其传递给构造函数。执行方法后,将数据保存到文件中以供进一步分析:ETDumpGenModuleETDump

#include <fstream>
#include <memory>

#include <executorch/extension/module/module.h>
#include <executorch/devtools/etdump/etdump_flatcc.h>

using namespace ::executorch::extension;

Module module("/path/to/model.pte", Module::LoadMode::MmapUseMlock, std::make_unique<ETDumpGen>());

// Execute a method, e.g., module.forward(...); or module.execute("my_method", ...);

if (auto* etdump = dynamic_cast<ETDumpGen*>(module.event_tracer())) {
  const auto trace = etdump->get_etdump_data();

  if (trace.buf && trace.size > 0) {
    std::unique_ptr<void, decltype(&free)> guard(trace.buf, free);
    std::ofstream file("/path/to/trace.etdump", std::ios::binary);

    if (file) {
      file.write(static_cast<const char*>(trace.buf), trace.size);
    }
  }
}

结论

这些 API 为在 C++ 中运行 ExecuTorch 模型提供了一个简化的接口,与 PyTorch 的 Eager 模式的体验非常相似。通过抽象出较低级别运行时 API 的复杂性,开发人员可以专注于模型执行,而无需担心底层细节。Module

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源