• 文件 >
  • Bundled Program – 用於 ExecuTorch 模型驗證的工具
快捷方式

Bundled Program – 用於 ExecuTorch 模型驗證的工具

介紹

BundledProgram 是核心 ExecuTorch program 的包裝器,旨在幫助使用者將測試用例與他們部署的模型捆綁在一起。BundledProgram 不一定是程式的核心部分,也無需用於程式執行,但對於各種其他用例(例如模型正確性評估,包括模型啟用過程中的端到端測試)尤其重要。

總的來說,該過程可以分為兩個階段,並且在每個階段我們都支援

  • 生成階段: 將測試 I/O 用例與 ExecuTorch program 捆綁在一起,序列化為 flatbuffer。

  • 執行時階段: 在執行時訪問、執行和驗證捆綁的測試用例。

生成階段

該階段主要側重於 BundledProgram 的建立並將其作為 flatbuffer 檔案轉儲到磁碟。主要過程如下

  1. 建立模型並生成其 ExecuTorch program。

  2. 構造 List[MethodTestSuite] 以記錄所有需要捆綁的測試用例。

  3. 使用已生成的模型和 List[MethodTestSuite] 生成 BundledProgram

  4. 序列化 BundledProgram 並將其轉儲到磁碟。

步驟 1:建立模型並生成其 ExecuTorch Program。

ExecuTorch Program 可以使用 ExecuTorch API 從使用者模型生成。請參閱生成和生成示例 ExecuTorch 程式匯出到 ExecuTorch 教程

步驟 2:構造 List[MethodTestSuite] 以儲存測試資訊

BundledProgram 中,我們建立了兩個新類,MethodTestCaseMethodTestSuite,以儲存 ExecuTorch program 驗證的基本資訊。

MethodTestCase 表示單個測試用例。每個 MethodTestCase 包含單次執行的輸入和預期輸出。

MethodTestCase
executorch.devtools.bundled_program.config.MethodTestCase.__init__(self, inputs, expected_outputs=None)

用於驗證特定方法的單個測試用例

引數
  • inputs

    eager_model 使用特定推理方法進行一次執行所需的所有輸入。

    值得一提的是,雖然 bundled program 和 ET 執行時 API 都支援設定 torch.tensor 型別以外的輸入,但方法中實際更新的只有 torch.tensor 型別的輸入,其餘輸入只會檢查它們是否與方法中的預設值匹配。

  • expected_outputs – 用於驗證給定輸入的預期輸出。如果使用者只想使用測試用例進行效能分析,則可以為 None。

返回

self

MethodTestSuite 包含單個方法的所有測試資訊,包括表示方法名稱的字串以及所有測試用例的 List[MethodTestCase]

MethodTestSuite
executorch.devtools.bundled_program.config.MethodTestSuite(method_name, test_cases)[source]

與方法驗證相關的所有測試資訊

executorch.devtools.bundled_program.config.method_name

待驗證方法的名稱。

executorch.devtools.bundled_program.config.test_cases

用於驗證方法的所有測試用例。

由於每個模型可能包含多個推理方法,我們需要生成 List[MethodTestSuite] 來儲存所有基本資訊。

步驟 3:生成 BundledProgram

我們在 executorch/devtools/bundled_program/core.py 下提供了 BundledProgram 類,用於將 ExecutorchProgram-like 變數(包括 ExecutorchProgramMultiMethodExecutorchProgramExecutorchProgramManager)與 List[MethodTestSuite] 捆綁在一起

BundledProgram
executorch.devtools.bundled_program.core.BundledProgram.__init__(self, executorch_program, method_test_suites, pte_file_path=None)

透過將給定的程式和 method_test_suites 捆綁在一起建立 BundledProgram。

引數
  • executorch_program – 要捆綁的程式。

  • method_test_suites – 要捆綁的特定方法的測試用例。

  • pte_file_path – 如果未提供 executorch_program,則用於反序列化程式的 .pte 檔案路徑。

BundledProgram 的建構函式將在內部進行健全性檢查,以檢視給定的 List[MethodTestSuite] 是否符合給定 Program 的要求。具體來說

  1. List[MethodTestSuite] 中每個 MethodTestSuite 的 method_names 也應該在 program 中。請注意,無需為 Program 中的每個方法設定測試用例。

  2. 每個測試用例的元資料應滿足相應推理方法輸入的要求。

步驟 4:將 BundledProgram 序列化為 Flatbuffer。

為了序列化 BundledProgram 以供執行時 API 使用,我們提供了兩個 API,均在 executorch/devtools/bundled_program/serialize/__init__.py 下。

序列化和反序列化
executorch.devtools.bundled_program.serialize.serialize_from_bundled_program_to_flatbuffer(bundled_program)[source]

將 BundledProgram 序列化為 FlatBuffer 二進位制格式。

引數

bundled_program (BundledProgram) – 要序列化的 BundledProgram 變數。

返回

序列化後的 FlatBuffer 二進位制資料(位元組格式)。

executorch.devtools.bundled_program.serialize.deserialize_from_flatbuffer_to_bundled_program(flatbuffer)[source]

將 FlatBuffer 二進位制格式反序列化為 BundledProgram。

引數

flatbuffer (bytes) – FlatBuffer 二進位制資料(位元組格式)。

返回

一個 BundledProgram 例項。

生成示例

下面是一個流程,重點說明如何從 PyTorch 模型及其要測試的代表性輸入生成 BundledProgram

import torch

from executorch.exir import to_edge_transform_and_lower
from executorch.devtools import BundledProgram

from executorch.devtools.bundled_program.config import MethodTestCase, MethodTestSuite
from executorch.devtools.bundled_program.serialize import (
    serialize_from_bundled_program_to_flatbuffer,
)
from torch.export import export, export_for_training


# Step 1: ExecuTorch Program Export
class SampleModel(torch.nn.Module):
    """An example model with multi-methods. Each method has multiple input and single output"""

    def __init__(self) -> None:
        super().__init__()
        self.register_buffer('a', 3 * torch.ones(2, 2, dtype=torch.int32))
        self.register_buffer('b', 2 * torch.ones(2, 2, dtype=torch.int32))

    def forward(self, x: torch.Tensor, q: torch.Tensor) -> torch.Tensor:
        z = x.clone()
        torch.mul(self.a, x, out=z)
        y = x.clone()
        torch.add(z, self.b, out=y)
        torch.add(y, q, out=y)
        return y


# Inference method name of SampleModel we want to bundle testcases to.
# Notices that we do not need to bundle testcases for every inference methods.
method_name = "forward"
model = SampleModel()

# Inputs for graph capture.
capture_input = (
    (torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
    (torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
)

# Export method's FX Graph.
method_graph = export(
    export_for_training(model, capture_input).module(),
    capture_input,
)


# Emit the traced method into ET Program.
et_program = to_edge_transform_and_lower(method_graph).to_executorch()

# Step 2: Construct MethodTestSuite for Each Method

# Prepare the Test Inputs.

# Number of input sets to be verified
n_input = 10

# Input sets to be verified.
inputs = [
    # Each list below is a individual input set.
    # The number of inputs, dtype and size of each input follow Program's spec.
    [
        (torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
        (torch.rand(2, 2) - 0.5).to(dtype=torch.int32),
    ]
    for _ in range(n_input)
]

# Generate Test Suites
method_test_suites = [
    MethodTestSuite(
        method_name=method_name,
        test_cases=[
            MethodTestCase(
                inputs=input,
                expected_outputs=(getattr(model, method_name)(*input), ),
            )
            for input in inputs
        ],
    ),
]

# Step 3: Generate BundledProgram
bundled_program = BundledProgram(et_program, method_test_suites)

# Step 4: Serialize BundledProgram to flatbuffer.
serialized_bundled_program = serialize_from_bundled_program_to_flatbuffer(
    bundled_program
)
save_path = "bundled_program.bpte"
with open(save_path, "wb") as f:
    f.write(serialized_bundled_program)

如果需要,我們還可以從 flatbuffer 檔案重新生成 BundledProgram

from executorch.devtools.bundled_program.serialize import deserialize_from_flatbuffer_to_bundled_program
save_path = "bundled_program.bpte"
with open(save_path, "rb") as f:
    serialized_bundled_program = f.read()

regenerate_bundled_program = deserialize_from_flatbuffer_to_bundled_program(serialized_bundled_program)

執行時階段

該階段主要側重於使用捆綁的輸入執行模型,並將模型的輸出與捆綁的預期輸出進行比較。我們提供了多個 API 來處理其中的關鍵部分。

BundledProgram 緩衝區獲取 ExecuTorch Program 指標

我們需要 ExecuTorch program 的指標來執行。為了統一載入和執行 BundledProgram 和 Program flatbuffer 的過程,我們為此建立了一個 API executorch::bundled_program::get_program_data。請參閱此 API 的示例用法

載入捆綁的輸入到方法

要使用捆綁的輸入執行程式,我們需要將捆綁的輸入載入到方法中。這裡我們提供了一個名為 executorch::bundled_program::load_bundled_input 的 API。請參閱此 API 的示例用法

驗證方法的輸出。

我們呼叫 executorch::bundled_program::verify_method_outputs 來驗證方法的輸出是否與捆綁的預期輸出一致。請參閱此 API 的示例用法

執行時示例

請檢視我們用於 bundled program 的示例執行器。您可以執行這些命令來測試您在上一步中生成的 BundledProgram 二進位制檔案(.bpte

cd executorch
   ./examples/devtools/build_example_runner.sh
   ./cmake-out/examples/devtools/example_runner --bundled_program_path {your-bpte-file} --output_verification

執行上述程式碼片段後,預期不會看到任何輸出。

有關執行器應如何構建的詳細示例,請參閱我們的示例執行器

常見錯誤

如果 List[MethodTestSuites]Program 不匹配,將丟擲錯誤。以下是兩種常見情況

測試輸入與模型要求不匹配。

PyTorch 模型的每個推理方法對其輸入都有自己的要求,例如輸入數量、每個輸入的資料型別等。如果測試輸入不符合要求,BundledProgram 將丟擲錯誤。

以下是測試輸入的資料型別不符合模型要求的示例

import torch

from executorch.exir import to_edge
from executorch.devtools import BundledProgram

from executorch.devtools.bundled_program.config import MethodTestCase, MethodTestSuite
from torch.export import export, export_for_training


class Module(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.a = 3 * torch.ones(2, 2, dtype=torch.float)
        self.b = 2 * torch.ones(2, 2, dtype=torch.float)

    def forward(self, x):
        out_1 = torch.ones(2, 2, dtype=torch.float)
        out_2 = torch.ones(2, 2, dtype=torch.float)
        torch.mul(self.a, x, out=out_1)
        torch.add(out_1, self.b, out=out_2)
        return out_2


model = Module()
method_names = ["forward"]

inputs = (torch.ones(2, 2, dtype=torch.float), )

# Find each method of model needs to be traced my its name, export its FX Graph.
method_graph = export(
    export_for_training(model, inputs).module(),
    inputs,
)

# Emit the traced methods into ET Program.
et_program = to_edge(method_graph).to_executorch()

# number of input sets to be verified
n_input = 10

# Input sets to be verified for each inference methods.
# To simplify, here we create same inputs for all methods.
inputs = {
    # Inference method name corresponding to its test cases.
    m_name: [
        # NOTE: executorch program needs torch.float, but here is torch.int
        [
            torch.randint(-5, 5, (2, 2), dtype=torch.int),
        ]
        for _ in range(n_input)
    ]
    for m_name in method_names
}

# Generate Test Suites
method_test_suites = [
    MethodTestSuite(
        method_name=m_name,
        test_cases=[
            MethodTestCase(
                inputs=input,
                expected_outputs=(getattr(model, m_name)(*input),),
            )
            for input in inputs[m_name]
        ],
    )
    for m_name in method_names
]

# Generate BundledProgram

bundled_program = BundledProgram(et_program, method_test_suites)
錯誤資訊
The input tensor tensor([[-2,  0],
        [-2, -1]], dtype=torch.int32) dtype shall be torch.float32, but now is torch.int32
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[1], line 72
     56 method_test_suites = [
     57     MethodTestSuite(
     58         method_name=m_name,
   (...)
     67     for m_name in method_names
     68 ]
     70 # Step 3: Generate BundledProgram
---> 72 bundled_program = create_bundled_program(program, method_test_suites)
File /executorch/devtools/bundled_program/core.py:276, in create_bundled_program(program, method_test_suites)
    264 """Create bp_schema.BundledProgram by bundling the given program and method_test_suites together.
    265
    266 Args:
   (...)
    271     The `BundledProgram` variable contains given ExecuTorch program and test cases.
    272 """
    274 method_test_suites = sorted(method_test_suites, key=lambda x: x.method_name)
--> 276 assert_valid_bundle(program, method_test_suites)
    278 bundled_method_test_suites: List[bp_schema.BundledMethodTestSuite] = []
    280 # Emit data and metadata of bundled tensor
File /executorch/devtools/bundled_program/core.py:219, in assert_valid_bundle(program, method_test_suites)
    215 # type of tensor input should match execution plan
    216 if type(cur_plan_test_inputs[j]) == torch.Tensor:
    217     # pyre-fixme[16]: Undefined attribute [16]: Item `bool` of `typing.Union[bool, float, int, torch._tensor.Tensor]`
    218     # has no attribute `dtype`.
--> 219     assert cur_plan_test_inputs[j].dtype == get_input_dtype(
    220         program, program_plan_id, j
    221     ), "The input tensor {} dtype shall be {}, but now is {}".format(
    222         cur_plan_test_inputs[j],
    223         get_input_dtype(program, program_plan_id, j),
    224         cur_plan_test_inputs[j].dtype,
    225     )
    226 elif type(cur_plan_test_inputs[j]) in (
    227     int,
    228     bool,
    229     float,
    230 ):
    231     assert type(cur_plan_test_inputs[j]) == get_input_type(
    232         program, program_plan_id, j
    233     ), "The input primitive dtype shall be {}, but now is {}".format(
    234         get_input_type(program, program_plan_id, j),
    235         type(cur_plan_test_inputs[j]),
    236     )
AssertionError: The input tensor tensor([[-2,  0],
        [-2, -1]], dtype=torch.int32) dtype shall be torch.float32, but now is torch.int32

BundleConfig 中的方法名不存在。

另一種常見錯誤是任何 MethodTestSuite 中的方法名在模型中不存在。BundledProgram 將丟擲錯誤並顯示不存在的方法名

import torch

from executorch.exir import to_edge
from executorch.devtools import BundledProgram

from executorch.devtools.bundled_program.config import MethodTestCase, MethodTestSuite
from torch.export import export, export_for_training


class Module(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.a = 3 * torch.ones(2, 2, dtype=torch.float)
        self.b = 2 * torch.ones(2, 2, dtype=torch.float)

    def forward(self, x):
        out_1 = torch.ones(2, 2, dtype=torch.float)
        out_2 = torch.ones(2, 2, dtype=torch.float)
        torch.mul(self.a, x, out=out_1)
        torch.add(out_1, self.b, out=out_2)
        return out_2


model = Module()
method_names = ["forward"]

inputs = (torch.ones(2, 2, dtype=torch.float),)

# Find each method of model needs to be traced my its name, export its FX Graph.
method_graph = export(
    export_for_training(model, inputs).module(),
    inputs,
)

# Emit the traced methods into ET Program.
et_program = to_edge(method_graph).to_executorch()

# number of input sets to be verified
n_input = 10

# Input sets to be verified for each inference methods.
# To simplify, here we create same inputs for all methods.
inputs = {
    # Inference method name corresponding to its test cases.
    m_name: [
        [
            torch.randint(-5, 5, (2, 2), dtype=torch.float),
        ]
        for _ in range(n_input)
    ]
    for m_name in method_names
}

# Generate Test Suites
method_test_suites = [
    MethodTestSuite(
        method_name=m_name,
        test_cases=[
            MethodTestCase(
                inputs=input,
                expected_outputs=(getattr(model, m_name)(*input),),
            )
            for input in inputs[m_name]
        ],
    )
    for m_name in method_names
]

# NOTE: MISSING_METHOD_NAME is not an inference method in the above model.
method_test_suites[0].method_name = "MISSING_METHOD_NAME"

# Generate BundledProgram
bundled_program = BundledProgram(et_program, method_test_suites)

錯誤資訊
All method names in bundled config should be found in program.execution_plan,          but {'MISSING_METHOD_NAME'} does not include.
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[3], line 73
     70 method_test_suites[0].method_name = "MISSING_METHOD_NAME"
     72 # Generate BundledProgram
---> 73 bundled_program = create_bundled_program(program, method_test_suites)
File /executorch/devtools/bundled_program/core.py:276, in create_bundled_program(program, method_test_suites)
    264 """Create bp_schema.BundledProgram by bundling the given program and method_test_suites together.
    265
    266 Args:
   (...)
    271     The `BundledProgram` variable contains given ExecuTorch program and test cases.
    272 """
    274 method_test_suites = sorted(method_test_suites, key=lambda x: x.method_name)
--> 276 assert_valid_bundle(program, method_test_suites)
    278 bundled_method_test_suites: List[bp_schema.BundledMethodTestSuite] = []
    280 # Emit data and metadata of bundled tensor
File /executorch/devtools/bundled_program/core.py:141, in assert_valid_bundle(program, method_test_suites)
    138 method_name_of_program = {e.name for e in program.execution_plan}
    139 method_name_of_test_suites = {t.method_name for t in method_test_suites}
--> 141 assert method_name_of_test_suites.issubset(
    142     method_name_of_program
    143 ), f"All method names in bundled config should be found in program.execution_plan, \
    144      but {str(method_name_of_test_suites - method_name_of_program)} does not include."
    146 # check if method_tesdt_suites has been sorted in ascending alphabetical order of method name.
    147 for test_suite_id in range(1, len(method_test_suites)):
AssertionError: All method names in bundled config should be found in program.execution_plan,          but {'MISSING_METHOD_NAME'} does not include.

文件

查閱 PyTorch 的全面開發者文件

檢視文件

教程

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

檢視教程

資源

查詢開發資源並獲取問題解答

檢視資源