• 文件 >
  • 在 C++ 中使用 Module 擴充套件執行 ExecuTorch 模型
快捷方式

在 C++ 中使用 Module 擴充套件執行 ExecuTorch 模型

作者: Anthony Shoumikhin

在《在 C++ 中執行 ExecuTorch 模型教程》中,我們探討了用於執行匯出模型的較低級別 ExecuTorch API。雖然這些 API 提供了零開銷、極大的靈活性和控制力,但對於常規使用來說可能顯得冗長和複雜。為了簡化此過程並模仿 PyTorch 在 Python 中的 eager 模式,我們在常規的 ExecuTorch 執行時 API 上引入了 Module 外觀 (facade) 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 物件是一個快速操作,不涉及大量的處理時間或記憶體分配。除非使用專門的 API 明確請求,否則實際載入 ProgramMethod 會在第一次推理時延遲發生。

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

強制載入 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,包括 tensors、scalars、lists 和其他支援的型別。要為方法設定特定的輸入值:

module.set_input("forward", input_value, input_index);
  • input_value 是一個 EValue,表示您要設定的輸入。

  • input_index 是要設定輸入的從零開始的索引。

例如,要設定第一個輸入 tensor:

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 型別的輸出,並且這些輸出在模型匯出時不能是 memory-planned 的。Memory-planned tensors 在模型匯出期間預分配,無法替換。

要為特定方法設定輸出 tensor:

module.set_output("forward", output_tensor, output_index);
  • output_tensor 是一個包含您希望設定為輸出的 tensor 的 EValue

  • output_index 是要設定輸出的從零開始的索引。

注意:確保您設定的輸出 tensor 與該方法期望的形狀和資料型別一致。

對於 forward() 方法,可以省略方法名稱;對於第一個輸出,可以省略索引。

module.set_output(output_tensor);

注意:預設的輸出儲存在 Module 中,可以多次重複用於後續執行,就像輸入一樣。

Result 和 Error 型別

大多數 ExecuTorch API 返回 ResultError 型別。

  • Error 是一個包含有效錯誤程式碼的 C++ 列舉。預設值為 Error::Ok,表示成功。

  • Result 如果操作失敗,可以包含一個 Error;如果成功,則可以包含一個負載 (payload),例如包裹著 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 eager 模式的體驗。透過抽象掉較低級別執行時 API 的複雜性,開發者可以專注於模型執行,而無需擔心底層細節。

文件

查閱 PyTorch 的全面開發者文件

檢視文件

教程

獲取針對初學者和高階開發者的深入教程

檢視教程

資源

查詢開發資源並解答您的問題

檢視資源