將模型作為委託進行 Lowering¶
受眾:機器學習工程師,對應用委託以加速執行時程式感興趣的人員。
後端委託是後端處理和執行 PyTorch 程式的入口點,旨在利用專用後端和硬體的效能和效率優勢,同時仍為 PyTorch 使用者提供接近 PyTorch 執行時的體驗。後端委託通常由 ExecuTorch 或供應商提供。在程式中利用委託的方式是透過標準入口點 to_backend。
前端介面¶
將程式委託給後端有三種流程
將整個模組 Lowering 到後端。這適用於測試後端和預處理階段。
將整個模組 Lowering 到後端,並將其與其他模組組合。這適用於重用從其他流程匯出的 Lowering 模組。
根據分割槽器 Lowering 模組的部分。這適用於 Lowering 包含可 Lowering 和不可 Lowering 節點的模型,是最簡化的流程。
流程 1:Lowering 整個模組¶
此流程從帶有 Edge Dialect 表示的跟蹤圖模組開始。要對其進行 Lowering,我們呼叫以下函式,該函式返回一個 LoweredBackendModule(關於此函式的更多文件可以在 匯出 API 參考 中找到)
# defined in backend_api.py
def to_backend(
backend_id: str,
edge_program: ExportedProgram,
compile_spec: List[CompileSpec],
) -> LoweredBackendModule:
在此函式內部,會呼叫後端的 preprocess() 函式,該函式生成一個編譯後的 blob,該 blob 將被寫入到 flatbuffer 二進位制檔案中。Lowering 後的模組可以直接捕獲,或者放回父模組中進行捕獲。最終,捕獲的模組會序列化到 flatbuffer 模型中,該模型可以由執行時載入。
以下是此流程的一個示例
from executorch.exir.backend.backend_api import to_backend
import executorch.exir as exir
import torch
from torch.export import export
from executorch.exir import to_edge
# The submodule runs in a specific backend. In this example, `BackendWithCompilerDemo` backend
class LowerableSubModel(torch.nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
return torch.sin(x)
# Convert the lowerable module to Edge IR Representation
to_be_lowered = LowerableSubModel()
example_input = (torch.ones(1), )
to_be_lowered_exir_submodule = to_edge(export(to_be_lowered, example_input))
# Import the backend implementation
from executorch.exir.backend.test.backend_with_compiler_demo import (
BackendWithCompilerDemo,
)
lowered_module = to_backend('BackendWithCompilerDemo', to_be_lowered_exir_submodule.exported_program(), [])
我們可以透過直接執行以下命令將程式序列化為 flatbuffer 格式
# Save the flatbuffer to a local file
save_path = "delegate.pte"
with open(save_path, "wb") as f:
f.write(lowered_module.buffer())
流程 2:Lowering 整個模組並組合¶
或者,在流程 1 之後,我們可以將此 Lowering 後的模組與另一個模組組合
# This submodule runs in executor runtime
class NonLowerableSubModel(torch.nn.Module):
def __init__(self, bias):
super().__init__()
self.bias = bias
def forward(self, a, b):
return torch.add(torch.add(a, b), self.bias)
# The composite module, including lower part and non-lowerpart
class CompositeModel(torch.nn.Module):
def __init__(self):
super().__init__()
self.non_lowerable = NonLowerableSubModel(torch.ones(1) * 0.3)
self.lowerable = lowered_module
def forward(self, x):
a = self.lowerable(x)
b = self.lowerable(a)
ret = self.non_lowerable(a, b)
return a, b, ret
composite_model = CompositeModel()
model_inputs = (torch.ones(1), )
exec_prog = to_edge(export(composite_model, model_inputs)).to_executorch()
# Save the flatbuffer to a local file
save_path = "delegate.pte"
with open(save_path, "wb") as f:
f.write(exec_prog.buffer)
流程 3:分割槽¶
第三個流程也從帶有 Edge Dialect 表示的跟蹤圖模組開始。要 Lowering 此圖模組中的某些節點,我們可以使用過載的 to_backend 函式。
def to_backend(
edge_program: ExportedProgram,
partitioner: Partitioner,
) -> ExportedProgram:
此函式接收一個 Partitioner,它會為所有需要 Lowering 的節點新增標籤。它將返回一個 partition_tags 字典,將標籤對映到後端名稱和模組編譯規範。然後,帶有標籤的節點將使用流程 1 的過程進行分割槽並 Lowering 到其對映的後端。可用的輔助分割槽器文件可以在此處找到。這些 Lowering 後的模組將被插入到頂層模組中並進行序列化。
以下是此流程的一個示例
import executorch.exir as exir
from executorch.exir.backend.backend_api import to_backend
from executorch.exir.backend.test.op_partitioner_demo import AddMulPartitionerDemo
from executorch.exir.program import (
EdgeProgramManager,
to_edge,
)
from torch.export import export
import torch
class Model(torch.nn.Module):
def __init__(self):
super().__init__()
def forward(self, x, y):
x = x + y
x = x * y
x = x - y
x = x / y
x = x * y
x = x + y
return x
model = Model()
model_inputs = (torch.randn(1, 3), torch.randn(1, 3))
core_aten_ep = export(model, model_inputs)
edge: EdgeProgramManager = to_edge(core_aten_ep)
edge = edge.to_backend(AddMulPartitionerDemo())
exec_prog = edge.to_executorch()
# Save the flatbuffer to a local file
save_path = "delegate.pte"
with open(save_path, "wb") as f:
f.write(exec_prog.buffer)
執行時¶
獲得帶有委託的程式後,要在後端上執行模型,我們需要註冊後端。根據委託實現的不同,後端可以作為全域性變數的一部分註冊,也可以在 main 函式內部顯式註冊。
如果後端是在全域性變數初始化期間註冊的,那麼只要它是靜態連結的,後端就會被註冊。使用者只需要將庫作為依賴項包含進來即可。
如果供應商提供 API 來註冊後端,使用者需要將庫作為依賴項包含進來,並呼叫供應商提供的 API 來在 main 函式中顯式註冊後端。