0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ラビットチャレンジ レポート(その9 深層学習Day2 Section1)

Posted at

深層学習Day2

Section1 勾配消失問題

<確認テスト>
連鎖律の原理を用い、$\frac{dz}{dx}$を求めよ。

z = t^2 \\
t = x + y \\
\frac{dz}{dx} = \frac{dz}{dt} \frac{dt}{dx} = 2t = 2(x+y)

勾配消失問題とは、多層ニューラルネットワークにおいて、誤差逆伝播を進めていくにつれて、勾配がどんどん緩やかになっていき、勾配降下法におけるパラメータの更新量が小さくなりすぎてしまい、重みが収束しなくなる現象。

特に活性化関数でよく使用されるSigmoid関数などは、大きい/小さい値に対して勾配が0に近づくので、勾配消失問題を引き起こしやすいという問題点があった。

<確認テスト>
シグモイド関数を微分した時、入力値が0の時に最大値を取る。その値として正しいものを選択肢から選べ。

⇒実際にシグモイド関数を微分して、0を代入してみる。

\sigma(x) = \frac{1}{1+\exp(-x)} \\

\frac{d \sigma}{dx} = (1- \sigma(x))\sigma(x) \\
\therefore (1-0.5)*0.5 = 0.25

よって、答えは(2)0.25

勾配消失問題に対する一般的な解決方法は、

  • 活性化関数の選択
  • 重みの初期値設定
  • バッチ正規化

の3つ。

活性化関数の選択

層が深いときに勾配消失問題を起こしやすいSigmoid関数に代わってよく用いられるのが、ReLU関数。
数式で書くと以下。

f(x)=
\begin{cases}
x \ \ (x>0) \\
0 \ \ (x\le 0)
\end{cases}

サンプルコードは以下

import numpy as np

def relu(x):
    return np.maximum(0,x)

このReLU関数を用いることによって、勾配消失問題に対処でき、微分計算の簡略化もできる。

重みの初期値設定

重みの初期値を、活性化関数に合わせて変える。
使用されるのは主に2種類。

Xavierの初期値

  • 重みの要素を、前の層のノード数の平方根で除算した値にする。
  • 使用する活性化関数は以下。
    • ReLU関数
    • Sigmoid関数
    • 双曲線正接関数

Heの初期値

  • 重みの要素を、前の層のノード数の平方根で除算した値に対し$\sqrt{2}$を掛け合わせた値にする。
  • 使用する活性化関数は以下。
    • ReLU関数

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

⇒入力値に対して0をかけて伝播していくことになるので、全く学習が進まない。

バッチ正規化

ミニバッチ単位で入力値のデータの偏りを抑制する方法。
活性化関数に値を渡す前後に、バッチ正規化の処理を含んだ層を加える。

確認テスト
一般的に考えられるバッチ正規化の効果を2点挙げよ。

⇒以下の2点

  • 安定して学習できるようになる
  • 計算の効率化

実装演習

MNISTデータセットの分類のためのニューラルネットワークを学習させてみる。

import numpy as np
from commonDSpackage import layers
from collections import OrderedDict
from commonDSpackage import functions
from commonDSpackage.mnist import load_mnist
import matplotlib.pyplot as plt

# mnistをロード
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)
train_size = len(x_train)

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

# 重み初期値補正係数
wieght_init = 0.01
# 入力層サイズ
input_layer_size = 784
# 中間層サイズ
hidden_layer_1_size = 40
hidden_layer_2_size = 20

# 出力層サイズ
output_layer_size = 10
# 繰り返し数
iters_num = 2000
# ミニバッチサイズ
batch_size = 100
# 学習率
learning_rate = 0.1
# 描写頻度
plot_interval=10

# 初期設定
def init_network():
    network = {} 
    network['W1'] = wieght_init * np.random.randn(input_layer_size, hidden_layer_1_size)
    network['W2'] = wieght_init * np.random.randn(hidden_layer_1_size, hidden_layer_2_size)
    network['W3'] = wieght_init * np.random.randn(hidden_layer_2_size, output_layer_size)
    
    #xavierの初期値
    network['W1'] = np.random.randn(input_layer_size, hidden_layer_1_size) / (np.sqrt(input_layer_size))
    network['W2'] = np.random.randn(hidden_layer_1_size, hidden_layer_2_size) / (np.sqrt(hidden_layer_1_size))
    network['W3'] = np.random.randn(hidden_layer_2_size, output_layer_size) / (np.sqrt(hidden_layer_2_size))

    network['b1'] = np.zeros(hidden_layer_1_size)
    network['b2'] = np.zeros(hidden_layer_2_size)
    network['b3'] = np.zeros(output_layer_size)

    return network

# 順伝播
def forward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    
    #活性化関数の変更
    #hidden_f = functions.sigmoid
    hidden_f = functions.relu
    
    u1 =  np.dot(x, W1) + b1
    z1 = hidden_f(u1)
    u2 =  np.dot(z1, W2) + b2
    z2 = hidden_f(u2)
    u3 =  np.dot(z2, W3) + b3
    y = functions.softmax(u3)
 
    return z1, z2, y

# 誤差逆伝播
def backward(x, d, z1, z2, y):
    grad = {}
    
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    
    #活性化関数の変更
    hidden_d_f = functions.d_relu
    
    last_d_f = functions.d_softmax_with_loss
    
    
    # 出力層でのデルタ
    delta3 = last_d_f(d, y)
    # b3の勾配
    grad['b3'] = np.sum(delta3, axis=0)
    # W3の勾配
    grad['W3'] = np.dot(z2.T, delta3)
    # 2層でのデルタ
    delta2 = np.dot(delta3, W3.T) * hidden_d_f(z2)
    # b2の勾配
    grad['b2'] = np.sum(delta2, axis=0)
    # W2の勾配
    grad['W2'] = np.dot(z1.T, delta2)
    # 1層でのデルタ
    delta1 = np.dot(delta2, W2.T) * hidden_d_f(z1)
    # b1の勾配
    grad['b1'] = np.sum(delta1, axis=0)
    # W1の勾配
    grad['W1'] = np.dot(x.T, delta1)

    return grad

# パラメータの初期化
network = init_network()

accuracies_train = []
accuracies_test = []

# 正答率
def accuracy(x, d):
    z1, z2, y = forward(network, x)
    y = np.argmax(y, axis=1)
    if d.ndim != 1 : d = np.argmax(d, axis=1)
    accuracy = np.sum(y == d) / float(x.shape[0])
    return accuracy

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]


    
    z1, z2, y = forward(network, x_batch)
    grad = backward(x_batch, d_batch, z1, z2, y)

    if (i+1)%plot_interval==0:
        accr_test = accuracy(x_test, d_test)
        accuracies_test.append(accr_test)
        
        accr_train = 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))

    # パラメータに勾配適用
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        network[key]  -= learning_rate * grad[key]


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、初期値ランダム
深層学習Day2-1.png

②ReLU、初期値ランダム
深層学習Day2-2.png

③ReLU、Xavierの初期値使用
深層学習Day2-3.png

この実験結果からも、活性化関数の変更と重みの初期値の変更は、共に効果があることが分かった。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?