在 C++ 中使用模組擴充功能執行 ExecuTorch 模型¶
在在 C++ 中執行 ExecuTorch 模型教學中,我們探索了用於執行匯出模型的較低階 ExecuTorch API。雖然這些 API 提供了零開銷、極佳的彈性與控制,但對於常規使用而言,它們可能繁瑣且複雜。為了簡化此流程並使其類似於 Python 中 PyTorch 的 Eager 模式,我們在常規 ExecuTorch 執行階段 API 上引入了 Module
外觀 API。Module
API 提供了相同的彈性,但預設使用常用的元件,例如 DataLoader
和 MemoryAllocator
,隱藏了大多數複雜的細節。
範例¶
讓我們看看如何使用 Module
和 TensorPtr
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 明確要求,否則 Program
和 Method
的實際載入會在第一次推論時延遲發生。
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()
時,將載入 Program
和 Method
。 因此,第一次推論將花費更長的時間,因為模型會延遲載入並準備執行,除非先前已明確載入。
設定輸入和輸出¶
您可以使用下列 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 都會傳回 Result
或 Error
類型
對模組進行效能分析¶
使用 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 模式體驗。透過抽象化較低層級 runtime API 的複雜性,開發人員可以專注於模型執行,而無需擔心底層細節。