『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

中間層の実装

Middle_layerクラス

活性化関数クラスの実装

今回設計する中間層(Middle_layer クラス)では、メソッドに活性化関数と、その導関数を渡す必要があります。これらの関数を別々に設定して渡すのは煩わしいので、あらかじめクラスの属性値としてまとめておくことにします。たとえば、Sigmoid クラスは属性 y でシグモイド関数値を、属性 dy でシグモイド関数の微分係数を取得できるようにします。

Middle_layer クラスでは、ReLU、$\tanh(x)$、シグモイド関数 の中から活性化関数を選択できるようにするので、この 3 種類の関数についてクラスをまとめて設計しておきます。

# In[3]

# ReLUクラス
class ReLU:
    def __init__(self, x):
        self.y = np.where(x <= 0, 0, x)
        self.dy = np.where(x <= 0, 0, 1)

# Tanhクラス
class Tanh:
    def __init__(self, x):
        self.y = np.tanh(x)
        self.dy = 1 / np.cosh(x)**2

# Sigmoidクラス
class Sigmoid:
    def __init__(self, x):
        self.y = 1 / (1 + np.exp(-x))
        self.dy = (1 - self.y) * self.y

Sigmoidクラスのインスタンスをつくって、x = 1 における関数値と微分係数を取得してみます。

# In[4]

# Sigmoidオブジェクトを生成
f = Sigmoid(1)

# 関数値を取得
y = f.y

# 微分係数を取得
dy = f.dy

print("y_1 = {}".format(y))
print("(dy/dx)_1 = {}".format(dy))

# y_1 = 0.7310585786300049
# (dy/dx)_1 = 0.19661193324148185

後でまた扱いますが、ニューラルネットワークの規模が大きくなると、過学習や学習遅滞などの様々な問題が発生します。ある状況で上手くいかないと思ったら、活性化関数を変えてみるのも一つの手です。上に挙げた関数以外にも色々な種類の活性化関数を考案できるかもしれません。自分でクラスを定義して色々試してみてください。

Middle_layerクラスの実装

Middle_layerクラスのオブジェクトがもつ機能をまとめておきます。
 
基本機能
・重みの初期値を生成してデータを保持しておく。
・活性化関数を設定する。
 
順伝播 (Forward Propagation)
・上の層から入力信号を受け取る。
・入力総和を計算して保持しておく。
・入力総和を活性化関数に通して出力する。
 
逆伝播 (Back Propagation)
・逆信号を受け取る。
・誤差 $\delta_j$ を計算する。
・$\delta_j$ に重みをつけて上の層へ出力する。
・重みパラメータを修正する。

出力層はネットワークの一番下にある層なので、activate()メソッド1つですべての計算を行ないましたが、中間層では、その下の層からの逆伝播が伝わってくるまで自身からの逆信号出力や重みの更新などはできません。したがって、順伝播と逆伝播は別々のメソッドで実行する必要があります。

# In[5]

# 中間層クラスを定義
class Middle_layer:
    def __init__(self, n_upper, n, func):
        self.w = np.random.normal(0, 0.05, (n, n_upper + 1))
        self.w2 = np.delete(self.w, -1, 1)  # バイアスの重みを削除した配列
        self.func = func  # 活性化関数

    def forward(self, y_in):
        dummy = np.ones((y_in.shape[0], 1))  # 疑似信号を作成
        self.y_in = np.append(y_in, dummy, axis = 1)  # 入力信号に疑似信号を追加
        self.u = np.dot(self.y_in, self.w.T)  # 入力総和を計算
        self.y_out = self.func(self.u).y  # 入力総和を活性化関数に通して出力

    def backward(self, y_b):
        self.delta = self.func(self.u).dy * y_b  # この層の誤差δjを計算
        self.y_back = np.dot(self.delta, self.w2)  # 逆伝播信号を出力
        self.w -= alpha * np.dot(self.delta.T, self.y_in)  # 重みの更新
        self.w2 = np.delete(self.w, -1, 1)

class Middle_layer クラスのインスタンスは、上の層のニューロンの個数とこの層のニューロンの個数、および活性化関数クラスを受け取って生成されます。
forward()メソッドは上の層から入力信号を受け取って順伝播を実行し、backward()メソッドは下の層から逆信号を受け取って逆伝播と重みの更新を実行します。
 
テスト信号を入力して中間層の機能を確認しておきます。

# In[6]

# 中間層の動作テスト

# 乱数を初期化
np.random.seed(11)

# 学習率を設定
alpha = 0.01

# 入力信号
test_in = np.array([[3, 2],
                    [2, 1]])

# 逆信号
test_b = np.array([[0.1, 0.2, 0.3],
                   [0.2, 0.1, 0.1]])

# 中間層を作成
mid = Middle_layer(2, 3, Sigmoid)

# 順伝播
mid.forward(test_in)

# 重みの初期値を表示
print("重みの初期値:\n{}\n".format(mid.w))

# 順伝播信号を表示
print("順伝播信号:\n{}\n".format(mid.y_out))

# 逆伝播
mid.backward(test_b)

# この層の誤差を表示
print("誤差:\n{}\n".format(mid.delta))

# 逆伝播信号を表示
print("逆伝播信号:\n{}\n".format(mid.y_back))

# 更新された重みを表示
print("更新された重み:\n{}".format(mid.w))

'''
重みの初期値:
[[ 0.08747274 -0.01430365 -0.02422826]
 [-0.13266593 -0.00041423 -0.01598157]
 [-0.02683147  0.01577013  0.02105254]]

順伝播信号:
[[0.55220471 0.39775986 0.49302505]
 [0.53405061 0.43003027 0.49579003]]

誤差:
[[0.02472747 0.04790939 0.07498541]
 [0.04976811 0.02451042 0.02499823]]

逆伝播信号:
[[-0.00620493  0.00080899]
 [ 0.00043092 -0.00032779]]

更新された重み:
[[ 0.08573555 -0.01529588 -0.02497321]
 [-0.13459342 -0.00161752 -0.01670577]
 [-0.02958099  0.01402044  0.0200527 ]]
'''

これで各層を作るための部品が揃いました。

コメント

  1. あとりえこばと より:

    ChatGPTに本記事のコードIn[5]を解読させてみました。
    ただし、解読のヒントになりうるコメントアウト文は省きました。

    このコードは、ニューラルネットワークの中間層を表すクラス Middle_layer を実装しています。中間層は入力データを受け取り、そのデータに対して重み付けや活性化関数の計算を行います。以下にコードの主要な部分を説明します。

    __init__(self, n_upper, n, func):
    ・n_upper: 上位層からの入力のユニット数
    ・n: 中間層のユニット数
    ・func: 活性化関数
    このメソッドは、中間層のオブジェクトを初期化します。中間層の重み行列 w をランダムな値で初期化し、活性化関数 func を設定します。

    forward(self, y_in):
    ・y_in: 中間層に入力されるデータ
    このメソッドは、与えられた入力データ y_in を中間層に渡し、次のステップの出力を計算します。
    ・ダミーの1列を追加して self.y_in に保存します。
    ・self.u に重み付き入力を計算します。
    ・self.y_out に活性化関数 func を適用した結果を保存します。

    backward(self, y_b):
    ・y_b: 中間層からの誤差勾配
    このメソッドは、誤差逆伝播法に基づいて中間層の誤差勾配を計算し、重みの更新を行います。
    ・中間層の出力に対する誤差勾配 self.delta を計算します。
    ・逆伝播誤差 self.y_back を計算し、上位層への誤差勾配として保存します。
    ・重み行列 w を誤差勾配と学習率 alpha に基づいて更新します。
    ・w2 は重み行列 w からバイアス項を除いたものを表します。

    このクラスは、ニューラルネットワークの中間層を構築し、順伝播と逆伝播の計算を行うために使用されるものです。活性化関数や学習率などのハイパーパラメータは、このクラスの初期化時に設定され、ニューラルネットワークの訓練に使用されます。