注意
跳到结尾 下载完整示例代码
使用ExecuTorch开发者工具对模型进行分析¶
作者: Jack Khuu
ExecuTorch 开发者工具是一组旨在为用户提供分析、调试和可视化 ExecuTorch 模型能力的工具。
本教程将展示如何利用开发者工具对模型进行性能分析的完整端到端流程。 具体来说,它将:
预备知识¶
要运行本教程,您首先需要 设置您的ExecuTorch环境。
生成ETRecord(可选)¶
第一步是生成一个 ETRecord。 ETRecord 包含模型
图和元数据,用于将运行时结果(如性能分析)与
急切模型链接。这是通过 executorch.devtools.generate_etrecord 生成的。
executorch.devtools.generate_etrecord 接收一个输出文件路径 (str),边模式模型 (EdgeProgramManager),ExecuTorch 模式模型
(ExecutorchProgramManager),以及一个可选的额外模型字典。
在这个教程中,将使用一个示例模型(如下所示)来进行演示。
import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
from executorch.devtools import generate_etrecord
from executorch.exir import (
EdgeCompileConfig,
EdgeProgramManager,
ExecutorchProgramManager,
to_edge,
)
from torch.export import export, ExportedProgram
# Generate Model
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 5x5 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5*5 from image dimension
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square, you can specify with a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = torch.flatten(x, 1) # flatten all dimensions except the batch dimension
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
model = Net()
aten_model: ExportedProgram = export(model, (torch.randn(1, 1, 32, 32),), strict=True)
edge_program_manager: EdgeProgramManager = to_edge(
aten_model, compile_config=EdgeCompileConfig(_check_ir_validity=True)
)
edge_program_manager_copy = copy.deepcopy(edge_program_manager)
et_program_manager: ExecutorchProgramManager = edge_program_manager.to_executorch()
# Generate ETRecord
etrecord_path = "etrecord.bin"
generate_etrecord(etrecord_path, edge_program_manager_copy, et_program_manager)
警告
用户应该对 to_edge() 的输出进行深拷贝,并将该深拷贝传递给 generate_etrecord API。这是必要的,因为后续调用 to_executorch() 会进行原地变异,从而在过程中丢失调试数据。
生成ETDump¶
下一步是生成一个ETDump。ETDump 包含执行捆绑程序模型的运行时结果。
在此教程中,从上面的示例模型创建了一个 Bundled Program。
import torch
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 executorch.exir import to_edge
from torch.export import export
# Step 1: ExecuTorch Program Export
m_name = "forward"
method_graphs = {m_name: export(model, (torch.randn(1, 1, 32, 32),), strict=True)}
# Step 2: Construct Method Test Suites
inputs = [[torch.randn(1, 1, 32, 32)] for _ in range(2)]
method_test_suites = [
MethodTestSuite(
method_name=m_name,
test_cases=[
MethodTestCase(inputs=inp, expected_outputs=getattr(model, m_name)(*inp))
for inp in inputs
],
)
]
# Step 3: Generate BundledProgram
executorch_program = to_edge(method_graphs).to_executorch()
bundled_program = BundledProgram(executorch_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.bp"
with open(save_path, "wb") as f:
f.write(serialized_bundled_program)
使用CMake(按照这些说明设置cmake)来执行捆绑程序以生成ETDump:
cd executorch
./examples/devtools/build_example_runner.sh
cmake-out/examples/devtools/example_runner --bundled_program_path="bundled_program.bp"
创建一个检查器¶
最后一步是通过传入工件路径来创建 Inspector。
检查器从 ETDump 中获取运行时结果,并将其与 Edge Dialect Graph 的操作符进行关联。
Recall: 不需要 ETRecord。如果未提供 ETRecord,
Inspector 将显示运行时结果,而无需操作符相关性。
要可视化所有运行时事件,请调用 Inspector 的 print_data_tabular。
from executorch.devtools import Inspector
etrecord_path = "etrecord.bin"
etdump_path = "etdump.etdp"
inspector = Inspector(etdump_path=etdump_path, etrecord=etrecord_path)
inspector.print_data_tabular()
False
使用检查器进行分析¶
Inspector 提供了 2 种访问摄入信息的方式: EventBlocks
和 DataFrames。这些方式使用户能够对其模型性能进行自定义分析。
以下是使用示例,包含 EventBlock 和 DataFrame 两种方法。
# Set Up
import pprint as pp
import pandas as pd
pd.set_option("display.max_colwidth", None)
pd.set_option("display.max_columns", None)
如果用户想要原始的性能分析结果,他们将执行类似查找 addmm.out 事件的原始运行时数据的操作。
for event_block in inspector.event_blocks:
# Via EventBlocks
for event in event_block.events:
if event.name == "native_call_addmm.out":
print(event.name, event.perf_data.raw if event.perf_data else "")
# Via Dataframe
df = event_block.to_dataframe()
df = df[df.event_name == "native_call_addmm.out"]
print(df[["event_name", "raw"]])
print()
如果用户想要追溯一个操作符回其模型代码,他们将做类似查找最慢的 convolution.out 调用的模块层次结构和堆栈跟踪的事情。
for event_block in inspector.event_blocks:
# Via EventBlocks
slowest = None
for event in event_block.events:
if event.name == "native_call_convolution.out":
if slowest is None or event.perf_data.p50 > slowest.perf_data.p50:
slowest = event
if slowest is not None:
print(slowest.name)
print()
pp.pprint(slowest.stack_traces)
print()
pp.pprint(slowest.module_hierarchy)
# Via Dataframe
df = event_block.to_dataframe()
df = df[df.event_name == "native_call_convolution.out"]
if len(df) > 0:
slowest = df.loc[df["p50"].idxmax()]
assert slowest
print(slowest.name)
print()
pp.pprint(slowest.stack_traces if slowest.stack_traces else "")
print()
pp.pprint(slowest.module_hierarchy if slowest.module_hierarchy else "")
如果用户想要一个模块的总运行时间,他们可以使用
find_total_for_module。
print(inspector.find_total_for_module("L__self__"))
print(inspector.find_total_for_module("L__self___conv2"))
0.0
0.0
注意: find_total_for_module 是
Inspector
结论¶
在这个教程中,我们学习了使用 ExecuTorch 开发者工具消费 ExecuTorch 模型所需的步骤。它还展示了如何使用 Inspector API 分析模型运行结果。