TorchScriptでScriptModuleに子モジュールを追加:add_module()徹底解説


torch.jit.ScriptModule.add_module() は、TorchScript における ScriptModule クラスのメソッドで、子モジュールを親モジュールに追加するためのものです。子モジュールは、nn.Module クラスのインスタンスである必要があります。

使用方法

torch.jit.ScriptModule.add_module(name, module)
  • module: 追加する子モジュール (nn.Module クラスのインスタンス)
  • name: 子モジュールに割り当てる名前 (文字列)

import torch
import torch.nn as nn
import torch.jit

class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.linear1 = nn.Linear(10, 20)
    self.linear2 = nn.Linear(20, 5)

  def forward(self, x):
    x = self.linear1(x)
    x = self.linear2(x)
    return x

# ScriptModule に変換
scripted_model = torch.jit.script(MyModel())

# 子モジュールを追加
scripted_model.add_module('my_conv', nn.Conv2d(3, 16, 3))

# 子モジュールにアクセス
print(scripted_model.my_conv)  # nn.Conv2d(3, 16, 3)
  • 子モジュールを追加しても、ScriptModule クラス自体は自動的に再コンパイルされません。再コンパイルが必要な場合は、torch.jit.script() メソッドを再度呼び出す必要があります。
  • add_module() メソッドは、再帰的に呼び出すことができます。
  • 子モジュールは、forward() メソッド内で使用できます。
  • add_module() メソッドは、ScriptModule クラスの属性として子モジュールを追加します。

利点

  • モデルを動的に構成することで、コードの再利用性を向上させることができます。
  • モデルを動的に構成することで、異なる入力データやタスクに応じてモデルを調整することができます。
  • torch.jit.ScriptModule.add_module() メソッドを使用すると、モデルを動的に構成することができます。
  • 子モジュールを追加する前に、ScriptModule クラスが ScriptMode に設定されていることを確認する必要があります。
  • torch.jit.ScriptModule.add_module() メソッドは、訓練済みのモデルには使用できません。


モデルの動的構成

import torch
import torch.nn as nn
import torch.jit

class MyModel(nn.Module):
  def __init__(self, input_dim):
    super().__init__()
    if input_dim == 1:
      self.linear = nn.Linear(1, 10)
    elif input_dim == 2:
      self.conv = nn.Conv2d(1, 16, 3)
      self.pool = nn.MaxPool2d(2)
      self.linear = nn.Linear(16 * 9, 10)
    else:
      raise ValueError(f"Unsupported input dimension: {input_dim}")

  def forward(self, x):
    if input_dim == 1:
      x = self.linear(x)
    elif input_dim == 2:
      x = self.conv(x)
      x = self.pool(x)
      x = x.view(-1, 16 * 9)
      x = self.linear(x)
    else:
      raise ValueError(f"Unsupported input dimension: {input_dim}")
    return x

# ScriptModule に変換
input_dim = 2
scripted_model = torch.jit.script(MyModel(input_dim))

# 子モジュールを追加
print(scripted_model)  # MyModel(linear=Linear(in_features=1, out_features=10))

# 入力データに応じてモデルを動的に構成
if input_dim == 2:
  scripted_model.add_module('my_conv', nn.Conv2d(3, 16, 3))
  print(scripted_model)  # MyModel(linear=Linear(in_features=10, out_features=10), my_conv=Conv2d(3, 16, 3))

この例では、torch.jit.ScriptModule.add_module() メソッドを使用して、共通のサブモジュールを再利用する方法を示します。

import torch
import torch.nn as nn
import torch.jit

class CommonSubmodule(nn.Module):
  def __init__(self):
    super().__init__()
    self.linear1 = nn.Linear(10, 20)
    self.relu = nn.ReLU()

class MyModel1(nn.Module):
  def __init__(self):
    super().__init__()
    self.common = CommonSubmodule()
    self.linear2 = nn.Linear(20, 5)

  def forward(self, x):
    x = self.common(x)
    x = self.linear2(x)
    return x

class MyModel2(nn.Module):
  def __init__(self):
    super().__init__()
    self.common = CommonSubmodule()
    self.linear3 = nn.Linear(20, 1)

  def forward(self, x):
    x = self.common(x)
    x = self.linear3(x)
    return x

# ScriptModule に変換
scripted_model1 = torch.jit.script(MyModel1())
scripted_model2 = torch.jit.script(MyModel2())

# 共通サブモジュールにアクセス
print(scripted_model1.common)  # CommonSubmodule(linear1=Linear(in_features=10, out_features=20), relu=ReLU())
print(scripted_model2.common)  # CommonSubmodule(linear1=Linear(in_features=10, out_features=20), relu=ReLU())


状況に応じて、以下の代替方法を検討してください。

直接子モジュールを割り当てる

最も単純な代替方法は、add_module() メソッドを使用せずに、子モジュールを直接 ScriptModule の属性として割り当てることです。

import torch
import torch.nn as nn
import torch.jit

class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.linear1 = nn.Linear(10, 20)
    self.linear2 = nn.Linear(20, 5)

  def forward(self, x):
    x = self.linear1(x)
    x = self.linear2(x)
    return x

# ScriptModule に変換
scripted_model = torch.jit.script(MyModel())

# 子モジュールを直接割り当てる
scripted_model.my_conv = nn.Conv2d(3, 16, 3)

# 子モジュールにアクセス
print(scripted_model.my_conv)  # nn.Conv2d(3, 16, 3)

torch.nn.ModuleDict を使用する

より柔軟な方法として、torch.nn.ModuleDict を使用して子モジュールを管理することができます。ModuleDict は、名前とモジュールのペアの辞書として機能します。

import torch
import torch.nn as nn
import torch.jit

class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.modules = nn.ModuleDict({
      'linear1': nn.Linear(10, 20),
      'linear2': nn.Linear(20, 5)
    })

  def forward(self, x):
    x = self.modules['linear1'](x)
    x = self.modules['linear2'](x)
    return x

# ScriptModule に変換
scripted_model = torch.jit.script(MyModel())

# 子モジュールを追加
scripted_model.modules['my_conv'] = nn.Conv2d(3, 16, 3)

# 子モジュールにアクセス
print(scripted_model.modules['my_conv'])  # nn.Conv2d(3, 16, 3)

カスタムコンテナクラスを作成する

高度な制御が必要な場合は、独自のカスタムコンテナクラスを作成することができます。このクラスは、子モジュールの追加、削除、アクセスを制御する独自のロジックを実装できます。

add_module() を拡張する

add_module() メソッドを拡張して、独自の機能を追加することもできます。これにより、子モジュールの追加方法をより細かく制御することができます。

選択の指針

上記で紹介した代替方法はそれぞれ、異なる利点と欠点があります。

  • add_module() を拡張する:** 既存の add_module() メソッドの機能を拡張できますが、複雑でエラーが発生しやすい可能性があります。
  • カスタムコンテナクラスを作成する
    高度な制御が可能ですが、実装が複雑になります。
  • torch.nn.ModuleDict を使用する:** より柔軟な方法ですが、コードが冗長になる可能性があります。
  • 直接子モジュールを割り当てる
    最もシンプルでわかりやすい方法ですが、柔軟性に欠けます。

状況に応じて、適切な代替方法を選択してください。

  • 子モジュールを追加しても、ScriptModule クラス自体は自動的に再コンパイルされません。再コンパイルが必要な場合は、torch.jit.script() メソッドを再度呼び出す必要があります。
  • いずれの代替方法を使用する場合でも、ScriptModule クラスが ScriptMode に設定されていることを確認する必要があります。