Output_layer_cクラス
バックプロパゲーション機能 を持つ ニューラルネットワーク の実装を始めます。
今回は 出力層 をインスタンスとして生成する Output_layer_c クラス を設計します (Python におけるクラスの設計の基本についてはこちらの記事を参照してください)。末尾に添えた _c は classify (分類) 用の出力層であることを意味します。以下に Output_layer_cクラスが備えておくべき機能をまとめておきます。
基本機能
・重みの初期値を生成してデータを保持しておく。
順伝播 (Forward Propagation)
・上の層から入力信号を受け取る。
・正解値信号を受け取る。
・入力信号に重みをつけて入力総和を計算する。
・入力総和をソフトマックス関数に通して出力する。
逆伝播 (Back Propagation)
・出力値と正解値の誤差 $\delta_j$ を計算する。
・平均交差エントロピー誤差を計算する。
・$\delta_j$ の線形和を上の層へ出力する(逆伝播信号)。
・重みパラメータを修正する。
Output_layer_c クラスのコードです。
# Output_classify
# In[1]
import numpy as np
# ソフトマックス関数
def softmax(x):
f = np.exp(x) / np.sum(np.exp(x), axis = 1, keepdims = True)
return f
# 平均交差エントロピー誤差関数
def cross_entropy(y, t):
ce = - np.sum(t * np.log(y + 1e-8)) / y.shape[0]
return ce
class Output_layer_c:
def __init__(self, n_upper, n):
self.w = np.random.normal(0, 0.05, (n, n_upper + 1))
self.w2 = np.delete(self.w, -1, 1)
def activate(self, y_in, t):
self.t = t
dummy = np.ones((y_in.shape[0], 1)) # 疑似信号を作成
y_in = np.append(y_in, dummy, axis = 1) # 入力信号に疑似信号を追加
u = np.dot(y_in, self.w.T) # 入力総和を計算
self.y_out = softmax(u) # 入力総和をソフトマックス関数に通して出力
self.delta = self.y_out - self.t # 出力値と正解値の誤差を計算
self.ce = cross_entropy(self.y_out, self.t) # 交差エントロピー誤差
self.y_back = np.dot(self.delta, self.w2) # 逆伝播信号を出力
self.w -= alpha * np.dot(self.delta.T, y_in) # 重みの更新
self.w2 = np.delete(self.w, -1, 1)
def forward(self, y_in):
dummy = np.ones((y_in.shape[0], 1)) # 疑似信号を作成
y_in = np.append(y_in, dummy, axis = 1) # 入力信号に疑似信号を追加
u = np.dot(y_in, self.w.T) # 入力総和を計算
self.y_out = softmax(u) # 入力総和をソフトマックス関数に通して出力
Output_layer_c クラスのインスタンスは、上層のニューロン数と自身のもつニューロン数を引数に受け取って生成されます。インスタンスが生成されると、自動的に平均 0、標準偏差 0.05 の正規分布を使って重みを初期設定します。
activate()メソッドを使用すると(信号を入力して層を活性化させると)、一斉に計算が実行されて、オブジェクトに新しいデータ属性が付与されるとともに、重みの値が書き換えられます。
forward() は受け取った信号を出力するだけのメソッドで、重みの更新などは行われません。このメソッドが学習過程で使用されることはありませんが、学習済みのネットワークに新しいデータを入れて出力を確認するときなどに使います。
実際に出力層 (Output_layer_c クラスのインスタンス) を作成し、テスト信号を入力して動作を確認してみましょう。出力層は 2 個、1つ上の中間層は 3 個のニューロンで構成されるものとします。バッチサイズ は 2 です。
# In[2]
# 出力層の動作テスト
# 乱数を初期化
np.random.seed(10)
# 学習率を設定
alpha = 0.01
# テスト信号
test_y = np.array([[3, 2, 5],
[2, 1, 4]])
test_t = np.array([[0, 1],
[1, 0]])
# 出力層を作成
out_layer = Output_layer_c(3, 2)
# 重みの初期値を表示
print("重みの初期値:\n{}\n".format(out_layer.w))
# 出力層にテスト信号を入力
out_layer.activate(test_y, test_t)
# 誤差を表示
print("誤差:\n{}\n".format(out_layer.delta))
# 交差エントロピー誤差を表示
print("交差エントロピー誤差:\n{}\n".format(out_layer.ce))
# 逆伝播信号を表示
print("逆伝播信号:\n{}\n".format(out_layer.y_back))
# 更新された重みを表示
print("更新された重み:\n{}".format(out_layer.w))
重みの初期値: [[ 0.06657933 0.03576395 -0.07727001 -0.00041919] [ 0.0310668 -0.03600428 0.01327558 0.00542743]] 誤差: [[ 0.44806288 -0.44806288] [-0.55607208 0.55607208]] 交差エントロピー誤差: 0.7032070887102677 逆伝播信号: [[ 0.01591184 0.03215668 -0.04057012] [-0.01974752 -0.03990831 0.05034988]] 更新された重み: [[ 0.06425888 0.03236341 -0.07743028 0.0006609 ] [ 0.03338724 -0.03260374 0.01343584 0.00434733]]
平均交差エントロピー誤差の値そのものは、バックプロパゲーションで直接使用するものではありませんが、ネットワークの学習効率などを検証するときに必要なので属性に組み込んであります。
コメントを書く