TorchScript: ScriptModule.modules() 関数の詳細解説と代替方法


torch.jit.ScriptModule.modules() は、TorchScript ScriptModule 内のすべてのサブモジュールを階層的に返すジェネレータ関数です。サブモジュールは、nn.Module のサブクラスである任意のオブジェクトです。

使用方法

for name, module in model.modules():
    print(f"{name}: {module}")

このコードは、model という名前の ScriptModule 内のすべてのサブモジュールをループし、各サブモジュールの名前とオブジェクトを出力します。

import torch
import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.fc = nn.Linear(9600, 10)

model = MyModel()
script_model = torch.jit.script(model)

for name, module in script_model.modules():
    print(f"{name}: {module}")

このコードは、以下の出力を生成します。

conv1: Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=True)
conv2: Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=True)
fc: Linear(in_features=9600, out_features=10, bias=True)
  • torch.jit.ScriptModule.modules() は、TorchScript でトレースされたモデルをデバッグする際に役立ちます。
  • モジュール名とオブジェクトに加えて、torch.jit.ScriptModule.modules() は、各サブモジュールの属性とパラメータにもアクセスできます。
  • torch.jit.ScriptModule.modules() は、再帰的にサブモジュールを探索するため、ネストされたサブモジュールもすべて返されます。


サブモジュールの属性とパラメータへのアクセス

import torch
import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.fc = nn.Linear(9600, 10)

model = MyModel()
script_model = torch.jit.script(model)

for name, module in script_model.modules():
    print(f"{name}:")
    print(f"  attributes: {module.__dict__}")
    print(f"  parameters: {module.parameters()}")
conv1:
  attributes: {'weight': Parameter containing: [32, 1, 3, 3], 'bias': Parameter containing: [32]}
  parameters: [Parameter containing: [32, 1, 3, 3], Parameter containing: [32]]
conv2:
  attributes: {'weight': Parameter containing: [64, 32, 3, 3], 'bias': Parameter containing: [64]}
  parameters: [Parameter containing: [64, 32, 3, 3], Parameter containing: [64]]
fc:
  attributes: {'weight': Parameter containing: [10, 9600], 'bias': Parameter containing: [10]}
  parameters: [Parameter containing: [10, 9600], Parameter containing: [10]]

特定の条件に一致するサブモジュールのフィルタリング

import torch
import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.fc = nn.Linear(9600, 10)

model = MyModel()
script_model = torch.jit.script(model)

for name, module in script_model.modules() if isinstance(module, nn.Conv2d):
    print(f"{name}: {module}")
conv1: Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=True)
conv2: Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=True)
import torch
import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, 3, 1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.fc = nn.Linear(9600, 10)

model = MyModel()
script_model = torch.jit.script(model)

def print_submodule_tree(module, indent=0):
    print(f"{' ' * indent}{module.__class__.__name__}: {module}")
    for name, child in module.named_children():
        print_submodule_tree(child, indent + 4)

print_submodule_tree(script_model)
MyModel: MyModel
    conv1: Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=True)
        conv2: Sequential


手動の再帰処理

最も基本的な代替方法は、手動で再帰処理を行うことです。これは、サブモジュールの探索方法を完全に制御したい場合に役立ちます。

def traverse_modules(module, indent=0):
    print(f"{' ' * indent}{module.__class__.__name__}: {module}")
    for name, child in module.named_children():
        traverse_modules(child, indent + 4)

model = MyModel()
script_model = torch.jit.script(model)
traverse_modules(script_model)

利点

  • コードが簡潔になる場合がある
  • サブモジュールの探索方法を完全に制御できる

欠点

  • 再帰処理のロジックを実装する必要がある
  • 煩雑でエラーが発生しやすい

inspect モジュール

inspect モジュールを使用して、ScriptModule 内のサブモジュールを反復処理することもできます。 これは、より簡潔で、エラーが発生しにくい方法です。

import inspect

def traverse_modules(module):
    for name, obj in inspect.getmembers(module):
        if inspect.ismodule(obj):
            print(f"{name}: {obj}")
            traverse_modules(obj)

model = MyModel()
script_model = torch.jit.script(model)
traverse_modules(script_model)

利点

  • エラーが発生しにくい
  • コードが簡潔で読みやすい

欠点

  • inspect モジュールのインポートが必要
  • サブモジュールの探索方法を完全に制御できない

カスタムジェネレータ

ScriptModule クラスのサブクラスを作成し、modules() メソッドをオーバーライドしてカスタム ジェネレータを実装することもできます。 これは、複雑なサブモジュール階層を持つ場合や、サブモジュールに対して特別な処理を実行する必要がある場合に役立ちます。

class MyScriptModule(torch.jit.ScriptModule):
    def __init__(self, model):
        super().__init__(model)

    def modules(self, recurse=True):
        yield self
        for name, module in self.named_children():
            yield from module.modules(recurse=recurse)

model = MyModel()
script_model = MyScriptModule(model)

for module in script_model.modules():
    print(module)

利点

  • サブモジュールに対して特別な処理を実行できる
  • サブモジュールの探索方法を完全に制御できる

欠点

  • ScriptModule クラスのサブクラスを作成する必要がある
  • コードが煩雑でエラーが発生しやすい

最適な代替方法の選択

最適な代替方法は、ニーズと要件によって異なります。

  • 複雑なサブモジュール階層を持つ場合や、サブモジュールに対して特別な処理を実行する必要がある場合は、カスタム ジェネレータを実装します。
  • コードを簡潔で読みやすくしたい場合は、inspect モジュールを使用します。
  • サブモジュールの探索方法を完全に制御したい場合は、手動の再帰処理が最適です。
  • TorchScript には、サブモジュールを操作するための他にもさまざまなメソッドがあります。 詳細については、TorchScript ドキュメントを参照してください。
  • 上記に記載されている代替方法は、すべて ScriptModule 内のサブモジュールのみを反復処理します。 サブモジュールの属性やパラメータにアクセスするには、module.__dict__module.parameters() などの属性を使用する必要があります。