AIMET API 文档(1)


1 适用于 PyTorch 的 AIMET API

1.1 AIMET PyTorch 量化 API

为了充分利用 AIMET 量化功能,鼓励用户在定义 PyTorch 模型时遵循几条准则。 AIMET 提供的 API 可以自动执行一些模型定义更改,并检查 AIMET 量化功能是否可以应用于 PyTorch 模型。

在使用任何 AIMET 量化功能之前,用户应首先调用模型准备器 API。

PyTorch 模型的 AIMET 量化提供以下功能。

如果用户想将Multi-GPU与CLE或QAT结合使用,可以参考:

  • 多 GPU 指南:将 PyTorch DataParallel API 与 AIMET 功能结合使用的指南

1.1.1 PyTorch 模型指南

为了充分利用 AIMET 功能,鼓励用户在定义 PyTorch 模型时遵循几条准则。

模型应支持转换为 onnx

模型定义应支持转换为onnx,用户可以检查模型对onnx转换的兼容性,如下所示:

...
model = Model()
torch.onnx.export(model, <dummy_input>, <onnx_file_name>):

模型应该是 jit 可追踪的

模型定义应该是jit可追踪的,用户可以检查jit追踪模型的兼容性,如下所示:

...
model = Model()
torch.jit.trace(model, <dummy_input>):

将层定义为模块,而不是使用 torch.nn.function 等效项

当使用激活函数和其他无状态层时,PyTorch 将允许用户

  • 将层定义为模块(在构造函数中实例化并在前向传递中使用),或者
  • 纯粹在前向传播中使用 torch.nn.function 等价物

对于AIMET量化仿真模型添加仿真节点,AIMET需要前者(层定义为模块)。 更改模型定义以使用模块而不是函数在数学上是等效的,并且不需要重新训练模型。

举个例子,如果用户有:

def forward(...):
    ...
    x = torch.nn.functional.relu(x)
    ...

用户应该将他们的模型定义为:

def __init__(self,...):
    ...
    self.relu = torch.nn.ReLU()
    ...

def forward(...):
    ...
    x = self.relu(x)
    ...

在某些情况下,这是不可能的,因为操作只能表示为函数而不是类定义,但应尽可能遵循。

此外,用户还可以使用模型准备器 API 来自动化此操作

避免重用类定义的模块

类定义中定义的模块只能使用一次。 如果重用任何模块,请在类定义中定义一个新的相同模块。 例如,如果用户有:

def __init__(self,...):
    ...
    self.relu = torch.nn.ReLU()
    ...

def forward(...):
    ...
    x = self.relu(x)
    ...
    x2 = self.relu(x2)
    ...

用户应该将他们的模型定义为:

def __init__(self,...):
    ...
    self.relu = torch.nn.ReLU()
    self.relu2 = torch.nn.ReLU()
    ...

def forward(...):
    ...
    x = self.relu(x)
    ...
    x2 = self.relu2(x2)
    ...

此外,用户还可以使用模型准备器 API 来自动化此操作

仅使用 torch.Tensor 或 torch.Tensors 的元组作为模型/子模块输入和输出

模块应使用张量或张量元组作为输入和输出,以支持模型到 onnx 的转换。 例如,如果用户有:

def __init__(self,...):
...
def forward(self, inputs: Dict[str, torch.Tensor]):
    ...
    x = self.conv1(inputs['image_rgb'])
    rgb_output = self.relu1(x)
    ...
    x = self.conv2(inputs['image_bw'])
    bw_output = self.relu2(x)
    ...
    return {
    
     'rgb': rgb_output, 'bw': bw_output }

用户应该将他们的模型定义为:

def __init__(self,...):
...
def forward(self, image_rgb, image_bw):
    ...
    x = self.conv1(image_rgb)
    rgb_output = self.relu1(x)
    ...
    x = self.conv2(image_bw)
    bw_output = self.relu2(x)
    ...
    return rgb_output, bw_output

1.1.2 架构检查器 API

aimet_torch.arch_checker.arch_checker.ArchChecker.check_model_arch(model, dummy_input, result_dir=None)

使用 _node_check_dict 中的检查检查模型中的每个节点。 仅记录节点和失败的测试。

参数

  • model(Module) - 要进行检查的Torch模型。
  • dummy_input (Union[Tensor, Tuple]) – 将输入传递给模型。 可以是张量或张量元组

返回 arch_checker_report :{op.dotted_name_op: NodeErrorReportObject }
返回类型ArchCheckerReport

AIMET PyTorch 架构检查器有助于检查次优模型构造,并提供更新模型以提高性能的潜在选项。 架构检查器当前检查以下条件:

  • 用于最佳通道大小的卷积层。
  • 不具备性能的激活函数。
  • 批量归一化层不能折叠。
  • 具有填充的卷积层序列中的中间卷积层。

在本节中,我们将介绍未通过架构检查的模型,并展示如何运行架构检查器。

示例 1:通道不足的模型

我们从以下模型开始,其中包含通道数小于 32 的卷积层。

class ModelWithNotEnoughChannels(torch.nn.Module):
    """ Model that prelu module. Expects input of shape (1, 3, 32, 32) """

    def __init__(self):
        super(ModelWithNotEnoughChannels, self).__init__()
        self.conv1 = torch.nn.Conv2d(3, 31, kernel_size=2, stride=2, padding=2, bias=False)
        self.bn1 = torch.nn.BatchNorm2d(31)

    def forward(self, *inputs):
        x = self.conv1(inputs[0])
        x = self.bn1(x)
        return x

导入架构检查器:

从aimet_torch.arch_checker.arch_checker导入ArchChecker

通过传入模型以及模型输入来运行模型检查器:

def example_check_for_number_of_conv_channels():

    model = ModelWithNotEnoughChannels()
    ArchChecker.check_model_arch(model, dummy_input=torch.rand(1, 3, 32, 32))

模型中的卷积层少了一个通道,将出现以下记录器打印:

Utils - INFO - Graph/Node: ModelWithNotEnoughChannels.conv1: Conv2d(3, 31, kernel_size=(2, 2), stride=(2, 2), padding=(2, 2), bias=False) fails check: {
    
    '_check_conv_channel_32_base', '_check_conv_channel_larger_than_32'}

将生成包含以下内容的 HTML 文件。
HTML 报告内容

图/图层名称 问题 推荐
ModelWithNotEnoughChannels.conv1 该卷积的输入/输出张量的通道大小小于32 尝试将通道调整为 32 的倍数以获得更好的性能
ModelWithNotEnoughChannels.conv1 该卷积的输入/输出张量的通道大小不是32的倍数 尝试将通道调整为 32 的倍数以获得更好的性能。

示例 2:具有非性能激活的模型

我们从以下模型开始,其中包含通道数小于 32 的卷积层。

class ModelWithPrelu(torch.nn.Module):
    """ Model that prelu module. Expects input of shape (1, 3, 32, 32) """

    def __init__(self):
        super(ModelWithPrelu, self).__init__()
        self.conv1 = torch.nn.Conv2d(32, 32, kernel_size=2, stride=2, padding=2, bias=False)
        self.bn1 = torch.nn.BatchNorm2d(32)
        self.prelu1 = torch.nn.PReLU()

    def forward(self, *inputs):
        x = self.conv1(inputs[0])
        x = self.bn1(x)
        x = self.prelu1(x)
        return x

通过传入模型以及模型输入来运行模型检查器:

def example_check_for_non_performant_activations():

    model = ModelWithPrelu()
    ArchChecker.check_model_arch(model, dummy_input=torch.rand(1, 32, 32, 32))

与 ReLU 相比,模型中的 PReLU 层被认为性能不佳,将出现以下记录器打印:

Utils - INFO - Graph/Node: ModelWithPrelu.prelu1: PReLU(num_parameters=1) fails check: {
    
    '_activation_checks'}

示例 3:具有独立批量归一化层的模型

我们从以下模型开始,其中包含通道数小于 32 的卷积层。

class ModelWithNonfoldableBN(torch.nn.Module):
    """ Model that has non-foldable batch norm. """

    def __init__(self):
        super(ModelWithNonfoldableBN, self).__init__()
        self.conv1 = torch.nn.Conv2d(32, 32, kernel_size=2, stride=2, padding=2, bias=False)
        self.avg_pool1 = torch.nn.AvgPool2d(3, padding=1)
        self.bn1 = torch.nn.BatchNorm2d(32)

    def forward(self, *inputs):
        x = self.conv1(inputs[0])
        x = self.avg_pool1(x)
        x = self.bn1(x)
        return x

通过传入模型以及模型输入来运行模型检查器:

def example_check_for_standalone_bn():

    model = ModelWithNonfoldableBN()
    ArchChecker.check_model_arch(model, dummy_input=torch.rand(1, 32, 32, 32))

AveragePool 层防止 BatchNormalization 层与 Convolution 层折叠,将出现以下记录器打印:

Utils - INFO - Graph/Node: ModelWithNonfoldableBN.bn1: BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) fails check: {
    
    '_check_batch_norm_fold'}

猜你喜欢

转载自blog.csdn.net/weixin_38498942/article/details/132545223