入力層~中間層
順伝播(単層・単ユニット)
- 重み、バイアスともにランダムで生成
- 活性化関数はReLu関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# 順伝播(単層・単ユニット) # 重み W = np.array([[0.1], [0.2]]) ## 試してみよう_配列の初期化 #W = np.zeros(2) #W = np.ones(2) W = np.random.rand(2) #W = np.random.randint(5, size=(2)) print_vec("重み", W) # バイアス b = np.array(0.5) ## 試してみよう_数値の初期化 b = np.array(np.random.rand()) # 0~1のランダム数値 #b = np.random.rand() * 10 -5 # -5~5のランダム数値 print_vec("バイアス", b) # 入力値 x = np.array([2, 3]) print_vec("入力", x) # 総入力 u = np.dot(x, W) + b print_vec("総入力", u) # 中間層出力 z = functions.relu(u) print_vec("中間層出力", z) |
出力結果
*** 重み *** [0.83504192 0.57427099] shape: (2,)
*** バイアス *** 0.9312772127495758 shape: ()
*** 入力 *** [2 3] shape: (2,)
*** 総入力 *** 4.324174027229502 shape: ()
*** 中間層出力 *** 4.324174027229502 shape: ()
考察:
ReLu関数を使用していて、なおかつu>0なので総入力と中間層出力が等しい。
次に総入力が0以下になるような重みとバイアスを取ってみる。
1 2 3 4 5 6 7 |
# 出力がマイナスになるように重みとバイアスを変更する。 # 他の部分は上記とおなじ # 重み W = np.array([[0.1], [0.2]]) # バイアス b = np.array(-1.5) |
*** 重み *** [[0.1] [0.2]] shape: (2, 1)
*** バイアス *** -1.5 shape: ()
*** 入力 *** [2 3] shape: (2,)
*** 総入力 *** [-0.7] shape: (1,)
*** 中間層出力 *** [0.] shape: (1,)
活性化関数がReLu関数でなおかつ総入力uがマイナスなので、中間層出力は0になる。
順伝播(単層・複数ユニット)
- 重みとバイアスはソースコードで指定されたもの
- 活性化関数はシグモイド関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# 順伝播(単層・複数ユニット) # 重み W = np.array([ [0.1, 0.2, 0.3,0], [0.2, 0.3, 0.4, 0.5], [0.3, 0.4, 0.5, 1], ]) ## 試してみよう_配列の初期化 #W = np.zeros((4,3)) #W = np.ones((4,3)) #W = np.random.rand(4,3) #W = np.random.randint(5, size=(4,3)) print_vec("重み", W) # バイアス b = np.array([0.1, 0.2, 0.3]) print_vec("バイアス", b) # 入力値 x = np.array([1.0, 5.0, 2.0, -1.0]) print_vec("入力", x) # 総入力 u = np.dot(W, x) + b print_vec("総入力", u) # 中間層出力 z = functions.sigmoid(u) print_vec("中間層出力", z) |
出力結果
*** 重み *** [[0.1 0.2 0.3 0. ] [0.2 0.3 0.4 0.5] [0.3 0.4 0.5 1. ]] shape: (3, 4) *** バイアス *** [0.1 0.2 0.3] shape: (3,)
*** 入力 *** [ 1. 5. 2. -1.] shape: (4,)
*** 総入力 *** [1.8 2.2 2.6] shape: (3,)
*** 中間層出力 *** [0.85814894 0.90024951 0.93086158] shape: (3,)
順伝播(3層・複数ユニット)
- 入力層、中間層1、中間層2、出力層の構成
- 中間層の活性化関数にはRuLE関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# 順伝播(3層・複数ユニット) # ウェイトとバイアスを設定 # ネートワークを作成 def init_network(): print("##### ネットワークの初期化 #####") network = {} input_layer_size = 3 hidden_layer_size_1=10 hidden_layer_size_2=5 output_layer_size = 4 #試してみよう #_各パラメータのshapeを表示 #_ネットワークの初期値ランダム生成 network['W1'] = np.random.rand(input_layer_size, hidden_layer_size_1) network['W2'] = np.random.rand(hidden_layer_size_1,hidden_layer_size_2) network['W3'] = np.random.rand(hidden_layer_size_2,output_layer_size) network['b1'] = np.random.rand(hidden_layer_size_1) network['b2'] = np.random.rand(hidden_layer_size_2) network['b3'] = np.random.rand(output_layer_size) print_vec("重み1", network['W1'] ) print_vec("重み2", network['W2'] ) print_vec("重み3", network['W3'] ) print_vec("バイアス1", network['b1'] ) print_vec("バイアス2", network['b2'] ) print_vec("バイアス3", network['b3'] ) return network # プロセスを作成 # x:入力値 def forward(network, x): print("##### 順伝播開始 #####") W1, W2, W3 = network['W1'], network['W2'], network['W3'] b1, b2, b3 = network['b1'], network['b2'], network['b3'] # 1層の総入力 u1 = np.dot(x, W1) + b1 # 1層の総出力 z1 = functions.relu(u1) # 2層の総入力 u2 = np.dot(z1, W2) + b2 # 2層の総出力 z2 = functions.relu(u2) # 出力層の総入力 u3 = np.dot(z2, W3) + b3 # 出力層の総出力 y = u3 print_vec("総入力1", u1) print_vec("中間層出力1", z1) print_vec("中間層出力2", z2) print_vec("総入力2", u2) print_vec("出力", y) print("出力合計: " + str(np.sum(y))) return y, z1, z2 # 入力値 x = np.array([1., 2., 4.]) print_vec("入力", x) # ネットワークの初期化 network = init_network() y, z1, z2 = forward(network, x) |
順伝播開始 #####
*** 総入力1 *** [2.45629821 5.10529484 2.94183202 6.00245319 4.62979921 5.69487186 3.63889929 4.1358152 2.50275556 3.13350507] shape: (10,)
*** 中間層出力1 *** [2.45629821 5.10529484 2.94183202 6.00245319 4.62979921 5.69487186 3.63889929 4.1358152 2.50275556 3.13350507] shape: (10,)
*** 中間層出力2 *** [21.42117735 18.29443323 26.67356868 21.18331503 28.13265757] shape: (5,)
*** 総入力2 *** [21.42117735 18.29443323 26.67356868 21.18331503 28.13265757] shape: (5,)
*** 出力 *** [50.30662356 29.89105075 55.43581744 69.80246561] shape: (4,) 出力合計: 205.43595736146057
中間層2の出力から出力層へ行く間にも\(\bf{u} = \bf{W} + \bf{b}\)の計算が行われている。
Section2 活性化関数
ステップ関数
1 2 3 |
# ステップ関数(閾値0) def step_function(x): return np.where( x > 0, 1, 0) |
シグモイド関数
1 2 3 |
# シグモイド関数(ロジスティック関数) def sigmoid(x): return 1/(1 + np.exp(-x)) |
ReLu関数
1 2 3 |
# ReLU関数 def relu(x): return np.maximum(0, x) |
Section3 出力層
平均二乗誤差
1 2 3 |
# 平均二乗誤差 def mean_squared_error(d, y): return np.mean(np.square(d - y)) / 2 |
クロスエントロピー誤差
1 2 3 4 5 6 7 8 9 10 11 12 |
# クロスエントロピー def cross_entropy_error(d, y): if y.ndim == 1: d = d.reshape(1, d.size) y = y.reshape(1, y.size) # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換 if d.size == y.size: d = d.argmax(axis=1) batch_size = y.shape[0] return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size |
多クラス分類
- 2-3-4ネットワークから3-5-6ネットワークへ変更
- 入力値、パラメータをinput_layer_size = 3,hidden_layer_size= 5,output_layer_size = 6として各層のサイズを設定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# 多クラス分類 # 2-3-4ネットワーク # !試してみよう_ノードの構成を 3-5-6 に変更してみよう # ウェイトとバイアスを設定 # ネートワークを作成 def init_network(): print("##### ネットワークの初期化 #####") #試してみよう #_各パラメータのshapeを表示 #_ネットワークの初期値ランダム生成 network = {} input_layer_size = 3 hidden_layer_size= 5 output_layer_size = 6 #試してみよう #_各パラメータのshapeを表示 #_ネットワークの初期値ランダム生成 network['W1'] = np.random.rand(input_layer_size, hidden_layer_size) network['W2'] = np.random.rand(hidden_layer_size,output_layer_size) network['b1'] = np.random.rand(hidden_layer_size) network['b2'] = np.random.rand(output_layer_size) print_vec("重み1", network['W1'] ) print_vec("重み2", network['W2'] ) print_vec("バイアス1", network['b1'] ) print_vec("バイアス2", network['b2'] ) return network # プロセスを作成 # x:入力値 def forward(network, x): print("##### 順伝播開始 #####") W1, W2 = network['W1'], network['W2'] b1, b2 = network['b1'], network['b2'] # 1層の総入力 u1 = np.dot(x, W1) + b1 # 1層の総出力 z1 = functions.relu(u1) # 2層の総入力 u2 = np.dot(z1, W2) + b2 # 出力値 y = functions.softmax(u2) print_vec("総入力1", u1) print_vec("中間層出力1", z1) print_vec("総入力2", u2) print_vec("出力1", y) print("出力合計: " + str(np.sum(y))) return y, z1 ## 事前データ # 入力値 x = np.array([1., 2., 3.]) # 目標出力 d = np.array([0, 0, 0, 1, 0, 0]) # ネットワークの初期化 network = init_network() # 出力 y, z1 = forward(network, x) # 誤差 loss = functions.cross_entropy_error(d, y) ## 表示 print("\n##### 結果表示 #####") print_vec("出力", y) print_vec("訓練データ", d) print_vec("交差エントロピー誤差", loss) |
ネットワークの初期化 #####
*** 重み1 *** [[0.7157339 0.47869371 0.6895278 0.90320904 0.72296846] [0.0101953 0.80433874 0.42552239 0.56963189 0.75262407] [0.81782667 0.04413062 0.11799351 0.10600158 0.50511239]] shape: (3, 5)
*** 重み2 *** [[0.78080966 0.36026241 0.66807195 0.36102792 0.45743724 0.33353327] [0.10056151 0.64494681 0.93788282 0.8285677 0.12998106 0.59939957] [0.67809687 0.72251967 0.34648522 0.13991157 0.74636086 0.50303284] [0.56567254 0.8172722 0.65523419 0.28281295 0.00292972 0.85119732] [0.90057742 0.58095183 0.72126442 0.23898039 0.48826979 0.59552556]] shape: (5, 6)
*** バイアス1 *** [0.52058935 0.64584221 0.68683109 0.7211139 0.67130925] shape: (5,)
*** バイアス2 *** [0.03162837 0.31907359 0.45987903 0.61817551 0.381086 0.72657623] shape: (6,)
##### 順伝播開始 #####
*** 総入力1 *** [3.71019387 2.86560525 2.5813842 3.08159144 4.41486301] shape: (5,)
*** 中間層出力1 *** [3.71019387 2.86560525 2.5813842 3.08159144 4.41486301] shape: (5,)
*** 総入力2 *** [10.6862793 10.45230259 11.7240166 6.61975222 6.54205781 10.23241903] shape: (6,)
*** 出力1 *** [0.18930908 0.14981546 0.53438594 0.00324415 0.00300164 0.12024373] shape: (6,) 出力合計: 1.0
##### 結果表示 #####
*** 出力 *** [0.18930908 0.14981546 0.53438594 0.00324415 0.00300164 0.12024373] shape: (6,)
*** 訓練データ *** [0 0 0 1 0 0] shape: (6,)
*** 交差エントロピー誤差 *** 5.730870534218311 shape: ()
Section4 勾配降下法
確率的勾配降下法
- 活性化関数にReLu関数を使用
- 後のパラメータはサンプル通り
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# サンプルとする関数 #yの値を予想するAI def f(x): y = 3 * x[0] + 2 * x[1] return y # 初期設定 def init_network(): # print("##### ネットワークの初期化 #####") network = {} nodesNum = 10 network['W1'] = np.random.randn(2, nodesNum) network['W2'] = np.random.randn(nodesNum) network['b1'] = np.random.randn(nodesNum) network['b2'] = np.random.randn() # print_vec("重み1", network['W1']) # print_vec("重み2", network['W2']) # print_vec("バイアス1", network['b1']) # print_vec("バイアス2", network['b2']) return network # 順伝播 def forward(network, x): # print("##### 順伝播開始 #####") W1, W2 = network['W1'], network['W2'] b1, b2 = network['b1'], network['b2'] u1 = np.dot(x, W1) + b1 z1 = functions.relu(u1) ## 試してみよう #z1 = functions.sigmoid(u1) u2 = np.dot(z1, W2) + b2 y = u2 # print_vec("総入力1", u1) # print_vec("中間層出力1", z1) # print_vec("総入力2", u2) # print_vec("出力1", y) # print("出力合計: " + str(np.sum(y))) return z1, y # 誤差逆伝播 def backward(x, d, z1, y): # print("\n##### 誤差逆伝播開始 #####") grad = {} W1, W2 = network['W1'], network['W2'] b1, b2 = network['b1'], network['b2'] # 出力層でのデルタ delta2 = functions.d_mean_squared_error(d, y) # b2の勾配 grad['b2'] = np.sum(delta2, axis=0) # W2の勾配 grad['W2'] = np.dot(z1.T, delta2) # 中間層でのデルタ #delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1) ## 試してみよう delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1) delta1 = delta1[np.newaxis, :] # b1の勾配 grad['b1'] = np.sum(delta1, axis=0) x = x[np.newaxis, :] # W1の勾配 grad['W1'] = np.dot(x.T, delta1) # print_vec("偏微分_重み1", grad["W1"]) # print_vec("偏微分_重み2", grad["W2"]) # print_vec("偏微分_バイアス1", grad["b1"]) # print_vec("偏微分_バイアス2", grad["b2"]) return grad # サンプルデータを作成 data_sets_size = 100000 data_sets = [0 for i in range(data_sets_size)] for i in range(data_sets_size): data_sets[i] = {} # ランダムな値を設定 data_sets[i]['x'] = np.random.rand(2) ## 試してみよう_入力値の設定 # data_sets[i]['x'] = np.random.rand(2) * 10 -5 # -5〜5のランダム数値 # 目標出力を設定 data_sets[i]['d'] = f(data_sets[i]['x']) losses = [] # 学習率 learning_rate = 0.07 # 抽出数 epoch = 1000 # パラメータの初期化 network = init_network() # データのランダム抽出 random_datasets = np.random.choice(data_sets, epoch) # 勾配降下の繰り返し for dataset in random_datasets: x, d = dataset['x'], dataset['d'] z1, y = forward(network, x) grad = backward(x, d, z1, y) # パラメータに勾配適用 for key in ('W1', 'W2', 'b1', 'b2'): network[key] -= learning_rate * grad[key] # 誤差 loss = functions.mean_squared_error(d, y) losses.append(loss) print("##### 結果表示 #####") lists = range(epoch) plt.plot(lists, losses, '.') # グラフの表示 plt.show() |
出力結果
学習を重ねるごとに誤差が小さくなっていることが分かった。
Section5 誤差逆伝播法
確率的勾配降下法 その2
- 順伝播の活性化関数をシグモイド関数にしてみる
- その他は勾配降下法のときと同じ
変更点のみ掲載
1 2 3 4 5 |
# コメントアウト #z1 = functions.relu(u1) ## 試してみよう z1 = functions.sigmoid(u1) |
結果
シグモイド関数にすると誤差の減少がやや遅いことが分かった。
コメント