今まで実装していなかった活性化関数について実装してみます。MaxOutおよびLeRU関連について確認します。
MaxOut
MaxOutは、以下の式で表せます。
通常のaffine変換をn個用意し、そのうち最大の値を採用します。
\begin{align}
u_{ik}^{(l)} &= \sum_{j=1}^m(w_{ijk}^{(l)}z_{j}^{(l-1)}) + b_{ik}^{(l)}\\
z_i^{(l)} &= max(u_{ik}^{(l)})
\end{align}
例えば、ノード数が3個、nが5個の場合は、以下のようになります。
\begin{align}
u_{11}^{(l)} &= w_{111}^{(l)}z_{1}^{(l-1)} + w_{121}^{(l)}z_{2}^{(l-1)} + w_{131}^{(l)}z_{3}^{(l-1)} + b_{11}^{(l)}\\
u_{12}^{(l)} &= w_{112}^{(l)}z_{1}^{(l-1)} + w_{122}^{(l)}z_{2}^{(l-1)} + w_{132}^{(l)}z_{3}^{(l-1)} + b_{12}^{(l)}\\
u_{13}^{(l)} &= w_{113}^{(l)}z_{1}^{(l-1)} + w_{123}^{(l)}z_{2}^{(l-1)} + w_{133}^{(l)}z_{3}^{(l-1)} + b_{13}^{(l)}\\
u_{14}^{(l)} &= w_{114}^{(l)}z_{1}^{(l-1)} + w_{124}^{(l)}z_{2}^{(l-1)} + w_{134}^{(l)}z_{3}^{(l-1)} + b_{14}^{(l)}\\
u_{15}^{(l)} &= w_{115}^{(l)}z_{1}^{(l-1)} + w_{125}^{(l)}z_{2}^{(l-1)} + w_{135}^{(l)}z_{3}^{(l-1)} + b_{15}^{(l)}
\end{align}
このうち、$u_{11}^{(l)}$~$u_{15}^{(l)}$の最大値が、$z_1^{(l)}$の値となります。
実装
通常のaffine変換をn回実行するようにします。1度で実行できるように、重みをn個分用意します。
回数は、活性化関数のパラメータとして、unitを受け渡します。
重みの初期化
unitが指定されている場合、重みをunit分用意します。
# 重み、バイアスの初期化
W = {}
b = {}
unit = 1
if "unit" in middle_params:
unit = middle_params["unit"]
for i in range(layer-1):
W[i+1] = weight_init_func(d[i], d[i+1]*unit, weight_init_params)
for i in range(layer-1):
b[i+1] = bias_init_func(d[i+1]*unit, bias_init_params)
W[layer] = weight_init_func(d[layer-1], d[layer], weight_init_params)
b[layer] = bias_init_func(d[layer], bias_init_params)
順伝播
unit数分のaffine変換は、affine関数で行っているため、最大の値を求めるだけです。そのために、reshapeを行っています。
def maxout(u, params):
u = u.reshape((u.shape[0],int(u.shape[1]/params["unit"]),params["unit"]))
return np.max(u, axis=2)
逆伝播
最大として選択された項目だけそのままの値を返します。最大以外は、値を利用されなかったため、0を返します。
一度に実行する方法が分からなかったため、地道にfor分でループしひとつづつ実行しました。
def maxout_back(dz, u, z, params):
u = u.reshape((u.shape[0],int(u.shape[1]/params["unit"]),params["unit"]))
u_max = np.argmax(u,axis=2)
du = np.zeros_like(u)
for i in range(du.shape[0]):
for j in range(du.shape[1]):
du[i,j,u_max[i,j]] = dz[i,j]
return du.reshape((du.shape[0],int(du.shape[1]*params["unit"])))
結果
今まで通り、中間層が[100,50]ノードで試してみます。unit数は、2~5とします。参考のため、ReLUの場合も載せます。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
units = [2,3,4,5]
for unit in units:
name = "maxout,unit=" + str(unit)
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, middle_func=maxout, middle_params={"unit":unit})
unit | 学習正解 | テスト正解 | テスト最高 | エポック | 時間 |
---|---|---|---|---|---|
ReLU | 100.00 | 97.85 | 97.88 | 34 | 3分44秒 |
2 | 100.00 | 98.01 | 98.11 | 28 | 13分39秒 |
3 | 100.00 | 98.01 | 98.03 | 21 | 17分23秒 |
4 | 100.00 | 97.87 | 97.94 | 24 | 19分38秒 |
5 | 100.00 | 98.28 | 98.28 | 24 | 22分57秒 |
グラフにしてみます。
unit数が3,4の場合は、若干悪くなりましたが、基本的にはノード数を増やすと良くなるようです。
重みを多く利用しているため、同じ数だけ重みを利用したReLUの場合を示します。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
mds = [[100,50],[200,100],[300,150],[400,200],[500,250]]
for md in mds:
name = "maxout,md=" + str(md)
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, md=md)
ノード数 | 学習正解 | テスト正解 | テスト最高 | エポック | 時間 |
---|---|---|---|---|---|
[100, 50] | 100.00 | 97.85 | 97.88 | 34 | 3分44秒 |
[200, 100] | 100.00 | 98.01 | 98.08 | 40 | 7分 4秒 |
[300, 150] | 100.00 | 98.21 | 98.25 | 21 | 9分58秒 |
[400, 200] | 100.00 | 98.17 | 98.19 | 35 | 13分26秒 |
[500, 250] | 100.00 | 98.13 | 98.20 | 49 | 17分28秒 |
ドロップアウトと組み合わせると良いということです。ドロップアウトの結果です。
縦が入力層のドロップアウト率、横が中間層のドロップアウト率です。
- unit=2
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
units = [2]
dropout_middle_ratio = [0.5,0.6,0.7,0.8,0.9,1.0]
dropout_input_ratio = [1.0,0.9,0.8,0.7,0.6,0.5]
for unit in units:
for input_ratio in dropout_input_ratio:
for middle_ratio in dropout_middle_ratio:
name = "maxout,unit=" + str(unit) + ",dropout_input_ratio=" + str(input_ratio) + ",dropout_middle_ratio=" + str(middle_ratio)
regularization_params = {"dropout_input_ratio":input_ratio, "dropout_middle_ratio":middle_ratio}
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, middle_func=maxout, middle_params={"unit":unit}, regularization_params=regularization_params)
1.0 | 0.9 | 0.8 | 0.7 | 0.6 | 0.5 | |
---|---|---|---|---|---|---|
1.0 | 98.12 | 98.30 | 98.31 | 98.34 | 98.35 | 98.18 |
0.9 | 98.25 | 98.53 | 98.57 | 98.44 | 98.27 | 98.15 |
0.8 | 98.51 | 98.58 | 98.42 | 98.24 | 98.13 | 98.09 |
0.7 | 98.44 | 98.45 | 98.42 | 98.31 | 98.10 | 97.88 |
0.6 | 98.52 | 98.41 | 98.34 | 98.11 | 98.05 | 97.76 |
0.5 | 98.35 | 98.23 | 98.04 | 97.96 | 97.69 | 97.46 |
参考までに、同じ重みの数の中間層が[200,100]ノードの場合のReLUの結果です。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
mds = [[200, 100]]
dropout_middle_ratio = [0.5,0.6,0.7,0.8,0.9,1.0]
dropout_input_ratio = [1.0,0.9,0.8,0.7,0.6,0.5]
for md in mds:
for input_ratio in dropout_input_ratio:
for middle_ratio in dropout_middle_ratio:
name = "md=" + str(md) + ",dropout_input_ratio=" + str(input_ratio) + ",dropout_middle_ratio=" + str(middle_ratio)
regularization_params = {"dropout_input_ratio":input_ratio, "dropout_middle_ratio":middle_ratio}
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, md=md, regularization_params=regularization_params)
1.0 | 0.9 | 0.8 | 0.7 | 0.6 | 0.5 | |
---|---|---|---|---|---|---|
1.0 | 98.10 | 98.25 | 98.34 | 98.26 | 98.07 | 97.90 |
0.9 | 98.41 | 98.39 | 98.36 | 98.27 | 98.17 | 97.92 |
0.8 | 98.55 | 98.46 | 98.38 | 98.36 | 98.04 | 97.84 |
0.7 | 98.54 | 98.49 | 98.32 | 98.26 | 98.00 | 97.69 |
0.6 | 98.49 | 98.46 | 98.25 | 98.24 | 97.81 | 97.40 |
0.5 | 98.37 | 98.25 | 98.06 | 97.82 | 97.46 | 97.03 |
- unit=5
1.0 | 0.9 | 0.8 | 0.7 | 0.6 | 0.5 | |
---|---|---|---|---|---|---|
1.0 | 98.14 | 98.43 | 98.52 | 98.53 | 98.48 | 98.43 |
0.9 | 98.51 | 98.60 | 98.55 | 98.58 | 98.55 | 98.51 |
0.8 | 98.57 | 98.65 | 98.60 | 98.58 | 98.50 | 98.40 |
0.7 | 98.65 | 98.61 | 98.53 | 98.57 | 98.30 | 98.35 |
0.6 | 98.46 | 98.53 | 98.46 | 98.38 | 98.19 | 98.15 |
0.5 | 98.42 | 98.42 | 98.29 | 98.11 | 97.82 | 97.69 |
参考までに、同じ重みの数の中間層が[500,250]ノードの場合のReLUの結果です。
1.0 | 0.9 | 0.8 | 0.7 | 0.6 | 0.5 | |
---|---|---|---|---|---|---|
1.0 | 98.17 | 98.52 | 98.49 | 98.50 | 98.39 | 98.30 |
0.9 | 98.52 | 98.56 | 98.58 | 98.59 | 98.38 | 98.25 |
0.8 | 98.63 | 98.72 | 98.61 | 98.51 | 98.38 | 98.16 |
0.7 | 98.65 | 98.66 | 98.57 | 98.44 | 98.23 | 98.09 |
0.6 | 98.58 | 98.55 | 98.53 | 98.30 | 98.07 | 97.89 |
0.5 | 98.55 | 98.39 | 98.32 | 98.07 | 97.83 | 97.59 |
ドロップアウトを適用すると良い結果となりました。
ReLU関連
ReLU,Leaky ReLUを試しましたが、他のReLU関連についても試してみます。
PReLU(Parametrized ReLU)
Leaky ReLUの勾配を学習により決定しようというものです。
以下の$\alpha$を学習により決定します。
f(x_i) = \left\{
\begin{array}{ll}
x_i & (x_i \gt 0) \\
\alpha_i x_i & (x_i \leq 0)
\end{array}
\right.
実装
$\alpha$を学習するため実装は複雑になります。
初期化
学習する$\alpha$の初期値を設定します。各層のノード数分用意し、初期値を0とします。
# PReLU初期化
if middle_func == prelu:
optimizer_stats_alpha = {}
middle_params_alpha = {}
for i in range(1, layer):
middle_params_alpha[i] = np.zeros(d[i])
optimizer_stats_alpha[i] = {}
middle_params["alphas"] = middle_params_alpha
順伝播
PReLU自体は、基本的にLeaky ReLUと同じです。Leaky ReLUの$\alpha$はひとつですが、PReLUはノード数分です。
def prelu(u, params):
return np.where(u > 0, u, u * params["alpha"] )
各層ごとに$\alpha$を渡すため順伝播の関数を変更します。
# 順伝播
def propagation(layer, x, W, b, middle_func, middle_params, output_func, output_params, regularization_params, batch_norm_params, dropout_mask={}):
u = {}
un = {}
z = {}
# 入力層
#z[0] = x
z[0] = calc_input(x, regularization_params, dropout_mask.get(0))
# 中間層
for i in range(1, layer):
u[i] = calc_affine(z[i-1], W[i], b[i])
# PReLUのパラメータ設定
if middle_func == prelu:
middle_params["alpha"] = middle_params["alphas"][i]
un[i], z[i] = calc_middle(u[i], middle_func, middle_params, regularization_params, batch_norm_params["batch_norm_gamma"][i], batch_norm_params["batch_norm_beta"][i], dropout_mask.get(i))
# 出力層
u[layer] = calc_affine(z[layer-1], W[layer], b[layer])
y = output_func(u[layer], output_params)
return u, un, z, y
逆伝播
逆伝播もLeaky ReLUと同じです。Leaky ReLUの$\alpha$はひとつですが、PReLUはノード数分です。
def prelu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, params["alpha"])
各層ごとに$\alpha$を渡すため逆伝播の関数を変更します。
# 逆伝播
def back_propagation(layer, u, un, z, y, t, W, b, middle_back_func, middle_params, output_error_back_func, regularization_params, batch_norm_params, dropout_mask={}):
du = {}
dz = {}
dW = {}
db = {}
batch_norm_dparams = {}
batch_norm_dparams["batch_norm_dgamma"] = {}
batch_norm_dparams["batch_norm_dbeta"] = {}
# 出力層
du[layer] = output_error_back_func(y, u[layer], t)
dz[layer-1], dW[layer], db[layer] = calc_affine_back(du[layer], z[layer-1], W[layer], b[layer], regularization_params)
# 中間層
for i in range(layer-1, 0, -1):
# PReLUのパラメータ設定
if middle_back_func == prelu_back:
middle_params["alpha"] = middle_params["alphas"][i]
du[i], batch_norm_dparams["batch_norm_dgamma"][i], batch_norm_dparams["batch_norm_dbeta"][i] = calc_middle_back(dz[i], u[i], un[i], z[i], middle_back_func, middle_params, regularization_params, batch_norm_params["batch_norm_gamma"][i], batch_norm_params["batch_norm_beta"][i], dropout_mask.get(i))
dz[i-1], dW[i], db[i] = calc_affine_back(du[i], z[i-1], W[i], b[i], regularization_params)
return du, dz, dW, db, batch_norm_dparams
調整
$\alpha$を調整するため、$\alpha$の勾配を求めます。
def prelu_alpha_back(dz, u, z, params):
return np.sum(dz * np.where(u > 0, 0, u), axis=0) / u.shape[0]
$\alpha$の調整を行います。
# PReLUの調整
if middle_func == prelu:
for k in range(1, layer):
dalpha = prelu_alpha_back(dz[k], un[k], z[k], {"alpha":middle_params["alphas"][k]})
middle_params["alphas"][k], optimizer_stats_alpha[k] = optimizer_func(middle_params["alphas"][k], dalpha, eta, optimizer_params, optimizer_stats_alpha[k])
結果
ReLUより少しだけよくなりました。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
name = "prelu"
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name],middle_params = learn(
name, x_train, t_train, x_test, t_test, middle_func=prelu)
学習正解 | テスト正解 | テスト最高 | エポック | |
---|---|---|---|---|
PReLU | 100.00 | 97.87 | 97.93 | 34 |
ReLU | 100.00 | 97.85 | 97.88 | 34 |
グラフにします。
50エポック後の$\alpha$の値の分布をヒストグラムにしてみましょう。$\alpha$を返却するように変更し実行しています。
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(10,3))
# 重みのヒストグラム
for i in range(1,3):
alphas = middle_params["alphas"][i]
plt.subplot(1, 2, i)
plt.title("alpha[" + str(i) + "]")
plt.hist(alphas,bins=50)
plt.xlim(-0.02,0.01)
plt.show()
大部分の$\alpha$の値が負になっています。正になると考えていましたが以外です。何か実装を間違えたか?
RReLU(Randomized Leaky ReLU)
Leaky ReLUの勾配を乱数により決定しようというものです。
以下の$\alpha$を乱数により決定します。
f(x_i) = \left\{
\begin{array}{ll}
x_i & (x_i \gt 0) \\
\alpha_i x_i & (x_i \leq 0)
\end{array}
\right.
ただし、$\alpha_i$は、minからmaxまでの乱数
実装
パラメータとして、発生させる乱数の最小値と最大値をmiddle_params["min"]、middle_params["max"]で指定します。
初期化
$\alpha$として、min、max間の乱数を設定します。
# RReLUの初期化
if middle_func == rrelu:
middle_params_alpha = {}
for i in range(1, layer):
middle_params_alpha[i] = np.random.uniform(middle_params["min"], middle_params["max"], d[i])
middle_params["alphas"] = middle_params_alpha
順伝播
RReLU自体は、基本的にPReLUと同じです。
def rrelu(u, params):
return np.where(u > 0, u, u * params["alpha"] )
propagationは、PReLUと同様の対応を行います。
逆伝播
逆伝播もPReLUと同じです
def rrelu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, params["alpha"])
back_propagationは、PReLUと同様の対応を行います。
結果
乱数の範囲を-0.1~0.1、0.0~0.1、-0.2~0.2、0.0~0.2で試してみます。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
# -0.1~0.1
name = "rrelu -0.1 - 0.1"
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, middle_func=rrelu, middle_params={"min":-0.1,"max":0.1})
参考までに、ReLU、Leaky ReLUの場合も以下に示します。
学習正解 | テスト正解 | テスト最高 | エポック | |
---|---|---|---|---|
RReLU -0.1 - 0.1 | 100.00 | 98.09 | 98.10 | 48 |
RReLU -0.2 - 0.2 | 100.00 | 97.95 | 97.97 | 45 |
RReLU 0.0 - 0.1 | 100.00 | 98.04 | 98.06 | 45 |
RReLU 0.0 - 0.2 | 100.00 | 98.00 | 98.02 | 27 |
ReLU | 100.00 | 97.85 | 97.88 | 34 |
Leaky ReLU 0.1 | 100.00 | 97.91 | 97.98 | 33 |
Leaky ReLU 0.2 | 100.00 | 97.94 | 97.97 | 45 |
Leaky ReLU -0.1 | 100.00 | 97.95 | 98.03 | 36 |
Leaky ReLU -0.2 | 100.00 | 98.06 | 98.11 | 34 |
なんと、Lealy ReRUは、負の場合が良くなった。PReLUで学習後に負になったのも間違いではないのかも。
RReLUは、少しだけ良い結果になりました。偶然なのかどうかはわかりません。
一定の値以上を定数とするReLU
ReLUで、$u$が一定以上の値となった場合、その値を上限とします。
f(x_i) = \left\{
\begin{array}{ll}
n & (x_i \gt n) \\
x_i & (n \ge x_i \gt 0) \\
0 & (x_i \leq 0)
\end{array}
\right.
n=5の場合のグラフです。
実装
一定の値以上を定数とするReLUをrelunとしています。$n$はパラメータで渡します。
def relun(u, params):
return np.minimum(np.maximum(0, u), params["n"])
def relun_back(dz, u, z, params):
return dz * np.where(u > params["n"], 0, np.where(u > 0, 1, 0))
結果
$n$を10~1まで変更して試してみます。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
ns = [10,9,8,7,6,5,4,3,2,1]
for n in ns:
name = "relun n=" + str(n)
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, middle_func=relun, middle_params={"n":n})
n | 学習正解 | テスト正解 | テスト最高 | エポック |
---|---|---|---|---|
10 | 100.00 | 97.91 | 97.99 | 34 |
9 | 100.00 | 97.90 | 97.95 | 45 |
8 | 100.00 | 97.84 | 97.96 | 34 |
7 | 100.00 | 98.01 | 98.01 | 50 |
6 | 100.00 | 97.88 | 97.91 | 47 |
5 | 100.00 | 97.97 | 98.01 | 34 |
4 | 100.00 | 98.07 | 98.09 | 34 |
3 | 100.00 | 97.93 | 97.97 | 45 |
2 | 100.00 | 98.01 | 98.04 | 37 |
1 | 100.00 | 97.62 | 97.68 | 37 |
ReLU | 100.00 | 97.85 | 97.88 | 34 |
n=2以上だとそれなりの結果を得られました。
正解率をグラフにしてみます。
SReLU(Shifted ReLU)
ReLUは、0を境目に負の場合に0としますが、境目を負の方向にシフトします。
f(x_i) = \left\{
\begin{array}{ll}
x_i & (x_i \gt \alpha) \\
\alpha & (x_i \leq \alpha)
\end{array}
\right.
$\alpha$が-1の場合のグラフです。
実装
$\alpha$をパラメータとして渡すようにしています。
def srelu(u, params):
return np.where(u > params["alpha"], u, params["alpha"])
def srelu_back(dz, u, z, params):
return dz * np.where(u > params["alpha"], 1, 0)
結果
$\alpha$を-1.0~-0.1に変更して試してみます。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
alphas = [-1.0,-0.9,-0.8,-0.7,-0.6,-0.5,-0.4,-0.3,-0.2,-0.1]
for alpha in alphas:
name = "srelu alpha=" + str(alpha)
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, middle_func=srelu, middle_params={"alpha":alpha})
$\alpha$ | 学習正解 | テスト正解 | テスト最高 | エポック |
---|---|---|---|---|
-1.0 | 100.00 | 97.66 | 97.72 | 42 |
-0.9 | 100.00 | 97.79 | 97.87 | 38 |
-0.8 | 100.00 | 97.90 | 97.91 | 35 |
-0.7 | 100.00 | 97.85 | 97.93 | 19 |
-0.6 | 100.00 | 97.88 | 97.99 | 33 |
-0.5 | 100.00 | 98.03 | 98.05 | 41 |
-0.4 | 100.00 | 97.90 | 97.90 | 33 |
-0.3 | 100.00 | 98.06 | 98.06 | 37 |
-0.2 | 100.00 | 97.91 | 98.02 | 29 |
-0.1 | 100.00 | 97.91 | 97.96 | 29 |
ELU
SReLUを滑らかにしたものとなります。
f(x_i) = \left\{
\begin{array}{ll}
x_i & (x_i \gt 0) \\
\alpha(\exp(x_i)-1) & (x_i \leq 0)
\end{array}
\right.
$\alpha$を1とした場合のグラフです。
実装
$\alpha$をパラメータとして渡すようにしています。
def elu(u, params):
return np.where(u > 0, u, params["alpha"] * (np.exp(u) - 1))
def elu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, params["alpha"] * np.exp(u))
結果
$\alpha$を-1.0~-0.1に変更して試してみます。
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')
train_rates = {}
train_errs = {}
test_rates = {}
test_errs = {}
total_times = {}
alphas = [1.0,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1]
for alpha in alphas:
name = "elu alpha=" + str(alpha)
_, _, _, _, train_rates[name], train_errs[name], test_rates[name], test_errs[name], total_times[name] = learn(
name, x_train, t_train, x_test, t_test, middle_func=elu, middle_params={"alpha":alpha})
$\alpha$ | 学習正解 | テスト正解 | テスト最高 | エポック |
---|---|---|---|---|
1.0 | 100.00 | 97.92 | 98.01 | 42 |
0.9 | 100.00 | 97.88 | 98.02 | 45 |
0.8 | 100.00 | 97.90 | 98.01 | 48 |
0.7 | 100.00 | 97.83 | 97.95 | 27 |
0.6 | 100.00 | 97.93 | 97.93 | 50 |
0.5 | 100.00 | 97.96 | 97.99 | 48 |
0.4 | 100.00 | 98.01 | 98.01 | 50 |
0.3 | 100.00 | 97.84 | 97.90 | 22 |
0.2 | 100.00 | 97.87 | 97.93 | 34 |
0.1 | 100.00 | 97.84 | 97.88 | 34 |
ReLU関連をいろいろと試してみました。
ReLUより良い結果となる場合もありましたが、確実にこれがよいという活性関数はわかりませんでした。何れにせよ試してみるしかなさそうです。
MaxOutは、ノートPCのスペック上、5ユニットまでしか試しませんでした。ユニット数を増やせばもっと良くなる可能性はあります。
参考
プログラム全体です。
import gzip
import numpy as np
# MNIST読み込み
def load_mnist( mnist_path ) :
return _load_image(mnist_path + 'train-images-idx3-ubyte.gz'), \
_load_label(mnist_path + 'train-labels-idx1-ubyte.gz'), \
_load_image(mnist_path + 't10k-images-idx3-ubyte.gz'), \
_load_label(mnist_path + 't10k-labels-idx1-ubyte.gz')
def _load_image( image_path ) :
# 画像データの読み込み
with gzip.open(image_path, 'rb') as f:
buffer = f.read()
size = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=4)
rows = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=8)
columns = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=12)
data = np.frombuffer(buffer, np.uint8, offset=16)
image = np.reshape(data, (size[0], rows[0]*columns[0]))
image = image.astype(np.float32)
return image
def _load_label( label_path ) :
# 正解データ読み込み
with gzip.open(label_path, 'rb') as f:
buffer = f.read()
size = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=4)
data = np.frombuffer(buffer, np.uint8, offset=8)
label = np.zeros((size[0], 10))
for i in range(size[0]):
label[i, data[i]] = 1
return label
# 正規化関数
def min_max(x, stats, params):
axis=params.get("axis")
if "min" not in stats:
stats["min"] = np.min(x, axis=axis, keepdims=True) # 最小値を求める
if "max" not in stats:
stats["max"] = np.max(x, axis=axis, keepdims=True) # 最大値を求める
return (x-stats["min"])/np.maximum((stats["max"]-stats["min"]),1e-7), stats
def z_score(x, stats, params):
axis=params.get("axis")
if "mean" not in stats:
stats["mean"] = np.mean(x, axis=axis, keepdims=True) # 平均値を求める
if "std" not in stats:
stats["std"] = np.std(x, axis=axis, keepdims=True) # 標準偏差を求める
return (x-stats["mean"])/np.maximum(stats["std"],1e-7), stats
# affine変換
def affine(z, W, b):
return np.dot(z, W) + b
def affine_back(du, z, W, b):
dz = np.dot(du, W.T) # zの勾配は、今までの勾配と重みを掛けた値
dW = np.dot(z.T, du) # 重みの勾配は、zに今までの勾配を掛けた値
size = 1
if z.ndim == 2:
size = z.shape[0]
db = np.dot(np.ones(size).T, du) # バイアスの勾配は、今までの勾配の値
return dz, dW, db
# 活性化関数(中間)
def sigmoid(u, params):
return 1/(1 + np.exp(-u))
def sigmoid_back(dz, u, z, params):
return dz * (z - z**2)
def tanh(u, params):
return np.tanh(u)
def tanh_back(dz, u, z, params):
return dz * (1/np.cosh(u)**2)
def relu(u, params):
return np.maximum(0, u)
def relu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, 0)
def leaky_relu(u, params):
return np.maximum(u * params["alpha"], u)
def leaky_relu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, params["alpha"])
def prelu(u, params):
return np.where(u > 0, u, u * params["alpha"] )
def prelu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, params["alpha"])
def prelu_alpha_back(dz, u, z, params):
return np.sum(dz * np.where(u > 0, 0, u), axis=0) / u.shape[0]
def rrelu(u, params):
return np.where(u > 0, u, u * params["alpha"] )
def rrelu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, params["alpha"])
def relun(u, params):
return np.minimum(np.maximum(0, u), params["n"])
def relun_back(dz, u, z, params):
return dz * np.where(u > params["n"], 0, np.where(u > 0, 1, 0))
def srelu(u, params):
return np.where(u > params["alpha"], u, params["alpha"])
def srelu_back(dz, u, z, params):
return dz * np.where(u > params["alpha"], 1, 0)
def elu(u, params):
return np.where(u > 0, u, params["alpha"] * (np.exp(u) - 1))
def elu_back(dz, u, z, params):
return dz * np.where(u > 0, 1, params["alpha"] * np.exp(u))
def maxout(u, params):
u = u.reshape((u.shape[0],int(u.shape[1]/params["unit"]),params["unit"]))
return np.max(u, axis=2)
def maxout_back(dz, u, z, params):
u = u.reshape((u.shape[0],int(u.shape[1]/params["unit"]),params["unit"]))
u_max = np.argmax(u,axis=2)
du = np.zeros_like(u)
for i in range(du.shape[0]):
for j in range(du.shape[1]):
du[i,j,u_max[i,j]] = dz[i,j]
return du.reshape((du.shape[0],int(du.shape[1]*params["unit"])))
def identity(u, params):
return u
def identity_back(dz, u, z, params):
return dz
def softplus(u, params):
return np.log(1+np.exp(u))
def softplus_back(dz, u, z, params):
return dz * (1/(1 + np.exp(-u)))
def softsign(u, params):
return u/(1+np.absolute(u))
def softsign_back(dz, u, z, params):
return dz * (1/(1+np.absolute(u))**2)
def step(u, params):
return np.where(u > 0, 1, 0)
def step_back(dz, u, x, params):
return 0
# 活性化関数(出力)
def softmax(u, params):
u = u.T
max_u = np.max(u, axis=0)
exp_u = np.exp(u - max_u)
sum_exp_u = np.sum(exp_u, axis=0)
y = exp_u/sum_exp_u
return y.T
# 損失関数
def mean_squared_error(y, t):
size = 1
if y.ndim == 2:
size = y.shape[0]
return 0.5 * np.sum((y-t)**2)/size
def cross_entropy_error(y, t):
size = 1
if y.ndim == 2:
size = y.shape[0]
return -np.sum(t * np.log(np.maximum(y,1e-7)))/size
#return -np.sum(t * np.log(y))/size
# 活性化関数(出力)+損失関数勾配
def softmax_cross_entropy_error_back(y, u, t):
size = 1
if y.ndim == 2:
size = y.shape[0]
return (y - t)/size
def identity_mean_squared_error_back(y, u, t):
size = 1
if y.ndim == 2:
size = y.shape[0]
return (y - t)/size
# 重み・バイアスの初期化
def lecun_normal(d_1, d, params):
var = 1/np.sqrt(d_1)
return np.random.normal(0, var, (d_1, d))
def lecun_uniform(d_1, d, params):
min = -np.sqrt(3/d_1)
max = np.sqrt(3/d_1)
return np.random.uniform(min, max, (d_1, d))
def glorot_normal(d_1, d, params):
var = np.sqrt(2/(d_1+d))
return np.random.normal(0, var, (d_1, d))
def glorot_uniform(d_1, d, params):
min = -np.sqrt(6/(d_1+d))
max = np.sqrt(6/(d_1+d))
return np.random.uniform(min, max, (d_1, d))
def he_normal(d_1, d, params):
var = np.sqrt(2/d_1)
return np.random.normal(0, var, (d_1, d))
def he_uniform(d_1, d, params):
min = -np.sqrt(6/d_1)
max = np.sqrt(6/d_1)
return np.random.uniform(min, max, (d_1, d))
def normal_w(d_1, d, params):
mean=0
var=1
if "mean" in params:
mean = params["mean"]
if "var" in params:
var = params["var"]
return np.random.normal(mean, var, (d_1, d))
def normal_b(d, params):
mean=0
var=1
if "mean" in params:
mean = params["mean"]
if "var" in params:
var = params["var"]
return np.random.normal(mean, var, d)
def uniform_w(d_1, d, params):
min=0
max=1
if "min" in params:
min = params["min"]
if "max" in params:
max = params["max"]
return np.random.uniform(min, max, (d_1, d))
def uniform_b(d, params):
min=0
max=1
if "min" in params:
min = params["min"]
if "max" in params:
max = params["max"]
return np.random.uniform(min, max, d)
def zeros_w(d_1, d, params):
return np.zeros((d_1, d))
def zeros_b(d, params):
return np.zeros(d)
def ones_w(d_1, d, params):
return np.ones((d_1, d))
def ones_b(d, params):
return np.ones(d)
# 勾配法
def SGD(W, dW, eta, optimizer_params, optimizer_stats):
return W - eta*dW, optimizer_stats
def Momentum(W, dW, eta, optimizer_params, optimizer_stats):
if "v" not in optimizer_stats: # vの初期値設定
optimizer_stats["v"] = np.zeros_like(W)
# vの更新
optimizer_stats["v"] = optimizer_params["mu"] * optimizer_stats["v"] - eta * dW
return W + optimizer_stats["v"], optimizer_stats
def Nesterov(W, dW, eta, optimizer_params, optimizer_stats):
if "v" not in optimizer_stats:
optimizer_stats["v"] = np.zeros_like(W)
optimizer_stats["v"] = optimizer_params["mu"] * optimizer_stats["v"] - eta * dW
return W + optimizer_stats["v"], optimizer_stats
def AdaGrad(W, dW, eta, optimizer_params, optimizer_stats):
epsilon = 1e-7
if "epsilon" in optimizer_params:
epsilon = optimizer_params["epsilon"]
if "g" not in optimizer_stats:
optimizer_stats["g"] = np.zeros_like(W)
optimizer_stats["g"] = optimizer_stats["g"] + (dW * dW)
return W - (eta * dW)/np.sqrt(np.maximum(optimizer_stats["g"], epsilon)), optimizer_stats
def RMSProp(W, dW, eta, optimizer_params, optimizer_stats):
epsilon = 1e-7
if "epsilon" in optimizer_params:
epsilon = optimizer_params["epsilon"]
if "g" not in optimizer_stats:
optimizer_stats["g"] = np.zeros_like(W)
optimizer_stats["g"] = optimizer_params["gamma"] * optimizer_stats["g"] + (1 - optimizer_params["gamma"]) * dW * dW
return W - (eta * dW)/np.sqrt(np.maximum(optimizer_stats["g"], epsilon)), optimizer_stats
def Adadelta(W, dW, eta, optimizer_params, optimizer_stats):
epsilon = 1e-7
if "epsilon" in optimizer_params:
epsilon = optimizer_params["epsilon"]
if "g" not in optimizer_stats:
optimizer_stats["g"] = np.zeros_like(W)
if "s" not in optimizer_stats:
optimizer_stats["s"] = np.zeros_like(W)
optimizer_stats["g"] = optimizer_params["gamma"] * optimizer_stats["g"] + (1 - optimizer_params["gamma"]) * dW * dW
newW = W - (eta * dW)/np.sqrt(np.maximum(optimizer_stats["g"], epsilon)) * np.sqrt(np.maximum(optimizer_stats["s"], epsilon))
optimizer_stats["s"] = (1 - optimizer_params["gamma"]) * (newW - W) * (newW - W) + optimizer_params["gamma"] * optimizer_stats["s"]
return newW, optimizer_stats
def Adam(W, dW, eta, optimizer_params, optimizer_stats):
epsilon = 1e-7
if "epsilon" in optimizer_params:
epsilon = optimizer_params["epsilon"]
if "k" not in optimizer_stats:
optimizer_stats["k"] = 0
if "m" not in optimizer_stats:
optimizer_stats["m"] = np.zeros_like(W)
if "v" not in optimizer_stats:
optimizer_stats["v"] = np.zeros_like(W)
optimizer_stats["k"] = optimizer_stats["k"] + 1
optimizer_stats["m"] = optimizer_params["beta1"] * optimizer_stats["m"] + (1 - optimizer_params["beta1"]) * dW
optimizer_stats["v"] = optimizer_params["beta2"] * optimizer_stats["v"] + (1 - optimizer_params["beta2"]) * dW * dW
hatm = optimizer_stats["m"] / (1 - np.power(optimizer_params["beta1"], optimizer_stats["k"]))
hatv = optimizer_stats["v"] / (1 - np.power(optimizer_params["beta2"], optimizer_stats["k"]))
return W - eta / np.maximum(np.sqrt(hatv), epsilon) * hatm, optimizer_stats
# 重み減衰
def L1_norm(W, weight_decay_lambda):
r = 0.
for WI in W.values():
r = r + np.sum(np.absolute(WI))
return weight_decay_lambda * r
def L1_norm_back(W, weight_decay_lambda):
return np.where(W > 0, weight_decay_lambda, np.where(W < 0, -weight_decay_lambda, 0))
def L2_norm(W, weight_decay_lambda):
r = 0.
for WI in W.values():
r = r + np.sum(WI**2)
return (weight_decay_lambda * r)/2
def L2_norm_back(W, weight_decay_lambda):
return weight_decay_lambda * W
# ドロップアウト
def dropout(z, mask):
return z * mask
def dropout_back(dz, mask):
return dz * mask
# バッチ正規化
def batch_norm(x, gamma, beta, axis = 0):
x_mean = np.mean(x, axis=axis, keepdims=True) # 平均値を求める
x_std = np.maximum(np.std(x, axis=axis, keepdims=True),1e-7) # 標準偏差を求める
xh = (x-x_mean)/x_std # 正規化
return gamma * xh + beta
def batch_norm_back(dy, x, gamma, beta, axis = 0):
m = x.shape[axis] # バッチサイズ
x_mean = np.mean(x, axis=axis, keepdims=True) # 平均値を求める
x_std = np.maximum(np.std(x, axis=axis, keepdims=True),1e-7) # 標準偏差を求める
xh = (x-x_mean)/x_std # 正規化
gdy = dy * gamma
dy_sum = np.sum(gdy * (x - x_mean), axis=axis, keepdims=True)
dz = (gdy - (xh * dy_sum /(m * x_std)))/ x_std
dz_sum = np.sum(dz, axis=axis, keepdims=True)
dx = dz - (dz_sum / m)
dgamma = np.sum(xh * dy, axis=axis, keepdims=True)
dbeta = np.sum(dy, axis=axis, keepdims=True)
return dx, dgamma, dbeta
# 正解率
def accuracy_rate(y, t):
max_y = np.argmax(y, axis=1)
max_t = np.argmax(t, axis=1)
return np.sum(max_y == max_t)/y.shape[0]
import numpy as np
# ドロップアウト
def set_dropout_mask(d, regularization_params):
mask = {}
if "dropout_input_ratio" in regularization_params:
mask[0] = np.zeros(d[0])
ratio = regularization_params["dropout_input_ratio"]
mask_idx = np.random.choice(d[0], round(ratio*d[0]), replace=False) # 通すindexの配列
mask[0][mask_idx] = 1
if "dropout_middle_ratio" in regularization_params:
for i in range(1, len(d)-1):
mask[i] = np.zeros(d[i])
ratio = regularization_params["dropout_middle_ratio"]
mask_idx = np.random.choice(d[i], round(ratio*d[i]), replace=False) # 通すindexの配列
mask[i][mask_idx] = 1
return mask
# affin計算
def calc_affine(z, W, b):
return affine(z, W, b)
# affin勾配計算
def calc_affine_back(du, z, W, b, regularization_params):
dz, dW, db = affine_back(du, z, W, b)
if "weight_decay_func" in regularization_params: # 重み減衰対応
dW = dW + regularization_params["weight_decay_back_func"](W, regularization_params["weight_decay_lambda"])
return dz, dW, db
# 入力計算
def calc_input(x, regularization_params, dropout_mask):
# ドロップアウト対応
if "dropout_input_ratio" in regularization_params:
if dropout_mask is not None:
x = dropout(x, dropout_mask)
else:
x = x * regularization_params["dropout_input_ratio"]
return x
# 活性化関数計算
def calc_middle(u, middle_func, middle_params, regularization_params, batch_norm_gamma, batch_norm_beta, dropout_mask):
# バッチ正規化対応
un = u
if regularization_params.get("batch_norm"):
un = batch_norm(u, batch_norm_gamma, batch_norm_beta)
# 活性化関数
z = middle_func(un, middle_params)
# ドロップアウト対応
if "dropout_middle_ratio" in regularization_params:
if dropout_mask is not None:
z = dropout(z, dropout_mask)
else:
z = z * regularization_params["dropout_middle_ratio"]
return un, z
# 活性化関数勾配計算
def calc_middle_back(dz, u, un, z, middle_back_func, middle_params, regularization_params, batch_norm_gamma, batch_norm_beta, dropout_mask):
# ドロップアウト対応
if dropout_mask is not None:
dz = dropout_back(dz, dropout_mask)
# 活性化関数勾配
du = middle_back_func(dz, un, z, middle_params)
# バッチ正規化対応
batch_norm_dgamma = None
batch_norm_dbeta = None
if regularization_params.get("batch_norm"):
du, batch_norm_dgamma, batch_norm_dbeta = batch_norm_back(du, u, batch_norm_gamma, batch_norm_beta)
return du, batch_norm_dgamma, batch_norm_dbeta
# 誤差計算
def calc_error(y, t, W, error_func, regularization_params):
e = error_func(y, t)
if "weight_decay_func" in regularization_params: # 重み減衰対応
e = e + regularization_params["weight_decay_func"](W, regularization_params["weight_decay_lambda"])
return e
# 順伝播
def propagation(layer, x, W, b, middle_func, middle_params, output_func, output_params, regularization_params, batch_norm_params, dropout_mask={}):
u = {}
un = {}
z = {}
# 入力層
#z[0] = x
z[0] = calc_input(x, regularization_params, dropout_mask.get(0))
# 中間層
for i in range(1, layer):
u[i] = calc_affine(z[i-1], W[i], b[i])
# PReLU,RReLUのパラメータ設定
if middle_func == prelu or middle_func == rrelu:
middle_params["alpha"] = middle_params["alphas"][i]
un[i], z[i] = calc_middle(u[i], middle_func, middle_params, regularization_params, batch_norm_params["batch_norm_gamma"][i], batch_norm_params["batch_norm_beta"][i], dropout_mask.get(i))
# 出力層
u[layer] = calc_affine(z[layer-1], W[layer], b[layer])
y = output_func(u[layer], output_params)
return u, un, z, y
# 逆伝播
def back_propagation(layer, u, un, z, y, t, W, b, middle_back_func, middle_params, output_error_back_func, regularization_params, batch_norm_params, dropout_mask={}):
du = {}
dz = {}
dW = {}
db = {}
batch_norm_dparams = {}
batch_norm_dparams["batch_norm_dgamma"] = {}
batch_norm_dparams["batch_norm_dbeta"] = {}
# 出力層
du[layer] = output_error_back_func(y, u[layer], t)
dz[layer-1], dW[layer], db[layer] = calc_affine_back(du[layer], z[layer-1], W[layer], b[layer], regularization_params)
# 中間層
for i in range(layer-1, 0, -1):
# PReLU,RReLUのパラメータ設定
if middle_back_func == prelu_back or middle_back_func == rrelu_back:
middle_params["alpha"] = middle_params["alphas"][i]
du[i], batch_norm_dparams["batch_norm_dgamma"][i], batch_norm_dparams["batch_norm_dbeta"][i] = calc_middle_back(dz[i], u[i], un[i], z[i], middle_back_func, middle_params, regularization_params, batch_norm_params["batch_norm_gamma"][i], batch_norm_params["batch_norm_beta"][i], dropout_mask.get(i))
dz[i-1], dW[i], db[i] = calc_affine_back(du[i], z[i-1], W[i], b[i], regularization_params)
return du, dz, dW, db, batch_norm_dparams
学習パラメータ
パラメータ | 既定値 | パラメータ値 |
---|---|---|
中間層ノード数 | [100,50] | - |
重み初期化関数 | he_normal | lecun_normal,lecun_uniform, glorot_normal,glorot_uniform, he_normal,he_uniform, normal_w,uniform_w, zeros_w,ones_w |
重み初期化関数パラメータ | - | normal_wの場合、"mean"、"var" uniform_wの場合、"min"、"max" |
バイアス初期化関数 | zeros_b | normal_b,uniform_b, zeros_b,ones_b |
バイアス初期化関数パラメータ | - | normal_bの場合、"mean"、"var" uniform_bの場合、"min"、"max" |
学習率 | 0.1 | - |
バッチサイズ | 100 | - |
エポック数 | 50 | - |
データ正規化関数 | min_max | min_max,z_score |
データ正規化関数パラメータ | - | "axis":0、"axis":None |
中間層活性化関数 | relu | sigmoid,tanh,relu,leaky_relu, identity,softplus,softsign,step prelu,rrelu,relun,srelu,elu,maxout |
中間層活性化関数パラメータ | - | leaky_reluの場合、"alpha" preluの場合、"alpha" rreluの場合、"alpha" relunの場合、"n" sreluの場合、"alpha" eluの場合、"alpha" maxoutの場合、"unit" |
出力層活性化関数 | softmax | softmax,identity |
出力層活性化関数パラメータ | - | - |
損失関数 | cross_entropy_error | mean_squared_error,cross_entropy_error |
最適化関数 | SGD | SGD,Momentum,Nesterov,AdaGrad,RMSProp,Adadelta,Adam |
最適化関数パラメータ | - | Momentum,Nesterovの場合、"mu" RMSProp,Adadeltaの場合、"gamma" Adamの場合、"beta1","beta2" AdaGrad,RMSProp,Adadelta,Adamの場合、"epsilon" |
正則化パラメータ | - | "weight_decay_func":L1_norm,L2_norm "weight_decay_lambda":重み定数 "dropout_input_ratio":入力層に対するドロップアウト率 "dropout_middle_ratio":中間層に対するドロップアウト率 "batch_norm":バッチ正規化有無(True,False) "batch_norm_eta":バッチ正規化学習率 |
学習データシャフルフラグ | True | True,False |
import numpy as np
import time
# 学習
def learn(
name, # 学習識別名
x_train, # 学習データ
t_train, # 学習正解
x_test, # テストデータ
t_test, # テスト正解
md=[100, 50], # 中間層ノード数
weight_init_func=he_normal, # 重み初期化関数
weight_init_params={}, # 重み初期化関数パラメータ
bias_init_func=zeros_b, # バイアス初期化関数
bias_init_params={}, # バイアス初期化関数パラメータ
eta=0.1, # 学習率
batch_size=100, # バッチサイズ
epoch=50, # エポック数
data_norm_func=min_max, # データ正規化関数
data_norm_params={}, # データ正規化関数パラメータ
middle_func=relu, # 中間層活性化関数
middle_params={}, # 中間層活性化関数パラメータ
output_func=softmax, # 出力層活性化関数
output_params={}, # 出力層活性化関数パラメータ
error_func=cross_entropy_error, # 損失関数
optimizer_func=SGD, # 最適化関数
optimizer_params={}, # 最適化パラメータ
regularization_params={}, # 正則化パラメータ
shuffle_flag=True # 学習データシャフルフラグ
):
# 学習識別名表示
print(name)
# ノード数
d = [x_train.shape[x_train.ndim-1]] + md + [t_train.shape[t_train.ndim-1]]
# 階層数
layer = len(d) - 1
# 重み、バイアスの初期化
W = {}
b = {}
unit = 1
if "unit" in middle_params:
unit = middle_params["unit"]
for i in range(layer-1):
W[i+1] = weight_init_func(d[i], d[i+1]*unit, weight_init_params)
for i in range(layer-1):
b[i+1] = bias_init_func(d[i+1]*unit, bias_init_params)
W[layer] = weight_init_func(d[layer-1], d[layer], weight_init_params)
b[layer] = bias_init_func(d[layer], bias_init_params)
# 入力データの正規化
stats = {}
nx_train, train_stats = data_norm_func(x_train, stats, data_norm_params)
nx_test, test_stats = data_norm_func(x_test, train_stats, data_norm_params)
# 正解率、誤差初期化
train_rate = np.zeros(epoch+1)
test_rate = np.zeros(epoch+1)
train_err = np.zeros(epoch+1)
test_err = np.zeros(epoch+1)
# 勾配関数
middle_back_func = eval(middle_func.__name__ + "_back")
output_error_back_func = eval(output_func.__name__ + "_" + error_func.__name__ + "_back")
if "weight_decay_func" in regularization_params: # 重み減衰
regularization_params["weight_decay_back_func"] = eval(regularization_params["weight_decay_func"].__name__ + "_back")
# PReLU初期化
if middle_func == prelu:
optimizer_stats_alpha = {}
middle_params_alpha = {}
for i in range(1, layer):
middle_params_alpha[i] = np.zeros(d[i])
optimizer_stats_alpha[i] = {}
middle_params["alphas"] = middle_params_alpha
# RReLUの初期化
if middle_func == rrelu:
middle_params_alpha = {}
for i in range(1, layer):
middle_params_alpha[i] = np.random.uniform(middle_params["min"], middle_params["max"], d[i])
middle_params["alphas"] = middle_params_alpha
# 最適化情報初期化
optimizer_statsW = {}
optimizer_statsb = {}
for i in range(layer):
optimizer_statsW[i+1] = {}
optimizer_statsb[i+1] = {}
# 正規化用、γ、β、学習率の初期化
batch_norm_params = {}
batch_norm_params["batch_norm_gamma"] = {}
batch_norm_params["batch_norm_beta"] = {}
for i in range(1, layer):
batch_norm_params["batch_norm_gamma"][i] = np.ones(d[i])
batch_norm_params["batch_norm_beta"][i] = np.zeros(d[i])
batch_norm_eta = eta
if "batch_norm_eta" in regularization_params:
batch_norm_eta = regularization_params["batch_norm_eta"]
optimizer_stats_gamma = {}
optimizer_stats_beta = {}
for i in range(1, layer):
optimizer_stats_gamma[i] = {}
optimizer_stats_beta[i] = {}
# 実行(学習データ)
u_train, un_train, z_train, y_train = propagation(layer, nx_train, W, b, middle_func, middle_params, output_func, output_params, regularization_params, batch_norm_params)
train_rate[0] = accuracy_rate(y_train, t_train)
train_err[0] = calc_error(y_train, t_train, W, error_func, regularization_params)
# 実行(テストデータ)
u_test, un_test, z_test, y_test = propagation(layer, nx_test, W, b, middle_func, middle_params, output_func, output_params, regularization_params, batch_norm_params)
test_rate[0] = accuracy_rate(y_test, t_test)
test_err[0] = calc_error(y_test, t_test, W, error_func, regularization_params)
# 正解率、誤差表示
print(" 学習データ正解率 = " + str(train_rate[0]) + " テストデータ正解率 = " + str(test_rate[0]) +
" 学習データ誤差 = " + str(train_err[0]) + " テストデータ誤差 = " + str(test_err[0]))
# 開始時刻設定
start_time = time.time()
for i in range(epoch):
# 学習データシャッフル
nx = nx_train
t = t_train
if shuffle_flag:
# データのシャッフル(正解データも同期してシャフルする必要があるため一度、結合し分離)
nx_t = np.concatenate([nx_train, t_train], axis=1)
np.random.shuffle(nx_t)
nx, t = np.split(nx_t, [nx_train.shape[1]], axis=1)
# 学習
for j in range(0, nx.shape[0], batch_size):
# ドロップアウトマスクの設定
dropout_mask = set_dropout_mask(d, regularization_params)
# Nesterovの場合
if optimizer_func == Nesterov:
for k in range(1, layer+1):
if "v" in optimizer_statsW[k]:
W[k] = W[k] + optimizer_params["mu"] * optimizer_statsW[k]["v"]
if "v" in optimizer_statsb[k]:
b[k] = b[k] + optimizer_params["mu"] * optimizer_statsb[k]["v"]
# 実行
u, un, z, y = propagation(layer, nx[j:j+batch_size], W, b, middle_func, middle_params, output_func, output_params,
regularization_params, batch_norm_params, dropout_mask)
# 勾配を計算
du, dz, dW, db, batch_norm_dparams = back_propagation(layer, u, un, z, y, t[j:j+batch_size], W, b, middle_back_func, middle_params, output_error_back_func,
regularization_params, batch_norm_params, dropout_mask)
# 重み、バイアスの調整
for k in range(1, layer+1):
W[k], optimizer_statsW[k] = optimizer_func(W[k], dW[k], eta, optimizer_params, optimizer_statsW[k])
b[k], optimizer_statsb[k] = optimizer_func(b[k], db[k], eta, optimizer_params, optimizer_statsb[k])
# PReLUの調整
if middle_func == prelu:
for k in range(1, layer):
dalpha = prelu_alpha_back(dz[k], un[k], z[k], {"alpha":middle_params["alphas"][k]})
middle_params["alphas"][k], optimizer_stats_alpha[k] = optimizer_func(middle_params["alphas"][k], dalpha, eta, optimizer_params, optimizer_stats_alpha[k])
# バッチ正規化の調整
if regularization_params.get("batch_norm"):
for k in range(1, layer):
batch_norm_params["batch_norm_gamma"][k], optimizer_stats_gamma[k] = optimizer_func(batch_norm_params["batch_norm_gamma"][k], batch_norm_dparams["batch_norm_dgamma"][k], batch_norm_eta, optimizer_params, optimizer_stats_gamma[k])
batch_norm_params["batch_norm_beta"][k], optimizer_stats_beta[k] = optimizer_func(batch_norm_params["batch_norm_beta"][k], batch_norm_dparams["batch_norm_dbeta"][k], batch_norm_eta, optimizer_params, optimizer_stats_beta[k])
# 重み、バイアス調整後の実行(学習データ)
u_train, un_train, z_train, y_train = propagation(layer, nx_train, W, b, middle_func, middle_params, output_func, output_params, regularization_params, batch_norm_params)
train_rate[i+1] = accuracy_rate(y_train, t_train)
train_err[i+1] = calc_error(y_train, t_train, W, error_func, regularization_params)
# 重み、バイアス調整後の実行(テストデータ)
u_test, un_test, z_test, y_test = propagation(layer, nx_test, W, b, middle_func, middle_params, output_func, output_params, regularization_params, batch_norm_params)
test_rate[i+1] = accuracy_rate(y_test, t_test)
test_err[i+1] = calc_error(y_test, t_test, W, error_func, regularization_params)
# 正解率、誤差表示
print(str(i+1) + " 学習データ正解率 = " + str(train_rate[i+1]) + " テストデータ正解率 = " + str(test_rate[i+1]) +
" 学習データ誤差 = " + str(train_err[i+1]) + " テストデータ誤差 = " + str(test_err[i+1]))
# 終了時刻設定
end_time = time.time()
total_time = end_time - start_time
print("所要時間 = " +str(int(total_time/60))+" 分 "+str(int(total_time%60)) + " 秒")
return y_train, y_test, W, b, train_rate, train_err, test_rate, test_err, total_time