【画像認識・音声認識にも最適】PyTorchでHardswish関数を使いこなす方法とサンプルコード


torch.nn.functional.hardswish は、PyTorchの nn.functional モジュールで提供される活性化関数です。この関数は、入力値に対して非線形変換を行い、ニューラルネットワークの学習効率を向上させる役割を果たします。

関数詳細

torch.nn.functional.hardswish(input)
  • 戻り値: 入力テンソルと同じ形状のテンソル
  • input: 活性化関数の入力となるテンソル

Hardswish 関数の動作

Hardswish 関数は、以下の式で計算されます。

hardswish(x) = x * (2.0 / (6.0 + math.exp(-x)))

この式は、入力値 x が小さい場合はほぼ x 自身、大きい場合は x0 に近づけるように動作します。この特性により、ニューラルネットワークの学習において、勾配消失問題の緩和や活性化関数の飽和を防ぐ効果が期待できます。

Hardswish 関数のメリット

  • モバイル向けニューラルネットワークなど、計算資源に制約のある環境での利用に適しています。
  • 入力値の範囲が広く、ニューラルネットワークの学習効率を向上させることができます。
  • ReLU や sigmoid などの従来の活性化関数と比べて、計算コストが低く、高速に処理できます。

Hardswish 関数のデメリット

  • 一部のハードウェアでは、ネイティブなサポートがない場合があります。
  • ReLU や sigmoid 関数と比べて、表現力がやや劣る場合があります。
import torch
import torch.nn as nn
import torch.nn.functional as F

# 畳み込み層と Hardswish 関数を組み合わせた例
class ConvHardswish(nn.Module):
  def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
    super().__init__()
    self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)

  def forward(self, x):
    x = self.conv(x)
    return F.hardswish(x)

# ニューラルネットワークモデルに Hardswish 関数を組み込む例
class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.conv1 = ConvHardswish(3, 32, 3)
    self.conv2 = ConvHardswish(32, 64, 3)
    self.fc = nn.Linear(64 * 10 * 10, 10)

  def forward(self, x):
    x = self.conv1(x)
    x = F.max_pool2d(x, 2)
    x = self.conv2(x)
    x = F.max_pool2d(x, 2)
    x = x.view(-1, 64 * 10 * 10)
    x = self.fc(x)
    return x


畳み込み層と Hardswish 関数を組み合わせた例

import torch
import torch.nn as nn
import torch.nn.functional as F

class ConvHardswish(nn.Module):
  def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
    super().__init__()
    self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)

  def forward(self, x):
    x = self.conv(x)
    return F.hardswish(x)

このモジュールを実際に使用するには、以下のようにニューラルネットワークモデルに組み込むことができます。

class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.conv1 = ConvHardswish(3, 32, 3)  # 入力チャネル数: 3, 出力チャネル数: 32, カーネルサイズ: 3
    self.conv2 = ConvHardswish(32, 64, 3)  # 入力チャネル数: 32, 出力チャネル数: 64, カーネルサイズ: 3
    self.fc = nn.Linear(64 * 10 * 10, 10)  # 入力特徴量: 64 * 10 * 10, 出力クラス数: 10

  def forward(self, x):
    x = self.conv1(x)
    x = F.max_pool2d(x, 2)
    x = self.conv2(x)
    x = F.max_pool2d(x, 2)
    x = x.view(-1, 64 * 10 * 10)
    x = self.fc(x)
    return x

このモデルは、畳み込み層と Hardswish 関数を組み合わせることで、従来の畳み込み層よりも効率的に学習することができます。

ニューラルネットワークモデル全体に Hardswish 関数を適用

この例では、ニューラルネットワークモデル全体の活性化関数として Hardswish 関数を適用します。これを行うには、nn.Module サブクラスを継承したカスタムモジュールを作成し、forward メソッド内で各層の出力に対して Hardswish 関数を適用します。

import torch
import torch.nn as nn
import torch.nn.functional as F

class HardswishModel(nn.Module):
  def __init__(self, original_model):
    super().__init__()
    self.original_model = original_model

  def forward(self, x):
    for name, module in self.original_model.named_modules():
      if isinstance(module, nn.ReLU) or isinstance(module, nn.Sigmoid):  # ReLU/Sigmoid 関数を Hardswish 関数に置き換え
        module = nn.Hardswish()
      setattr(self, name, module)
    x = self.original_model(x)
    return x

このコードを使用するには、まず元のニューラルネットワークモデルを定義する必要があります。その後、HardswishModel クラスを元のモデルのインスタンスを引数として渡し、新しいモデルのインスタンスを作成します。

original_model = MyModel()  # 上記の例で定義した MyModel インスタンス
hardswish_model = HardswishModel(original_model)

この hardswish_model インスタンスは、元のモデルと同じアーキテクチャを持ちますが、すべての活性化関数が Hardswish 関数に置き換えられています。

この例では、nn.Module サブクラスを継承したカスタム活性化関数として Hardswish 関数を定義します。このカスタム関数をニューラルネットワークモデルで使用するには、nn.Module クラスと同じようにインスタンス化して使用することができます。

import torch
import torch.nn as nn
import torch.nn.functional as F

class Hardswish(nn.Module):
  def __init__(self):
    


nn.ReLU6

nn.ReLU6は、入力値が0を超える場合はその値をそのまま返し、0以下の場合は0を返す活性化関数です。torch.nn.functional.hardswishと比較すると、計算コストが低く、高速に処理できます。また、表現力も比較的高いです。

import torch
import torch.nn as nn
import torch.nn.functional as F

# ReLU6 を使用した例
class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.conv1 = nn.Conv2d(3, 32, 3)
    self.conv2 = nn.Conv2d(32, 64, 3)
    self.fc = nn.Linear(64 * 10 * 10, 10)

  def forward(self, x):
    x = self.conv1(x)
    x = F.relu6(x)
    x = F.max_pool2d(x, 2)
    x = self.conv2(x)
    x = F.relu6(x)
    x = F.max_pool2d(x, 2)
    x = x.view(-1, 64 * 10 * 10)
    x = self.fc(x)
    return x

Swish

Swishは、torch.nn.functional.hardswishと似たような動作をする活性化関数です。torch.nn.functional.hardswishよりも表現力が高く、より複雑なモデルを学習することができます。

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn.modules.activation import Swish

# Swish を使用した例
class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.conv1 = nn.Conv2d(3, 32, 3)
    self.conv2 = nn.Conv2d(32, 64, 3)
    self.fc = nn.Linear(64 * 10 * 10, 10)

  def forward(self, x):
    x = self.conv1(x)
    x = Swish()(x)
    x = F.max_pool2d(x, 2)
    x = self.conv2(x)
    x = Swish()(x)
    x = F.max_pool2d(x, 2)
    x = x.view(-1, 64 * 10 * 10)
    x = self.fc(x)
    return x

カスタム活性化関数

torch.nn.functional.hardswishの代替方法として、独自の活性化関数を定義することもできます。これは、特定のニーズに合わせた活性化関数を設計したい場合に役立ちます。

import torch
import torch.nn as nn
import torch.nn.functional as F

# カスタム活性化関数の実装例
class MyHardswish(nn.Module):
  def __init__(self):
    super().__init__()

  def forward(self, x):
    return x * (2.0 / (6.0 + torch.exp(-x)))

# カスタム活性化関数をモデルで使用した例
class MyModel(nn.Module):
  def __init__(self):
    super().__init__()
    self.conv1 = nn.Conv2d(3, 32, 3)
    self.conv2 = nn.Conv2d(32, 64, 3)
    self.fc = nn.Linear(64 * 10 * 10, 10)
    self.hardswish = MyHardswish()

  def forward(self, x):
    x = self.conv1(x)
    x = self.hardswish(x)
    x = F.max_pool2d(x, 2)
    x = self.conv2(x)
    x = self.hards