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

今まで実装していなかった活性化関数について実装してみます。MaxOutおよびLeRU関連について確認します。

# MaxOut
MaxOutは、以下の式で表せます。
通常のaffine変換をn個用意し、そのうち最大の値を採用します。

```math
\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個の場合は、以下のようになります。

```math
\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回実行するようにします。１度で実行できるように、重みをn個分用意します。
回数は、活性化関数のパラメータとして、unitを受け渡します。

### 重みの初期化
unitが指定されている場合、重みをunit分用意します。

```python
    # 重み、バイアスの初期化
    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を行っています。

```python
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分でループしひとつづつ実行しました。

```python
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の場合も載せます。

```python
# 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](https://qiita-image-store.s3.amazonaws.com/0/212365/62f3e0e2-a4b4-f413-4574-0dd1ab43114d.png)

unit数が3,4の場合は、若干悪くなりましたが、基本的にはノード数を増やすと良くなるようです。

重みを多く利用しているため、同じ数だけ重みを利用したReLUの場合を示します。

```python
# 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

```python
# 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の結果です。

```python
# 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$を学習により決定します。

```math
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とします。

```python
    # 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はノード数分です。

```python
def prelu(u, params):
    return np.where(u > 0, u, u * params["alpha"] )
```

各層ごとに$\alpha$を渡すため順伝播の関数を変更します。

```python
# 順伝播
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はノード数分です。

```python
def prelu_back(dz, u, z, params):
    return dz * np.where(u > 0, 1, params["alpha"])
```

各層ごとに$\alpha$を渡すため逆伝播の関数を変更します。

```python
# 逆伝播
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$の勾配を求めます。

```python
def prelu_alpha_back(dz, u, z, params):
    return np.sum(dz * np.where(u > 0, 0, u), axis=0) / u.shape[0]
```

$\alpha$の調整を行います。

```python
            # 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より少しだけよくなりました。

```python
# 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](https://qiita-image-store.s3.amazonaws.com/0/212365/4ce9c6e3-fd96-6fbe-e855-43cdb4bb400f.png)

50エポック後の$\alpha$の値の分布をヒストグラムにしてみましょう。$\alpha$を返却するように変更し実行しています。

```python
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](https://qiita-image-store.s3.amazonaws.com/0/212365/dcd21040-1984-edef-0467-2147cb5b651b.png)

大部分の$\alpha$の値が負になっています。正になると考えていましたが以外です。何か実装を間違えたか？

## RReLU(Randomized Leaky ReLU)
Leaky ReLUの勾配を乱数により決定しようというものです。
以下の$\alpha$を乱数により決定します。

```math
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間の乱数を設定します。

```python
    # 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と同じです。

```python
def rrelu(u, params):
    return np.where(u > 0, u, u * params["alpha"] )
```

propagationは、PReLUと同様の対応を行います。

#### 逆伝播
逆伝播もPReLUと同じです

```python
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で試してみます。

```python
# 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](https://qiita-image-store.s3.amazonaws.com/0/212365/9c05e06d-e7e1-ca0b-c471-48a7327fe3fa.png)

なんと、Lealy ReRUは、負の場合が良くなった。PReLUで学習後に負になったのも間違いではないのかも。
RReLUは、少しだけ良い結果になりました。偶然なのかどうかはわかりません。

## 一定の値以上を定数とするReLU
ReLUで、$u$が一定以上の値となった場合、その値を上限とします。

```math
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](https://qiita-image-store.s3.amazonaws.com/0/212365/5b69cd62-b235-598c-1411-2db1fa0bccdf.png)

### 実装
一定の値以上を定数とするReLUをrelunとしています。$n$はパラメータで渡します。

```python
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まで変更して試してみます。

```python
# 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](https://qiita-image-store.s3.amazonaws.com/0/212365/f980ad7c-3749-e237-c690-ca5f66258174.png)

## SReLU(Shifted ReLU)
ReLUは、0を境目に負の場合に0としますが、境目を負の方向にシフトします。

```math
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](https://qiita-image-store.s3.amazonaws.com/0/212365/faeba0d7-6ec6-13b7-320c-d5d1ff5ab330.png)

### 実装
$\alpha$をパラメータとして渡すようにしています。

```python
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に変更して試してみます。

```python
# 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](https://qiita-image-store.s3.amazonaws.com/0/212365/36c65079-e7bf-dedc-b5fe-97b03621acd6.png)

## ELU
SReLUを滑らかにしたものとなります。

```math
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](https://qiita-image-store.s3.amazonaws.com/0/212365/763b888f-275d-ac98-fadc-cb8d73e6d803.png)

### 実装
$\alpha$をパラメータとして渡すようにしています。

```python
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に変更して試してみます。

```python
# 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](https://qiita-image-store.s3.amazonaws.com/0/212365/37d482c8-7b4b-a810-85de-0e88cfdcca98.png)


ReLU関連をいろいろと試してみました。
ReLUより良い結果となる場合もありましたが、確実にこれがよいという活性関数はわかりませんでした。何れにせよ試してみるしかなさそうです。
MaxOutは、ノートPCのスペック上、5ユニットまでしか試しませんでした。ユニット数を増やせばもっと良くなる可能性はあります。

# 参考
プログラム全体です。

```python
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]
```

```python
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,<BR>glorot_normal,glorot_uniform,<BR>he_normal,he_uniform,<BR>normal_w,uniform_w,<BR>zeros_w,ones_w |
| 重み初期化関数パラメータ | -          | normal_wの場合、"mean"、"var"<BR>uniform_wの場合、"min"、"max" |
| バイアス初期化関数 | zeros_b　　　　　 | normal_b,uniform_b,<BR>zeros_b,ones_b |
| バイアス初期化関数パラメータ | -　　　 | normal_bの場合、"mean"、"var"<BR>uniform_bの場合、"min"、"max" |
| 学習率            |             0.1  | -            |
| バッチサイズ       |              100 | -            |           
| エポック数        |               50 | -                |
| データ正規化関数   | min_max           | min_max,z_score |
| データ正規化関数パラメータ | -         | "axis":0、"axis":None |
| 中間層活性化関数   | relu             | sigmoid,tanh,relu,leaky_relu,<BR>identity,softplus,softsign,step<BR>prelu,rrelu,relun,srelu,elu,maxout |
| 中間層活性化関数パラメータ |  -        | leaky_reluの場合、"alpha"<BR> preluの場合、"alpha"<BR>rreluの場合、"alpha"<BR>relunの場合、"n"<BR>sreluの場合、"alpha"<BR>eluの場合、"alpha"<BR>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"<BR>RMSProp,Adadeltaの場合、"gamma"<BR>Adamの場合、"beta1","beta2"<BR>AdaGrad,RMSProp,Adadelta,Adamの場合、"epsilon" |
| 正則化パラメータ   | -                | "weight_decay_func":L1_norm,L2_norm<BR>"weight_decay_lambda":重み定数<BR>"dropout_input_ratio":入力層に対するドロップアウト率<BR>"dropout_middle_ratio":中間層に対するドロップアウト率<BR>"batch_norm":バッチ正規化有無(True,False)<BR>"batch_norm_eta":バッチ正規化学習率 |
| 学習データシャフルフラグ | True        | True,False |

```python
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
```