#1.勾配消失問題
$ 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}
●誤差逆伝播法が下位層に進んでいくにつれて、勾配がどんどん緩くなっていく。
そのため、勾配降下法による、更新では下位層のパラメータはほとんど変わらず、
訓練は最適値に収束しなくなる。
●上の右の図では、学習回数が増えるに連れて、正解率が上がっている。
そのため、うまく学習していると言える。
しかし、左の図では学習回数が増えても、正解率が上がっていない。
このように学習がうまくできなくなってしまう。
●図の赤線で囲ったところを見てみると、出力層から中間層の間で微分の数が増えている。
ニューラルネットワークでは、中間層を増やしていってより複雑な学習ができるようにするが、
中間層を増やせば増やすほど、微分の回数が増えていく。
さらに、微分の値は0〜1の間を取ることが多いため、
入力層に向かっていくにつれて、値がどんどん小さくなっていってしまう。
●勾配消失問題が発生してしまう、代表的な例として、シグモイド関数がある。
・シグモイド関数
$$ f(u) = \frac{1}{1 + e^{(-u)}} $$
・シグモイド関数の微分
$$ f(u)' = (1 - f(u)) \cdot f(u) $$
●ここで問題なのが、シグモイド関数を微分すると、最大でも0.25の値しか取らない。
例えば、中間層の活性化関数がシグモイド関数で、100層もあるとすると、
誤差逆伝播の中で、100回も、0.25より小さい値をかける必要がある。
そのため、入力層まで遡る頃にはとても小さい数になり、勾配消失が発生してしまう。
[確認テスト]
シグモイド関数を微分した時、入力値が0の時に最大値をとる。
その値として正しいものを選べ。
①0.15 ②0.25 ③0.35 ④0.45
② 0.25
●活性化関数の選択
そもそも、シグモイド関数が勾配消失問題を発生させているので、
活性化関数自体を変更することで、勾配消失問題を解決する。
代表的な例として、RELU関数がある。
・RELU関数
f(x) =
\left\{
\begin{array}{ll}
x \ (x > 0) \\
0 \ (x \leqq 0)
\end{array}
\right.
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}$:ミニバッチのインデックスとスケーリングの積にシフトを加算した値
●シグモイド関数-ガウス分布
# データの読み込み
(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()
シグモイド関数とガウス分布を使用すると正解率は上がらず、
上手く学習ができていない。
●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関数とガウス分布では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()
次にシグモイド関数と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関数では初めの方からいきなり正解率が上がっている。
4つの中では一番学習ができていて、効率も良い。
[確認テスト]
①.hidden_size_listを変更する。
コードは省略
1.シグモイド関数-ガウス分布
2.RELU関数-ガウス分布
3.シグモイド関数-Xavier
4.RELU関数-He
1以外は、全体的に、正解率が上がるタイミングが早くなった。
②同じように「シグモイド関数とHe」、「RELU関数とXavier」の組み合わせを試す。
コード省略
どちらも同じように学習ができている。
バッチ正規化
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
(2)
解説
batch_sizeに一つのミニバッチの数が入力される。
Nには学習用の全てのデータが入る。
まずはiにミニバッチのスタートの数を入れる。
その後、i_endにミニバッチの最後の数を入れる。
data_xから定義したスタートとエンドまでをスライスで取り出す。
#2.学習率の最適化
●学習率の値が小さい場合
○発散することはないが、小さすぎると収束までに時間がかかってしまう。
○大域局所最適値に収束しづらくなる。
●初期の学習率設定方法の指針
○初期の学習率を大きく設定し、徐々に学習率を小さくしていく
○パラメータごとに学習率を可変させる
⇨学習率最適化手法を利用して、学習率を最適化
●メリット
○局所的最適解にはならず、大域的最適解となる。
○谷間についてから最も低い位置(最適値)にいくまでの時間が早い
●数式
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]
●メリット
○勾配の緩やかな斜面に対して、最適値に近づける
●課題
○学習率が徐々に小さくなるので、鞍点問題を引き起こす事があった
●数式
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)
●メリット
○局所的最適解にはならず、大域的最適解となる。
○ハイパーパラメータの調整が必要な場合が少ない
●数式
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)
●Adamとは
・モメンタムの、過去の勾配の指数関数的減衰平均
・RMSPropの、過去の勾配の2乗の指数関数的減衰平均
上記をそれぞれ孕んだ最適化アルゴリズムである。
●メリット
○モメンタムおよびRMSPropのメリットを孕んだアルゴリズムである
[確認テスト]
・モメンタム、AdaGrad、RMSpropの特徴をそれぞれ簡潔に説明せよ。
モメンタム:大域的最適解となる
AdaGrad:勾配の緩やかな斜面に対して、最適値に近づける
RMSprop:ハイパーパラメータの調整が必要な場合が少ない
●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ではうまく学習ができていないことがわかる。
●モメンタム
# データの読み込み
(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()
今回のデータではモメンタムでもうまく学習ができていない。
●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()
●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ではうまく学習できることがわかる。
●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()
#3.過学習
●発生する原因
○パラメータの数が多い
○パラメータの値が適切ではない
○ノードが多い
など。。。
ネットワークの自由度が高すぎることが原因となる。
過学習の解決策
○誤差に対して、正則化項を加算することで、重みを抑制する
⇨過学習がおこりそうな重みの大きさ以下で重みをコントロールし、
かつ重みの大きさにばらつきを出す必要がある。
●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正則化と呼ぶ。(別名:リッジ回帰)
ノルムとは距離を表しており図で書くと以下のようなイメージとなる。
距離の求め方にも色々とある。
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正則化を表しているグラフはどちらか答えよ
右図がL1正則化である。
右図(L1正則化)では、最終的な重みは縦軸上に目指していく。
そのため、1つの重み(横軸の重み)を0のポイントになるように探し出してくれる。
(え) (4)param
解説
L2ノルムは、$||p||^2$なので、その勾配が誤差の勾配に加えられる。
つまり、2*param であるが、係数2は正則化の係数に吸収されても変わらないのでparamが正解である。
(お) (3)np.sign(param)
解説
L1ノルムは、|param|なのでその勾配が誤差の勾配に加えられる。
つまり、sign(param)である。
signは+の値だったら1、ーの値だったら−1、0だったら0を出力する符号関数である。
(か) (4)image[top:bottom, left:right, :]
解説
imageの形式が(縦幅, 横幅, チャンネル)であるのも考慮する。
●ドロップアウト
○ランダムにノードを削除して、学習させること
●メリット
○データ量を変化させずに、異なるモデルを学習させていると解釈できる
●正則化やドロップアウトを行っていない状態(過学習を起こしている場合)
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()
学習の途中から、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()
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()
こちらについても、L2正則化と同様にtraning set は100%にならなくなっている。
また、traing set とtest setとの差もあまり変わらない結果となった。
●ドロップアウト
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
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()
ドロップアウトでは、他の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()
ドロップアウトとL1正則化を合わせることによって、ドロップアウト単体の時より、
学習のスピードが上がったことがわかる。
#4 畳み込みニューラルネットワーク(CNN)
CNNを使った代表的なものにLeNetというものがある。
LeNetでは、入力の画像データは32×32pxの画像である。
最終的に、10種類の値を出力する。(例えば入力された画像は0~9のどの数字ですか?など)
入力から出力までの間に、畳み込み層やプーリング層、全結合層の処理を行う。
次から一つ一つの層の説明を行っていく。
上の画像では、4×4×3の情報が入力として渡される。
イメージとしては、4px×4pxのカラー画像で、RGBの3チャンネルを持っている。
畳み込み層では、入力値に対して、フィルターを通して、出力結果を出す。
画像では様々な色を使ってごちゃごちゃしてるが、
要は、3×3のフィルターを左上、右上、左下、右下にあてて、それぞれ数値を出し、
移動分(今回は2×2)の出力を行う。
フィルターを通して新たな出力を得ているが、フィルターの当て方を見ると
周りのデータを取得していると言える。
そのため、周りの情報と繋がりを保ったまま、
情報を処理できることが、CNNの特徴と言える。
全結合層というのは、今までの学習で出てきたニューラルネットワークのことである。
畳み込み層との違いとしては、畳み込み層は次元の繋がりを保ったままの情報を出力していく。
それに対し、全結合層では、次元の繋がりをまとめて、本来ほしかった情報に出力するようになる。
●演算概念
畳み込み層ではフィルターを使うがニューラルネットワークで学習した、重みと考えて良い。
初期の状態では、乱数を入れる。
計算も簡単で、入力画像とフィルターの対応する所の積をとり、
1つにまとめることで次の層への出力となる。
先程の画像では、入力画像にフィルターを当てただけなので、
ニューラルネットワークでいう所の、x(入力) w(重み)を行ったことになる。
ニューラルネットワークではもう一つ +b というものがあった。
バイアスを足してあげるという処理だが、畳み込み層でも同じようにできる。
●パディング
先程の演算では4×4の入力画像を、2×2の出力画像に変換した。
仮にフィルターを2×2のフィルターにしても、出力画像は3×3となり、
画像が小さくなってしまう。
もし畳み込み層を大量に組み込んだニューラルネットワークがあれば、
画像はどんどん小さくなりすぎてしまう。
ここで登場するのが、パディングである。
パディングでは、縦のサイズ、横のサイズをあらかじめ、広げて、フィルターを通す。
そうすることで、出力画像は大きさを変えずに出力することができるようになる。
広げた場所の入力値については、全て0で埋めたり、一番近い所の数字を埋めたりできる。
●ストライド
先程までは、フィルターを1つずつ動かしていた。
これをストライド1という、ストライドを2に変えると、
フィルターは2つずつ動く事になる。
●チャンネル
チャンネルはフィルターの数を意味する。
イメージとしてはチャンネルを増やすことによって、様々な見方ができるようになる。
●畳み込み層ができた理由
全結合層では、例えば画像の場合、縦、横、チャンネルの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}
となり、後に計算が高速にできるようになる。
上の画像では、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
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の全結合層では、過学習を防ぐため、
ドロップアウトを行っている。