LoginSignup
6
6

More than 5 years have passed since last update.

ディープラーニングを実装から学ぶ(7-1)その他(活性化関数~MaxOut、ReLU関連)

Last updated at Posted at 2018-07-15

今まで実装していなかった活性化関数について実装してみます。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秒

グラフにしてみます。

maxout_rate.png

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

グラフにします。

prelu_rate.png

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()

prelu_alpha.png

大部分の$\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

rrelu_rate.png

なんと、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の場合のグラフです。

relun.png

実装

一定の値以上を定数とする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以上だとそれなりの結果を得られました。

正解率をグラフにしてみます。

relun_n.png

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の場合のグラフです。

srelu.png

実装

$\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

srelu_rate.png

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とした場合のグラフです。

elu.png

実装

$\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

elu_rate.png

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
6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6