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.

ラビットチャレンジ レポート(その8 深層学習day1)

Posted at

深層学習Day1

ニューラルネットワークの全体像

<確認テスト>
ディープラーニングは、結局何をやろうとしているか2行以内で述べよ。
また、次の中のどの値の最適化が最終目的か、全て選べ。

  • ディープラーニングでやろうとしていることは、明示的なプログラムを用いずに、多数の中間層を持つネットワークを用いて、入力値から出力値に変換するモデルを構築すること。
  • 最終的に最適化したいのは、重み・バイアスの2つ。

<確認テスト>
次のネットワークを紙に書け。
入力層:2ノード1層
中間層:3ノード2層
出力層:1ノード1層

ニューラルネットワーク.png

Section1 入力層~出力層

ニューラルネットワークの入力層とは、ニューラルネットワークへの入力データ。(=モデル自体への入力値)
この入力値に重みをかけてバイアスを加えた値が、次の層(中間層)へと渡される。
最後の層に渡されたものが、出力値として出てくる(出力層)。
中間層や出力層では、活性化関数と呼ばれるものをかけて値を変換する(後述)。

<確認テスト>
図式に動物分類の実例を入れてみる。

動物分類.png

<確認テスト>
この数式をpythonで書け

import numpy as np
u = np.dot(x, w) + b

<確認テスト>
1-1のファイルから、中間層の出力を定義しているソースを抜き出せ。

z = functions.relu(u)
print_vec("中間層出力", z)

実装演習

単層・単ユニットのニューラルネットワークを実装

import numpy as np
from common import functions

# 重み
W = np.array([[0.1], [0.2]])

# バイアス
b = 0.5

# 入力値
x = np.array([2, 3])

# 総入力
u = np.dot(x, W) + b

# 中間層出力
z = functions.relu(u)

Section2 活性化関数

ニューラルネットワークにおいて、次の層への出力の大きさを決める非線形の関数。
入力値の値によって、次の層への信号のON/OFFや強弱を定める働きを持つ。

<確認テスト>
線形と非線形の違いを図に書いて簡易に説明せよ。

線形と非線形.png

グラフが直線になるものが線形、曲線になるものが非線形。

  • 中間層用の活性化関数

    • ReLU
    • Sigmoid
    • ステップ関数
  • 出力層用の活性化関数

    • Softmax
    • 恒等関数
    • Sigmoid

実装演習

活性化関数の実装

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

# ステップ関数
def step(x):
    if x>0:
        return 1
    else:
        return 0

# Sigmoid関数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

<確認テスト>
配布されたソースコードより該当する箇所を抜き出せ。

z = functions.sigmoid(u)

Section3 出力層

モデルに入力データを入れたときの推定結果を出力する層。
分類であれば、各クラスに属する確率を出力。回帰であれば、目的変数の推定値を出力、といった具合。
この出力層の結果を用いて、モデルの精度を評価する。そのために使われる関数が誤差関数。
一般的には、以下の式で示される平均二乗誤差がよく用いられる。

E_n(\boldsymbol{w}) = \frac{1}{2} \Sigma_{j=1}^{l}(y_j-d_j)^2 = \frac{1}{2}||\boldsymbol{y}-\boldsymbol{d}||^2

ここで、$\boldsymbol{y}$は出力結果、$\boldsymbol{d}$は正解データを表している。

<確認テスト>
なぜ引き算ではなく二乗するのか答えよ
上式の1/2はどういう意味を持つか答えよ

⇒各データでの推定結果と正解の誤差は、正負両方になり得るので、打ち消しあって誤差が小さくならないように、2乗して総和を取ることで、すべてのデータに対する誤差を正しく計算するため。
⇒1/2は誤差逆伝播する際の微分計算を簡単にするためのもので、本質的な意味はない。

<確認テスト>
①~③の数式に該当するソースコードを示し、1行ずつ処理の説明をせよ。
以下のコード中に示す。

def softmax(x): #①の部分 関数の出力
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0) #分子が②、分母が③。この値が0~1に収まるため、確率とみなせる。
        return y.T

    x = x - np.max(x) # オーバーフロー対策
    return np.exp(x) / np.sum(np.exp(x))

<確認テスト>
①~②の数式に該当するソースコードを示し、1行ずつ処理の説明をせよ。
以下のコード中に示す。

def cross_entropy_error(d, y): #①の部分。関数の出力
    if y.ndim == 1:
        d = d.reshape(1, d.size)
        y = y.reshape(1, y.size)
        
    # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
    if d.size == y.size:
        d = d.argmax(axis=1)
             
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size #②の部分。

実装演習

ネットワークを作成(誤差逆伝播も含む)

def init_network():
    print("##### ネットワークの初期化 #####")

    network = {}
    network['W1'] = np.array([
        [0.1, 0.3, 0.5],
        [0.2, 0.4, 0.6]
    ])

    network['W2'] = np.array([
        [0.1, 0.4],
        [0.2, 0.5],
        [0.3, 0.6]
    ])

    network['b1'] = np.array([0.1, 0.2, 0.3])
    network['b2'] = np.array([0.1, 0.2])

    return network

# 順伝播
def forward(network, x):
    print("##### 順伝播開始 #####")

    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']

    u1 = np.dot(x, W1) + b1
    # 中間層の活性化関数
    # ReLU関数
    z1 = functions.relu(u1)
    u2 = np.dot(z1, W2) + b2
    y = functions.softmax(u2)

    print("出力合計: " + str(np.sum(y)))

    return y, z1

# 誤差逆伝播
def backward(x, d, z1, y):
    print("\n##### 誤差逆伝播開始 #####")

    grad = {}

    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']
    #  出力層でのデルタ
    delta2 = functions.d_sigmoid_with_loss(d, y)
    #  b2の勾配
    grad['b2'] = np.sum(delta2, axis=0)
    #  W2の勾配
    grad['W2'] = np.dot(z1.T, delta2)
    #  中間層でのデルタ
    delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1)
    # b1の勾配
    grad['b1'] = np.sum(delta1, axis=0)
    #  W1の勾配
    grad['W1'] = np.dot(x.T, delta1)

    return grad

# 訓練データ
x = np.array([[1.0, 5.0]])

# 目標出力
d = np.array([[0, 1]])

#  学習率
learning_rate = 0.01
network =  init_network()
y, z1 = forward(network, x)

# 誤差
loss = functions.cross_entropy_error(d, y)

grad = backward(x, d, z1, y)
for key in ('W1', 'W2', 'b1', 'b2'):
    network[key]  -= learning_rate * grad[key]

# ここまでで重み・バイアスが更新されている。

Section4 勾配降下法

深層学習における目的は、ネットワークにデータを学習させることで、誤差を最小にするネットワークを作成すること。
よって、誤差を最小にするようなパラメータを求めるために行うのが、勾配降下法。
主に以下の3種類。

  • 勾配降下法
  • 確率的勾配降下法
  • ミニバッチ勾配降下法

単純な勾配降下法は、全データをモデルに与えて得られた誤差を計算する。

<確認テスト>
勾配降下法に該当するソースコードを探す。

network[key] -= learning_rate * grad[key]
grad = backward(x, d, z1, y)

確率的勾配降下法では、ランダムに抽出したサンプルの誤差を用いて誤差を計算する。この方法のメリットは、

  • データが冗長な場合の計算コストの軽減
  • 望まない局所極小解に収束するリスクの軽減
  • オンライン学習ができる

ことが挙げられる。

<確認テスト>
オンライン学習とは何か。2行でまとめよ。

⇒モデルにその都度データを与えて学習させる学習手法。学習データが入ってくる都度パラメータを更新し、学習を進めていく。

ミニバッチ勾配降下法では、ランダムに分割したデータの集合(ミニバッチ)に属するサンプルの平均誤差を取る。
これによるメリットは、

  • 確率的勾配降下法のメリットを損なわず、計算機の計算資源を有効活用できること。

<確認テスト>
$\boldsymbol{w}^{t+1}=\boldsymbol{w}^{t}-\varepsilon \nabla E_t$
この数式の意味を図に書いて説明せよ。

⇒重み更新の数式。

重み更新.png

実装演習

実際に、

f(x,y) = x^2 + y^2

として、$(3.0, 4.0)$を初期位置として勾配法によりパラメータを更新してみる。

import numpy as np

# 関数の定義
def function(x):
    return x[0]**2 + x[1] **2

# 数値微分
def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]
        # f(x + h)の計算
        x[idx] = tmp_val + h
        fxh1 = f(x)

        # f(x - h)の計算
        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        # 値を元に戻す
        x[idx] = tmp_val

    return grad

# 勾配法による最適化
def gradienr_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x

    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x

init_x = np.array([3.0,4.0])
gradienr_descent(function, init_x, lr=0.1)

この結果は、

array([6.11110793e-10, 8.14814391e-10])

となり、限りなく0に近づいていることがわかる。

Section5 誤差逆伝播法

ニューラルネットワークにおいて、各重み(パラメータ)の微分値を直接計算しようとすると、計算量が膨大になってしまう。そこで利用するのが誤差逆伝播法。
算出された誤差を、出力層側から順に微分し、前の層へと伝播させていく手法。
最小限の計算で各パラメータでの微分値を解析的に計算していく。

<確認テスト>
誤差逆伝播法では不要な再帰的処理を避けることができる。
既に行った計算結果を保持しているソースコードを抽出せよ。

#  出力層でのデルタ
delta2 = functions.d_sigmoid_with_loss(d, y)
#  b2の勾配
grad['b2'] = np.sum(delta2, axis=0)
#  W2の勾配
grad['W2'] = np.dot(z1.T, delta2)
#  中間層でのデルタ
delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1)

<確認テスト>
2つの空欄に該当するソースコードを探せ

delta2 = functions.d_mean_squared_error(d, y)
grad['W2'] = np.dot(z1.T, delta2)

実装演習

(Section3の実装演習に含まれる)

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?