PyTorch Quantization:融合モジュールで量子化推論を高速化 - 「torch.ao.quantization.backend_config.BackendPatternConfig.set_fused_module」の使い方と代替方法


torch.ao.quantization.backend_config.BackendPatternConfig.set_fused_module() は、PyTorch Quantizationにおいて、複数のモジュールを融合したカスタムモジュールを指定するためのメソッドです。このメソッドを使用することで、量子の推論効率を向上させることができます。

引数

  • fused_module: 融合されたカスタムモジュールのクラス。このモジュールは、元のモジュールと同じ入出力と機能を持つ必要があります。

動作

  1. BackendPatternConfig オブジェクトは、特定のパターンの量子化方法を定義します。set_fused_module() メソッドを呼び出すことで、このパターンにおける融合モジュールを指定できます。
  2. PyTorch Quantizationは、推論時に、元のモジュールを融合モジュールに置き換えます。これにより、計算量とメモリ使用量を削減できます。

import torch
from torch.ao.quantization.backend_config import BackendPatternConfig, DTypeConfig, ObservationType

def fuse_conv2d_relu(is_qat, conv, relu):
    return torch.nn.intrinsic.ConvReLU2d(conv, relu)

# 畳み込み2DとReLUを融合するパターンを定義
conv_relu_config = BackendPatternConfig((torch.nn.Conv2d, torch.nn.ReLU))

# 観察タイプを設定
conv_relu_config.set_observation_type(ObservationType.OUTPUT_USE_DIFFERENT_OBSERVER_AS_INPUT)

# データ型設定
weighted_int8_dtype_config = DTypeConfig(
    input_dtype=torch.quint8,
    output_dtype=torch.quint8,
    weight_dtype=torch.qint8,
    bias_dtype=torch.float,
)
conv_relu_config.add_dtype_config(weighted_int8_dtype_config)

# 融合モジュールを設定
conv_relu_config.set_fused_module(torch.ao.nn.intrinsic.ConvReLU2d)

# 融合方法を設定
conv_relu_config.set_fuser_method(fuse_conv2d_relu)

上記の例では、conv2drelu モジュールの融合を定義しています。fuse_conv2d_relu 関数は、融合されたカスタムモジュールを実装します。

利点

  • モデルの保守性の向上
  • コードの簡素化
  • 量子の推論効率の向上
  • すべてのバックエンドが融合をサポートしているわけではありません。
  • 融合モジュールは、元のモジュールと同じ入出力と機能を持つ必要があります。


import torch
from torch.ao.quantization.backend_config import BackendPatternConfig, DTypeConfig, ObservationType
from torch.ao.quantization.quantizer import Quantizer
from torch.quantization import QuantStub, DeQuantStub

# モデルを定義
class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 32, 3, 1, padding=1)
        self.relu1 = torch.nn.ReLU()
        self.conv2 = torch.nn.Conv2d(32, 64, 3, 1, padding=1)
        self.relu2 = torch.nn.ReLU()

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        return x

# 畳み込み2DとReLUを融合するパターンを定義
conv_relu_config = BackendPatternConfig((torch.nn.Conv2d, torch.nn.ReLU))

# 観察タイプを設定
conv_relu_config.set_observation_type(ObservationType.OUTPUT_USE_DIFFERENT_OBSERVER_AS_INPUT)

# データ型設定
weighted_int8_dtype_config = DTypeConfig(
    input_dtype=torch.quint8,
    output_dtype=torch.quint8,
    weight_dtype=torch.qint8,
    bias_dtype=torch.float,
)
conv_relu_config.add_dtype_config(weighted_int8_dtype_config)

# 融合モジュールを設定
conv_relu_config.set_fused_module(torch.ao.nn.intrinsic.ConvReLU2d)

# 融合方法を設定
def fuse_conv2d_relu(is_qat, conv, relu):
    return torch.nn.intrinsic.ConvReLU2d(conv, relu)

conv_relu_config.set_fuser_method(fuse_conv2d_relu)

# モデルを準備
model = MyModel()
qat_model = torch.quantization.quantize_dynamic(
    model,
    {torch.nn.Conv2d: QuantStub},
    {torch.nn.ReLU: DeQuantStub},
    backend_config=conv_relu_config,
)

# 量子化を行う
quantizer = Quantizer(qat_model)
quantizer.prepare()
quantizer.convert()

# 量子化モデルを評価

このコードでは、まず MyModel クラスを定義します。このモデルには、2つの畳み込み2D層と2つのReLU層が含まれています。

次に、conv_relu_config オブジェクトを作成して、畳み込み2DとReLUを融合するパターンを定義します。このオブジェクトには、観察タイプ、データ型、融合モジュール、融合方法などの設定が含まれます。

続いて、qat_model を作成して、モデルを動的に量子化します。このモデルには、QuantStub モジュールと DeQuantStub モジュールが挿入されます。

最後に、quantizer オブジェクトを作成して、モデルを量子化します。

  • このコードは、PyTorch 1.12以降で動作します。


torch.quantization.QuantFusion を使用する

torch.quantization.QuantFusion は、モデルの量子化時にモジュールを融合するための機能です。set_fused_module() メソッドと比較して、以下の利点があります。

  • より柔軟なモジュール融合
  • より簡潔なコード

QuantFusion を使用する例:

import torch
from torch.quantization import QuantFusion

# モデルを定義
class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 32, 3, 1, padding=1)
        self.relu1 = torch.nn.ReLU()
        self.conv2 = torch.nn.Conv2d(32, 64, 3, 1, padding=1)
        self.relu2 = torch.nn.ReLU()

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        return x

# モデルを準備
model = MyModel()

# モデルを融合
fused_model = QuantFusion.fuse(model, [torch.nn.Conv2d, torch.nn.ReLU])

# 量子化を行う
quantizer = Quantizer(fused_model)
quantizer.prepare()
quantizer.convert()

# 量子化モデルを評価

torch.quantization.quantize_dynamic の add_pattern オプションを使用する

torch.quantization.quantize_dynamicadd_pattern オプションを使用して、融合パターンを定義することもできます。この方法は、QuantFusion よりも詳細な制御を提供します。

add_pattern を使用する例:

import torch
from torch.quantization import QuantStub, DeQuantStub, Quantizer

# モデルを定義
class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 32, 3, 1, padding=1)
        self.relu1 = torch.nn.ReLU()
        self.conv2 = torch.nn.Conv2d(32, 64, 3, 1, padding=1)
        self.relu2 = torch.nn.ReLU()

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        return x

# モデルを準備
model = MyModel()

# 量子化を行う
quantizer = Quantizer(model)

# 融合パターンを定義
def fuse_conv2d_relu(is_qat, conv, relu):
    return torch.nn.intrinsic.ConvReLU2d(conv, relu)

quantizer.add_pattern(torch.nn.Sequential(torch.nn.Conv2d, torch.nn.ReLU), fuse_conv2d_relu)

quantizer.prepare()
quantizer.convert()

# 量子化モデルを評価

手動でモジュールを融合する

最も詳細な制御が必要な場合は、手動でモジュールを融合することができます。この方法は、複雑なモデルやカスタムモジュールを使用する場合は役立ちます。

手動でモジュールを融合する例:

import torch
from torch.nn.intrinsic import ConvReLU2d

# モデルを定義
class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 32, 3, 1, padding=1)
        self.relu1 = torch.nn.ReLU()
        self.conv2 = torch.nn.Conv