0
0

More than 3 years have passed since last update.

ラビットチャレンジ stage3 深層学習(前編) -day2-

Posted at

1.勾配消失問題

1-1.勾配消失問題とは

 ●誤差逆伝播法を使用している際に、学習がうまくできなくなってしまう。 
 
 [確認テスト]
 連鎖率の原理を使い、dz/dxを求めよ

  $ z = t^2 $ ,$ t = x + y $

   \frac{dz}{dx} = \frac{dz}{dt} \cdot \frac{dt}{dx} \\

  \frac{dz}{dt} = 2t \\

  \frac{dt}{dx} = 1 \\

  \begin{eqnarray}
  \frac{dz}{dt} \frac{dt}{dx} &=& 2t \cdot 1 \\

  &=& 2t \\

  &=& 2(x + y)

  \end{eqnarray}

 
 ●誤差逆伝播法が下位層に進んでいくにつれて、勾配がどんどん緩くなっていく。
  そのため、勾配降下法による、更新では下位層のパラメータはほとんど変わらず、
  訓練は最適値に収束しなくなる。

スクリーンショット 2021-07-17 1.28.00.png

 ●上の右の図では、学習回数が増えるに連れて、正解率が上がっている。
  そのため、うまく学習していると言える。
  しかし、左の図では学習回数が増えても、正解率が上がっていない。
  このように学習がうまくできなくなってしまう。
 

1-2.勾配消失問題がなぜ発生するか

 
 ●勾配消失問題が発生する原因は、ニューラルネットワークの層の数と微分によって、引き起こされる。

スクリーンショット 2021-07-17 1.34.49.png

 ●図の赤線で囲ったところを見てみると、出力層から中間層の間で微分の数が増えている。
  ニューラルネットワークでは、中間層を増やしていってより複雑な学習ができるようにするが、
  中間層を増やせば増やすほど、微分の回数が増えていく。
  さらに、微分の値は0〜1の間を取ることが多いため、
  入力層に向かっていくにつれて、値がどんどん小さくなっていってしまう。

 ●勾配消失問題が発生してしまう、代表的な例として、シグモイド関数がある。

 ・シグモイド関数

$$ f(u) = \frac{1}{1 + e^{(-u)}} $$

sigmoid1.png

  ・シグモイド関数の微分

$$ f(u)' = (1 - f(u)) \cdot f(u) $$

sigmoid_d.png

 ●ここで問題なのが、シグモイド関数を微分すると、最大でも0.25の値しか取らない。
  例えば、中間層の活性化関数がシグモイド関数で、100層もあるとすると、
  誤差逆伝播の中で、100回も、0.25より小さい値をかける必要がある。
  そのため、入力層まで遡る頃にはとても小さい数になり、勾配消失が発生してしまう。
 
 [確認テスト]
  シグモイド関数を微分した時、入力値が0の時に最大値をとる。
  その値として正しいものを選べ。

  ①0.15 ②0.25 ③0.35 ④0.45
  
  ② 0.25

1-3.勾配消失問題の解決法

 ●活性化関数の選択
  そもそも、シグモイド関数が勾配消失問題を発生させているので、
  活性化関数自体を変更することで、勾配消失問題を解決する。

  代表的な例として、RELU関数がある。

  ・RELU関数

    f(x) =
   \left\{
    \begin{array}{ll}
     x \ (x > 0) \\
     0 \ (x \leqq 0)
   \end{array}
  \right.

RELU関数.png

  RELU関数では、出力が1より大きい場合、微分値は必ず1になり、
  1より小さい場合は、0になる。
  そのため、シグモイド関数とは違い、値が小さくなることはなくなり、
  勾配消失問題が解決できる。
  その他にも、微分値が0をとった場合は、0を取ったところの重みはあまり使われなくなり、
  必要な重みの部分だけが、選択的に残るような状態になる。(スパース化という)

  このように、活性化関数には他にも、勾配消失問題を解決できるものがあるため、
  活性化関数の選択は、解決法の1つとなりうる。

 ●重みの初期値設定

  ニューラルネットワークを最初学習させ始める時、重みは何かしらのルールに基づいて設定する。
  多くの場合は、乱数を使用して、設定を行う。
  乱数を使用して、初期値設定を行うときに、Xavierというもの等を用いることができる。

○Xavier
   重みの要素を、前の層のノード数の平方根で除算した値

   初期値を設定する際の活性化関数
    ・RELU関数
    ・シグモイド関数
    ・双曲線正接関数
   よくS字カーブを描く活性化関数に使用される

  ○He
   重みの要素を、前の層のノード数の平方根で除算した値に√2を掛け合わせた値
 
   初期値を設定する際の活性化関数
     ・RELU関数

  [確認テスト]
  重みの初期値に0を設定すると、どのような問題が発生するか簡潔にまとめよ。

  全ての重みの値が均一に更新されるため、多数の重みを持つ必要がなくなる。

 ●バッチ正規化

  バッチ正規化とは、ミニバッチ単位で、入力値のデータの偏りを抑制する手法

  バッチ正規化の使い所
  活性化関数に値を渡す前後に、バッチ正規化の処理を孕んだ層を加える

  バッチ正規化層への入力値は、
  $ u^{(l)} = w^{(l)} z^{(l-1)} + b^{(l)} $ または $z$
 
  ・数式

  1.
$$ \mu_t = \frac{1}{N_t} \sum_{i=1}^{N_t} \ x_{ni} $$

  2.
$$ \sigma_t^2 = \frac{1}{N_t} \sum_{i=1}^{N_t} (x_{ni} - \mu_t)^2 $$

  3.
$$ \hat{x}_ {ni} = \frac{x_{ni} - \mu_t}{\sqrt{\sigma_t^2 + \theta}} $$

  4.
$$ y_{ni} = \gamma x_{ni} + \beta $$

    $\mu_t$:ミニバッチt全体の平均

    $\sigma_t^2$:ミニバッチt全体の標準偏差

    $N_t$:ミニバッチのインデックス

    $\hat{x}_{ni}$:0に値を近づける計算と正規化を施した値
    
    $\gamma$:スケーリングパラメータ

    $\beta$:シフトパラメータ

    $y_{ni}$:ミニバッチのインデックスとスケーリングの積にシフトを加算した値

1-4.実装

 ●シグモイド関数-ガウス分布

# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='sigmoid', weight_init_std=0.01)

iters_num = 2000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)

    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        network.params[key] -= learning_rate * grad[key]

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)

    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

sigmoid_gauss.png

  シグモイド関数とガウス分布を使用すると正解率は上がらず、
  上手く学習ができていない。

 ●RELU関数-ガウス分布

# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='relu', weight_init_std=0.01)

iters_num = 2000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)

    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        network.params[key] -= learning_rate * grad[key]

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)

    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

RELU_gauss.png

  RELU関数とガウス分布では500回までは全然、学習できていなかったが、
  500回を超えてくると次第に、学習できていった。
  シグモイド関数からRELU関数に変更しただけで、勾配消失問題が回避され、
  学習ができることがわかる。

 ●シグモイド関数-Xavier

# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='sigmoid', weight_init_std='Xavier')

iters_num = 2000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)

    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        network.params[key] -= learning_rate * grad[key]

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)

    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

sigmoid_Xavier.png

  次にシグモイド関数とXavierの組み合わせ、ガウス分布を使用した際よりも、
  学習が行われている。

  ●RELU関数-He

# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='relu', weight_init_std='He')

iters_num = 2000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)

    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        network.params[key] -= learning_rate * grad[key]

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)

    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

RELU_He.png

  最後にRELU関数とHe関数では初めの方からいきなり正解率が上がっている。
  4つの中では一番学習ができていて、効率も良い。

  [確認テスト]

  ①.hidden_size_listを変更する。

  コードは省略

   1.シグモイド関数-ガウス分布

sigmoid_gauss2.png

   2.RELU関数-ガウス分布

RELU_gauss2.png

   3.シグモイド関数-Xavier

sigmoid_Xavier2.png

   4.RELU関数-He

RELU_He2.png

  1以外は、全体的に、正解率が上がるタイミングが早くなった。
  

 ②同じように「シグモイド関数とHe」、「RELU関数とXavier」の組み合わせを試す。

  コード省略

  1.シグモイド関数-He
sigmoid-he.png

  2.RELU関数-Xavier
relu_xavier.png

   どちらも同じように学習ができている。

  バッチ正規化

import numpy as np
from collections import OrderedDict
from common import layers
from data.mnist import load_mnist
import matplotlib.pyplot as plt
from multi_layer_net import MultiLayerNet
from common import optimizer

# バッチ正則化 layer
class BatchNormalization:
    '''
    gamma: スケール係数
    beta: オフセット
    momentum: 慣性
    running_mean: テスト時に使用する平均
    running_var: テスト時に使用する分散
    '''
    def __init__(self, gamma, beta, momentum=0.9, running_mean=None, running_var=None):
        self.gamma = gamma
        self.beta = beta
        self.momentum = momentum
        self.input_shape = None

        self.running_mean = running_mean
        self.running_var = running_var  

        # backward時に使用する中間データ
        self.batch_size = None
        self.xc = None
        self.std = None
        self.dgamma = None
        self.dbeta = None

    def forward(self, x, train_flg=True):
        if self.running_mean is None:
            N, D = x.shape
            self.running_mean = np.zeros(D)
            self.running_var = np.zeros(D)

        if train_flg:
            mu = x.mean(axis=0) # 平均
            xc = x - mu # xをセンタリング
            var = np.mean(xc**2, axis=0) # 分散
            std = np.sqrt(var + 10e-7) # スケーリング
            xn = xc / std

            self.batch_size = x.shape[0]
            self.xc = xc
            self.xn = xn
            self.std = std
            self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * mu # 平均値の加重平均
            self.running_var = self.momentum * self.running_var + (1-self.momentum) * var #分散値の加重平均
        else:
            xc = x - self.running_mean
            xn = xc / ((np.sqrt(self.running_var + 10e-7)))

        out = self.gamma * xn + self.beta 

        return out

    def backward(self, dout):
        dbeta = dout.sum(axis=0)
        dgamma = np.sum(self.xn * dout, axis=0)
        dxn = self.gamma * dout
        dxc = dxn / self.std
        dstd = -np.sum((dxn * self.xc) / (self.std * self.std), axis=0)
        dvar = 0.5 * dstd / self.std
        dxc += (2.0 / self.batch_size) * self.xc * dvar
        dmu = np.sum(dxc, axis=0)
        dx = dxc - dmu / self.batch_size

        self.dgamma = dgamma
        self.dbeta = dbeta

        return dx    

batch.png

  [確認テスト]
スクリーンショット 2021-07-17 7.18.02.png

  (2)
   解説
    batch_sizeに一つのミニバッチの数が入力される。
    Nには学習用の全てのデータが入る。
    まずはiにミニバッチのスタートの数を入れる。
    その後、i_endにミニバッチの最後の数を入れる。
    data_xから定義したスタートとエンドまでをスライスで取り出す。

 

2.学習率の最適化

2-1.学習率の復習

 ●学習率の値が大きい場合
  ○最適値にいつまでも辿り着かず、発散してしまう。

 ●学習率の値が小さい場合
  ○発散することはないが、小さすぎると収束までに時間がかかってしまう。
  ○大域局所最適値に収束しづらくなる。

 ●初期の学習率設定方法の指針
  ○初期の学習率を大きく設定し、徐々に学習率を小さくしていく
  ○パラメータごとに学習率を可変させる
   ⇨学習率最適化手法を利用して、学習率を最適化

2-1.モメンタム

  ●誤差をパラメータで微分したものと学習率の積を積を減算したあと、
   現在の重みに前回の重みを減算した値と慣性の積を加算する

  ●メリット
   ○局所的最適解にはならず、大域的最適解となる。
   ○谷間についてから最も低い位置(最適値)にいくまでの時間が早い

  ●数式

  V_t = \mu V_{t-1} - \varepsilon \nabla E \\
  W^{(t+1)} = w^{(t)} + V_t \\
  慣性: \mu

  ●コード

 self.v[key] = self.momentum* self.v[key] -self.learning_rate* grad[key]

 params[key] += self.v[key]

2-3.AdaGrad

 ●誤差をパラメータで微分したものと再定義した学習率の積を減算する

 ●メリット
  ○勾配の緩やかな斜面に対して、最適値に近づける

 ●課題
  ○学習率が徐々に小さくなるので、鞍点問題を引き起こす事があった

 ●数式

  h_0 = \theta \\
  h_t = h_{t-1} + (\nabla E)^2 \\
  w^{(t+1)} = w^{(t)} - \varepsilon \frac{1}{\sqrt{h_t} + \theta} \nabla E

  ●コード

  self.h[key] = np.zeros_like(val)S2)-247

  self.h[key] += grad[key] * grad[key]

  params[key] -= self.learning_rate* grad[key] / (np.sqrt(self.h[key]) + 1e-7)

2-4.RMSProp

 ●誤差をパラメータで微分したものと再定義した学習率の積を減算する

 ●メリット
  ○局所的最適解にはならず、大域的最適解となる。
  ○ハイパーパラメータの調整が必要な場合が少ない

 ●数式

  h_t = \alpha h_{t-1} + (1 - \alpha) (\nabla E)^2 \\
  w^{(t+1)} = w^{(t)} - \varepsilon \frac{1}{\sqrt{h_t} + \theta} \nabla E

 ●コード

 self.h[key] *= self.decay_rate
 self.h[key] += (1 -self.decay_rate) * grad[key] * grad[key]

 params[key] -= self.learning_rate* grad[key] / (np.sqrt(self.h[key]) + 1e-7)

2-4.Adam

 ●Adamとは
   ・モメンタムの、過去の勾配の指数関数的減衰平均
   ・RMSPropの、過去の勾配の2乗の指数関数的減衰平均
  上記をそれぞれ孕んだ最適化アルゴリズムである。

 ●メリット
  ○モメンタムおよびRMSPropのメリットを孕んだアルゴリズムである

 [確認テスト]
   ・モメンタム、AdaGrad、RMSpropの特徴をそれぞれ簡潔に説明せよ。

   モメンタム:大域的最適解となる
   AdaGrad:勾配の緩やかな斜面に対して、最適値に近づける
   RMSprop:ハイパーパラメータの調整が必要な場合が少ない

2-5.実装

  ●SGD

import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from collections import OrderedDict
from common import layers
from data.mnist import load_mnist
import matplotlib.pyplot as plt
from multi_layer_net import MultiLayerNet


# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

# batch_normalizationの設定 ================================
# use_batchnorm = True
use_batchnorm = False
# ====================================================


network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='sigmoid', weight_init_std=0.01,
                       use_batchnorm=use_batchnorm)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)

    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        network.params[key] -= learning_rate * grad[key]

        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)


    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

SGD.png

  SGDではうまく学習ができていないことがわかる。

 ●モメンタム


# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

# batch_normalizationの設定 ================================
# use_batchnorm = True
use_batchnorm = False
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='sigmoid', weight_init_std=0.01,
                       use_batchnorm=use_batchnorm)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01
# 慣性
momentum = 0.9

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        v = {}
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        if i == 0:
            v[key] = np.zeros_like(network.params[key])
        v[key] = momentum * v[key] - learning_rate * grad[key]
        network.params[key] += v[key]

        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)

    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

momentam.png

 今回のデータではモメンタムでもうまく学習ができていない。

 ●AdaGrad

# AdaGradを作ってみよう
# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

# batch_normalizationの設定 ================================
# use_batchnorm = True
use_batchnorm = False
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='sigmoid', weight_init_std=0.01,
                       use_batchnorm=use_batchnorm)

iters_num = 1000
# iters_num = 500 # 処理を短縮

train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01

# AdaGradでは不必要
# =============================

momentum = 0.9 

# =============================

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        h = {}
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):

        # 変更しよう  
        # ===========================================
        if i == 0:
            h[key] = np.zeros_like(network.params[key])        
        h[key] = momentum * h[key] - learning_rate * grad[key]
        network.params[key] += h[key]

        # ===========================================

        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)

    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

AdaGrad.png

 ●RMSProp


# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

# batch_normalizationの設定 ================================
# use_batchnorm = True
use_batchnorm = False
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='sigmoid', weight_init_std=0.01,
                       use_batchnorm=use_batchnorm)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01
decay_rate = 0.99

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        h = {}
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        if i == 0:
            h[key] = np.zeros_like(network.params[key])
        h[key] *= decay_rate
        h[key] += (1 - decay_rate) * np.square(grad[key])
        network.params[key] -= learning_rate * grad[key] / (np.sqrt(h[key]) + 1e-7)

        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)                

    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

RMSProp.png

  RMSPropではうまく学習できることがわかる。

 ●Adam


# データの読み込み
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)

print("データ読み込み完了")

# batch_normalizationの設定 ================================
# use_batchnorm = True
use_batchnorm = False
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[40, 20], output_size=10, activation='sigmoid', weight_init_std=0.01,
                       use_batchnorm=use_batchnorm)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01
beta1 = 0.9
beta2 = 0.999

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    # 勾配
    grad = network.gradient(x_batch, d_batch)
    if i == 0:
        m = {}
        v = {}
    learning_rate_t  = learning_rate * np.sqrt(1.0 - beta2 ** (i + 1)) / (1.0 - beta1 ** (i + 1))    
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        if i == 0:
            m[key] = np.zeros_like(network.params[key])
            v[key] = np.zeros_like(network.params[key])

        m[key] += (1 - beta1) * (grad[key] - m[key])
        v[key] += (1 - beta2) * (grad[key] ** 2 - v[key])            
        network.params[key] -= learning_rate_t * m[key] / (np.sqrt(v[key]) + 1e-7)                


    if (i + 1) % plot_interval == 0:
        accr_test = network.accuracy(x_test, d_test)
        accuracies_test.append(accr_test)        
        accr_train = network.accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)
        loss = network.loss(x_batch, d_batch)
        train_loss_list.append(loss)        

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

Adam.png

3.過学習

3-1.過学習の復習
  
 ●訓練誤差だけ小さくなり、テスト誤差が小さくならい現象

スクリーンショット 2021-07-17 9.05.22.png

 ●発生する原因
  ○パラメータの数が多い
  ○パラメータの値が適切ではない
  ○ノードが多い
  など。。。
  ネットワークの自由度が高すぎることが原因となる。

3-2.正則化

 ●ネットワークの自由度(層数、ノード数、パラメータの値etc...)を制約すること
 
 ●Weight decay(荷重減衰)
  過学習の原因
   ○重みが大きい値をとることで、過学習が発生することがある。
    ⇨学習させていくと、重みにばらつきが発生する
      重みが大きい値は、学習において重要な値であり、重みが大きいと過学習が起こる

 過学習の解決策
  ○誤差に対して、正則化項を加算することで、重みを抑制する
   ⇨過学習がおこりそうな重みの大きさ以下で重みをコントロールし、
     かつ重みの大きさにばらつきを出す必要がある。

 ●L1正則化、L2正則化

   ○数式

   E_n(w) + \frac{1}{p} \lambda || x ||_p \\

   || x ||_p = (|x_1|^p + \cdots + |x_n|^p)^{\frac{1}{p}}

   1行目では、誤差関数にpノルムを加える。
   2行目では、pノルムの計算を行う。

   p=1の場合、L1正則化と呼ぶ。(別名:ラッソ回帰)
   p=2の場合、L2正則化と呼ぶ。(別名:リッジ回帰)

  ノルムとは距離を表しており図で書くと以下のようなイメージとなる。

スクリーンショット 2021-07-17 9.27.30.png

距離の求め方にも色々とある。
  P1ノルムの距離のことをマンハッタン距離という
  P2ノルムの距離のことをユークリッド距離という

  ○コード

 np.sum(np.abs(network.params['W' + str(idx)]))

 weight_decay+= weight_decay_lambda*np.sum(np.abs(network.params['W' + str(idx)]))

 loss = network.loss(x_batch, d_batch) + weight_decayS3)-163

  [確認テスト]
   下図について、L1正則化を表しているグラフはどちらか答えよ

スクリーンショット 2021-07-17 9.49.00.png

   右図がL1正則化である。
   右図(L1正則化)では、最終的な重みは縦軸上に目指していく。
   そのため、1つの重み(横軸の重み)を0のポイントになるように探し出してくれる。

スクリーンショット 2021-07-17 15.14.04.png

  (え) (4)param
  解説
   L2ノルムは、$||p||^2$なので、その勾配が誤差の勾配に加えられる。
   つまり、2*param であるが、係数2は正則化の係数に吸収されても変わらないのでparamが正解である。

スクリーンショット 2021-07-17 15.25.30.png

  (お) (3)np.sign(param)
  解説
  L1ノルムは、|param|なのでその勾配が誤差の勾配に加えられる。
  つまり、sign(param)である。
  signは+の値だったら1、ーの値だったら−1、0だったら0を出力する符号関数である。

スクリーンショット 2021-07-17 15.29.59.png

  (か) (4)image[top:bottom, left:right, :]
   解説
    imageの形式が(縦幅, 横幅, チャンネル)であるのも考慮する。

3-3.ドロップアウト

 ●過学習の課題
  ○ノードの数が多い

 ●ドロップアウト
  ○ランダムにノードを削除して、学習させること

  スクリーンショット 2021-07-17 15.36.53.png

 ●メリット
   ○データ量を変化させずに、異なるモデルを学習させていると解釈できる

3-4.実装

 ●正則化やドロップアウトを行っていない状態(過学習を起こしている場合)

import numpy as np
from collections import OrderedDict
from common import layers
from data.mnist import load_mnist
import matplotlib.pyplot as plt
from multi_layer_net import MultiLayerNet
from common import optimizer


(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)
optimizer = optimizer.SGD(learning_rate=0.01)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10


for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)

    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))        

lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

overfitting.png

学習の途中から、trainig setに対しては、正解率が100%となっている。
    しかし、test set では途中から学習結果があまり変わらず、過学習を起こしているとわかる。

 ●L2正則化

from common import optimizer

(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]


network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)


iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate=0.01

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10
hidden_layer_num = network.hidden_layer_num

# 正則化強度設定 ======================================
weight_decay_lambda = 0.1
# =================================================

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    weight_decay = 0

    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * network.params['W' + str(idx)]
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += 0.5 * weight_decay_lambda * np.sqrt(np.sum(network.params['W' + str(idx)] ** 2))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss)        

    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))               


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

L2.png

  training setの正解率が100%にならなくなった。
  L2正則化を加えたことによって、一番そこに辿り着けなくなったことにより、
  training setに対しても、100%にならなくなった。
  しかしこれも、training set と test set でまだ離れているので、
  期待したものとは結果が変わってきている。

 ●L1正則化

(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)


iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate=0.1

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10
hidden_layer_num = network.hidden_layer_num

# 正則化強度設定 ======================================
weight_decay_lambda = 0.005
# =================================================

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    weight_decay = 0

    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * np.sign(network.params['W' + str(idx)])
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += weight_decay_lambda * np.sum(np.abs(network.params['W' + str(idx)]))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss)        

    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))               

lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

L!.png

  こちらについても、L2正則化と同様にtraning set は100%にならなくなっている。
  また、traing set とtest setとの差もあまり変わらない結果となった。

 ●ドロップアウト
```python
class Dropout:
def init(self, dropout_ratio=0.5):
self.dropout_ratio = dropout_ratio
self.mask = None

def forward(self, x, train_flg=True):
    if train_flg:
        self.mask = np.random.rand(*x.shape) > self.dropout_ratio
        return x * self.mask
    else:
        return x * (1.0 - self.dropout_ratio)

def backward(self, dout):
    return dout * self.mask

```python

from common import optimizer
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

# ドロップアウト設定 ======================================
use_dropout = True
dropout_ratio = 0.15
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                        weight_decay_lambda=weight_decay_lambda, use_dropout = use_dropout, dropout_ratio = dropout_ratio)
optimizer = optimizer.SGD(learning_rate=0.01)
# optimizer = optimizer.Momentum(learning_rate=0.01, momentum=0.9)
# optimizer = optimizer.AdaGrad(learning_rate=0.01)
# optimizer = optimizer.Adam()

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10


for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)    

    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))        

lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

dropout.png

  ドロップアウトでは、他の2つに比べ、順調に学習ができている。

 ●ドロップアウト + L1


from common import optimizer
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

# ドロップアウト設定 ======================================
use_dropout = True
dropout_ratio = 0.08
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                        use_dropout = use_dropout, dropout_ratio = dropout_ratio)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate=0.01

train_loss_list = []
accuracies_train = []
accuracies_test = []
hidden_layer_num = network.hidden_layer_num

plot_interval=10

# 正則化強度設定 ======================================
weight_decay_lambda=0.004
# =================================================

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    weight_decay = 0

    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * np.sign(network.params['W' + str(idx)])
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += weight_decay_lambda * np.sum(np.abs(network.params['W' + str(idx)]))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss)        

    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))               

lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

dropout+L!.png

ドロップアウトとL1正則化を合わせることによって、ドロップアウト単体の時より、
   学習のスピードが上がったことがわかる。

4 畳み込みニューラルネットワーク(CNN)

4-1.CNNとは

 ●画像の識別や、画像の処理によく用いられる
   しかしながら、汎用性も高く画像の識別以外にも使用される。
   CNNでは、次元間で、繋がりのあるデータを扱うことができる。
   例えば、
    ○単一チャンネル
     ・1次元:音声[時刻、強度]
     ・2次元:フーリエ変換した音声[時刻、周波数、強度]
     ・3次元:CTスキャン[x,y,z,強度]
    ○複数チャンネル
     ・1次元:アニメのスケルトン[時刻,(腕の値、膝の値)]
     ・2次元:カラー画像[x,y,(R,G,B)]
     ・3次元:動画[時刻,x,y,(R,G,B)]
 
   単一チャンネルでは、音声で例えると、ある時刻に対して、
   どれだけの空気振動を起こしたかの情報しかない。
   一方、複数チャンネルでは、カラー画像で例えると、
   あるx軸、y軸の場所では、Rがどれくらい、Gがどれくらい、Bがどれくらいという
   2次元のデータに対して、複数(今回は3つ)の情報がある。

4-2.畳み込み層の構造図

スクリーンショット 2021-07-17 16.27.14.png

  CNNを使った代表的なものにLeNetというものがある。

スクリーンショット 2021-07-17 16.29.23.png

  LeNetでは、入力の画像データは32×32pxの画像である。
  最終的に、10種類の値を出力する。(例えば入力された画像は0~9のどの数字ですか?など)
  入力から出力までの間に、畳み込み層やプーリング層、全結合層の処理を行う。
  次から一つ一つの層の説明を行っていく。

4-3.全体像

スクリーンショット 2021-07-17 16.43.37.png

 上の画像では、4×4×3の情報が入力として渡される。
   イメージとしては、4px×4pxのカラー画像で、RGBの3チャンネルを持っている。

   畳み込み層では、入力値に対して、フィルターを通して、出力結果を出す。

   スクリーンショット 2021-07-17 16.55.39.png

   画像では様々な色を使ってごちゃごちゃしてるが、
   要は、3×3のフィルターを左上、右上、左下、右下にあてて、それぞれ数値を出し、
   移動分(今回は2×2)の出力を行う。
  
   フィルターを通して新たな出力を得ているが、フィルターの当て方を見ると
   周りのデータを取得していると言える。
   そのため、周りの情報と繋がりを保ったまま、
   情報を処理できることが、CNNの特徴と言える。

スクリーンショット 2021-07-17 17.06.27.png

  全結合層というのは、今までの学習で出てきたニューラルネットワークのことである。
  畳み込み層との違いとしては、畳み込み層は次元の繋がりを保ったままの情報を出力していく。
  それに対し、全結合層では、次元の繋がりをまとめて、本来ほしかった情報に出力するようになる。

4-4.畳み込み層

 ●畳み込み層では、縦、横、チャンネルの3次元のデータをそのまま学習し、
   次に伝えることができる。
   ⇨次元の空間情報も学習できるような層が畳み込み層である。

 ●演算概念

スクリーンショット 2021-07-17 17.12.14.png

  畳み込み層ではフィルターを使うがニューラルネットワークで学習した、重みと考えて良い。
  初期の状態では、乱数を入れる。
  計算も簡単で、入力画像とフィルターの対応する所の積をとり、
  1つにまとめることで次の層への出力となる。

スクリーンショット 2021-07-17 17.21.03.png

  先程の画像では、入力画像にフィルターを当てただけなので、
  ニューラルネットワークでいう所の、x(入力) w(重み)を行ったことになる。
  ニューラルネットワークではもう一つ +b というものがあった。
  バイアスを足してあげるという処理だが、畳み込み層でも同じようにできる。

 ●パディング
  先程の演算では4×4の入力画像を、2×2の出力画像に変換した。
  仮にフィルターを2×2のフィルターにしても、出力画像は3×3となり、
  画像が小さくなってしまう。
  もし畳み込み層を大量に組み込んだニューラルネットワークがあれば、
  画像はどんどん小さくなりすぎてしまう。

  ここで登場するのが、パディングである。

スクリーンショット 2021-07-17 17.31.19.png

  パディングでは、縦のサイズ、横のサイズをあらかじめ、広げて、フィルターを通す。
  そうすることで、出力画像は大きさを変えずに出力することができるようになる。
  広げた場所の入力値については、全て0で埋めたり、一番近い所の数字を埋めたりできる。

 ●ストライド
  先程までは、フィルターを1つずつ動かしていた。
  これをストライド1という、ストライドを2に変えると、
  フィルターは2つずつ動く事になる。

  スクリーンショット 2021-07-17 17.35.44.png

●チャンネル
 チャンネルはフィルターの数を意味する。
 イメージとしてはチャンネルを増やすことによって、様々な見方ができるようになる。

スクリーンショット 2021-07-17 17.38.18.png

 ●畳み込み層ができた理由
  全結合層では、例えば画像の場合、縦、横、チャンネルの3次元データだが、
  1次元のデータとして処理される。
  せっかくRGBという各チャンネルの繋がりがあったのに、
  この繋がりが学習に反映されないため、畳み込み層ができた。

 ●実装
  ○畳み込み層

class Convolution:
    # W: フィルター, b: バイアス
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad

        # 中間データ(backward時に使用)
        self.x = None   
        self.col = None
        self.col_W = None

        # フィルター・バイアスパラメータの勾配
        self.dW = None
        self.db = None

    def forward(self, x):
        # FN: filter_number, C: channel, FH: filter_height, FW: filter_width
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        # 出力値のheight, width
        out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
        out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)

        # xを行列に変換
        col = im2col(x, FH, FW, self.stride, self.pad)
        # フィルターをxに合わせた行列に変換
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        # 計算のために変えた形式を戻す
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        # dcolを画像データに変換
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx

 畳み込み層ではデータを高速で計算するためにある工夫が行われる。
 それが、column to image (以下、col2im)と呼ばれるものになる。

# 2次元配列を画像データに変換
def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    # N: number, C: channel, H: height, W: width
    N, C, H, W = input_shape
    # 切り捨て除算    
    out_h = (H + 2 * pad - filter_h)//stride + 1
    out_w = (W + 2 * pad - filter_w)//stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2) # (N, filter_h, filter_w, out_h, out_w, C)

    img = np.zeros((N, C, H + 2 * pad + stride - 1, W + 2 * pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]

   col2imでは畳み込み演算を行う際に、フィルターを通す箇所の抽出結果を
   縦に並べることによって、後の重みを掛けたりする計算を早くする。

   例)

  \begin{array}{llll}
   [ 1 ,& 2 ,& 3 ,& 4] \\
   [ 5 ,& 6 ,& 7 ,& 8] \\
   [ 9 ,& 10 ,& 11 ,& 12] \\
   [ 13 ,& 14 ,& 15 ,& 16] \\ 
  \end{array}

上のような画像に2×2のフィルターを通した場合、
  例えば、左上を抽出すると、[1, 2, 5, 6]になる。
  この[1, 2, 5, 6]を縦にまとめる処理を同じように他のところでも行うと、

  \begin{array}{lllllllll}
   [1,& 2,& 3,& 5,& 6,& 7,& 9,& 10,& 11]\\
   [2,& 3,& 4,& 6,& 7,& 8,& 10,& 11,& 12]\\
   [5,& 6,& 7,& 9,& 10,& 11,& 13,& 14,& 15]\\
   [6,& 7,& 8,& 10,& 11,& 12,& 14,& 15,& 16]\\
  \end{array}

  となり、後に計算が高速にできるようになる。

4-5.プーリング層

 ●考え方としては、畳み込み層と同じように画像を少しずつずらしながら処理を行う。
   しかし、畳み込み層と違い、プーリング層には重みが存在しない。
   対象領域のMax値または、平均値を取得していく。

スクリーンショット 2021-07-17 18.18.25.png

 上の画像では、Max poolingを使用している。
 Max poolingでは対象領域の最大値(今回は9)を出力とする。

 [確認テスト]
 サイズ6×6の入力画像を、サイズ2×2のフィルタで畳み込んだ時の出力画像のサイズを答えよ。
 なおストライドとパディングは1とする。

 7×7

 公式

  OH = \frac{画像の高さ + 2 \times パディングの高さ + フィルターの高さ}{ストライド} + 1 \\

  \\

  OW = \frac{画像の幅 + 2 \times パディングの幅 + フィルターの幅}{ストライド} + 1 \\

 ●実装
  ○プーリング層

class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None
        self.arg_max = None

    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        # xを行列に変換
        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        # プーリングのサイズに合わせてリサイズ
        col = col.reshape(-1, self.pool_h*self.pool_w)

        #maxプーリング
        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)

        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,)) 

        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)

        return dx

5.AlexNet

5-1.モデルの構造
 ●5層の畳み込み層及びプーリング層など、それに続く3層の全結合層で構成される。

スクリーンショット 2021-07-17 18.34.05.png

5-2.処理のながれ

  1.入力には224×224の画像を用いる。
      チャンネルは3
  2.入力された画像を11×11のフィルターで畳み込みを行う
      この際、チャンネルは96個にする。
  3.55×55の画像をmax poolingで縮める。
     この際、チャンネルは256個
  4.27×27の画像を3×3のフィルターで畳み込む
     この際、チャンネルは384個
  5.最後に、また3×3のフィルターで畳み込む
     この際、画像の大きさが変わっていないので、パディングを行なっていると考えられる。
  6.最終的に256種類の13×13のデータに圧縮し、全結合層へ渡す。
      データの渡し方には様々あるが、代表的なFrattenというものは
      13×13×256のデータを横一列にまとめて渡すという処理を行う。
      その他にも、「Global Max pooling」 「Global Avg pooling」 などがある。
  7.受け取ったデータを4096個の数字に出力する。
  8.最後に1000個の数字を出力する。

AlexNetでは、サイズ4096の全結合層では、過学習を防ぐため、
  ドロップアウトを行っている。

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