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

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

作者: Anthony Shoumikhin

在 C++ 教程中运行 ExecuTorch 模型 中,我们探索了用于运行导出模型的低级 ExecuTorch API。虽然这些 API 提供了零开销、极大的灵活性和控制,但对于常规使用来说它们可能很冗长且复杂。为了简化这一点并在 Python 中类似 PyTorch 的急切模式,我们介绍了 Module 在常规 ExecuTorch 运行时 API 之上的外观 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 对象是一个快速操作,不会涉及大量的处理时间或内存分配。实际的 ProgramMethod 加载是在第一次推断时延迟进行的,除非使用专用 API 显式请求。

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

强制加载方法

要随时强制加载 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() 或解引用运算符,例如 *->

分析模块

使用 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 的 Eager 模式体验非常相似。 通过抽象掉底层运行时 API 的复杂性,开发人员可以专注于模型执行,而无需担心底层细节。

文档

访问 PyTorch 的全面开发者文档

查看文档

教程

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

查看教程

资源

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

查看资源