このシリーズではE資格対策として、書籍「ゼロから作るDeep Learning」を参考に学習に役立つ情報をまとめています。

<参考書籍>

ReLUレイヤ

ReLU(Rectified Linear Unit)活性化関数のクラスを実装します。ReLU関数は、ニューラルネットワークの活性化関数として広く使用されており、入力が0以下の場合には0を出力し、それ以外の場合には入力をそのまま出力します。また、逆伝播も考慮しています。

class Relu:
    def __init__(self):
        # mask変数を初期化
        self.mask = None

    def forward(self, x):
        # xの要素が0以下の場合、Trueとなるマスクを作成
        self.mask = (x <= 0)
        # xのコピーを作成
        out = x.copy()
        # マスクがTrueの場合、outの対応する要素を0に設定
        out[self.mask] = 0

        # 出力を返す
        return out

    def backward(self, dout):
        # マスクがTrueの場合、doutの対応する要素を0に設定
        dout[self.mask] = 0
        # dxにdoutを代入し、dxを返す
        dx = dout

        return dx

ReLUレイヤの実装

ここでは、適当な数値を持つnumpy配列を作成し、ReLUクラスのインスタンスを使って順伝播と逆伝播を実行してみます。

import numpy as np

# 適当な数値を持つnumpy配列を作成
x = np.array([-1.0, 0.0, 1.0, -0.5, 2.0, 3.0])
print("入力x:", x)

# ReLUクラスのインスタンスを作成
relu = Relu()

# 順伝播
out = relu.forward(x)
print("順伝播の結果:", out)

# 逆伝播のための適当な勾配を作成
dout = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0])

# 逆伝播
dx = relu.backward(dout)
print("逆伝播の結果:", dx)

実行結果:

入力x: [-1.   0.   1.  -0.5  2.   3. ]
順伝播の結果: [0. 0. 1. 0. 2. 3.]
逆伝播の結果: [0. 0. 1. 0. 1. 1.]
  • 順伝播の結果、ReLU関数は入力配列の要素が0以下の場合、0を出力し、それ以外の場合、入力要素をそのまま出力していることがわかります。
  • 逆伝播の結果、入力配列の要素が0より大きい場合、勾配はそのまま伝搬されていますが、0以下の場合、勾配が0になっていることがわかります。これは、ReLU関数が線形領域(正の値)で微分可能であり、その導関数が1であるためです。一方、非線形領域(負の値)では、導関数が0であり、勾配が伝搬されません。

Sigmoidレイヤ

シグモイド活性化関数のクラスを実装しています。シグモイド関数は、ニューラルネットワークの活性化関数として広く使用されていましたが、現在ではReLU関数など他の関数に取って代わられつつあります。シグモイド関数は、入力を0から1の範囲に変換するため、確率に関連する問題で便利です。また、逆伝播も考慮しています。

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

class Sigmoid:
    def __init__(self):
        # out変数を初期化
        self.out = None

    def forward(self, x):
        # シグモイド関数を適用して出力を計算
        out = sigmoid(x)
        # 出力をインスタンス変数に保存
        self.out = out
        # 出力を返す
        return out

    def backward(self, dout):
        # 逆伝播の際の勾配を計算
        dx = dout * (1.0 - self.out) * self.out

        # 勾配を返す
        return dx

Sigmoidレイヤの実装

適当な数値を持つnumpy配列を作成し、Sigmoidクラスのインスタンスを使って順伝播と逆伝播を実行してみます。

# 適当な数値を持つnumpy配列を作成
x = np.array([-1.0, 0.0, 1.0, -0.5, 2.0, 3.0])
print("入力x:", x)

# Sigmoidクラスのインスタンスを作成
sigmoid_layer = Sigmoid()

# 順伝播
out = sigmoid_layer.forward(x)
print("順伝播の結果:", out)

# 逆伝播のための適当な勾配を作成
dout = np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0])

# 逆伝播
dx = sigmoid_layer.backward(dout)
print("逆伝播の結果:", dx)

実行結果:

入力x: [-1.   0.   1.  -0.5  2.   3. ]
順伝播の結果: [0.26894142 0.5  0.73105858 0.37754067 0.88079708 0.95257413]
逆伝播の結果: [0.19661193 0.25 0.19661193 0.23500371 0.10499359 0.04517666]
  • 順伝播の結果、シグモイド関数は入力配列の要素を0から1の範囲に変換しています。シグモイド関数は、連続的で微分可能な関数であり、確率に関連する問題で役立ちます。
  • 逆伝播の結果、シグモイド関数の勾配が計算されています。この勾配は、シグモイド関数の導関数である sigmoid(x) * (1 - sigmoid(x)) に基づいています。逆伝播の際、この勾配が前の層に伝搬されます。シグモイド関数の勾配は、0から0.25の範囲になります。これは、シグモイド関数が飽和すると(入力の絶対値が大きくなると)、勾配が小さくなり、勾配消失問題が発生することを示しています。これが、現在ではReLU関数など他の活性化関数に取って代わられつつある理由の1つです。

まとめ

最後までご覧いただきありがとうございました。