• 文档 >
  • 使用 C++ 中的模块扩展运行 ExecuTorch 模型
快捷方式

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

作者: Anthony Shoumikhin

在《在 C++ 中运行 ExecuTorch 模型教程》中,我们探讨了运行导出模型的较低级 ExecuTorch API。虽然这些 API 提供了零开销、高度灵活性和控制力,但对于常规使用来说,它们可能冗长而复杂。为了简化这一点并使其类似于 Python 中的 PyTorch 动态图模式,我们在常规 ExecuTorch 运行时 API 之上引入了 Module 门面 API。Module API 提供了相同的灵活性,但默认使用常用的组件,如 DataLoaderMemoryAllocator,隐藏了大多数复杂的细节。

示例

让我们看看如何使用 ModuleTensorPtr API 来运行从《导出到 ExecuTorch 教程》生成的 SimpleConv 模型。

#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>();
}

现在的代码简化为创建一个 Module 并对其调用 forward(),无需任何额外设置。让我们仔细看看这些以及其他 Module API,以便更好地理解内部工作原理。

API

创建 Module

创建 Module 对象是一个快速操作,不涉及显着的处理时间或内存分配。实际加载 ProgramMethod 会在第一次推理时惰性地发生,除非通过专用 API 显式请求。

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

对于数据已分离到 PTD 文件中的模型,请一起加载它们

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

强制加载 Method

要随时强制加载 Module(以及底层 ExecuTorch Program),请使用 load() 函数。

const auto error = module.load();

assert(module.is_loaded());

要强制加载特定的 Method,请调用 load_method() 函数。

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

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

您还可以使用便捷函数加载 forward 方法。

const auto error = module.load_forward();

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

注意: Program 在任何 Method 被加载之前会自动加载。如果之前的加载尝试成功,后续的加载尝试将无效。

查询元数据

使用 method_names() 函数获取 Module 包含的方法名称集合。

const auto method_names = module.method_names();

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

注意: 首次调用 method_names() 时会强制加载 Program

要自省特定方法的杂项元数据,请使用 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

执行推理

假设 Program 的方法名称及其输入格式是提前已知的,您可以使用 execute() 函数按名称直接运行方法。

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

对于标准的 forward() 方法,上述操作可以简化。

const auto result = module.forward(tensor);

注意: execute()forward() 在首次调用时会加载 ProgramMethod。因此,第一次推理会花费更长的时间,因为模型是惰性加载并为执行准备的,除非它之前已被显式加载。

设置输入和输出

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

设置输入

输入可以是任何 EValue,它包括张量、标量、列表和其他支持的类型。要为方法设置特定的输入值:

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

  • input_index 是要设置的输入的零基索引。

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

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

您也可以一次设置多个输入。

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

注意: 对于 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 中,并且可以在后续执行中重复使用多次。

如果您不再需要预设的输入,请记住通过将其设置为默认构造的 EValue 来清除或重置它们。

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

设置输出

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

要为特定方法设置输出张量:

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

  • output_index 是要设置的输出的零基索引。

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

对于 forward(),您可以跳过方法名称;对于第一个输出,您可以跳过索引。

module.set_output(output_tensor);

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

结果和错误类型

大多数 ExecuTorch API 返回 ResultError 类型。

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

  • Result 可以包含一个 Error(如果操作失败)或一个有效负载(例如,如果成功则包装 TensorEValue)。要检查 Result 是否有效,请调用 ok()。要检索 Error,请使用 error();要获取数据,请使用 get() 或解引用运算符,如 *->

剖析 Module

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

#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);
    }
  }
}

结论

Module API 提供了一个简化的接口,用于在 C++ 中运行 ExecuTorch 模型,非常类似于 PyTorch 动态图模式的体验。通过抽象化底层运行时 API 的复杂性,开发人员可以专注于模型执行,而无需担心底层细节。

文档

访问全面的 PyTorch 开发者文档

查看文档

教程

为初学者和高级开发者提供深入的教程

查看教程

资源

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

查看资源