👻

ラビット・チャレンジ レポート 深層学習day1

2022/03/20に公開約5,200字

Section1:入力層〜中間層

1-1 要点

  • 入力層とは、処理されるデータの集まりのこと
  • 中間層とは、入力層のデータに重みをかけてバイアスを加えた総和に、活性化関数を適用したもの
  • 重みやバイアスなどのパラメータの値を変えることで、出力される値が様々に変わる

1-2 実装

# ネートワークを作成
def init_network():

    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])
    
    print_vec("重み1", network['W1'])
    print_vec("重み2", network['W2'])
    print_vec("バイアス1", network['b1'])
    print_vec("バイアス2", network['b2'])

    return network

# 順伝播
def forward(network, x):

    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']
    
    u1 = np.dot(x, W1) + b1
    z1 = functions.relu(u1)
    u2 = np.dot(z1, W2) + b2
    y = functions.softmax(u2)
    
    print_vec("総入力1", u1)
    print_vec("中間層出力1", z1)
    print_vec("総入力2", u2)
    print_vec("出力1", y)
    print("出力合計: " + str(np.sum(y)))

    return y, z1

# 訓練データ
x = np.array([[1.0, 5.0]])
# 目標出力
d = np.array([[0, 1]])
#  学習率
learning_rate = 0.01
network =  init_network()
y, z1 = forward(network, x)

1-3 確認テスト

  • 確認テスト1:図式に動物分類の実例を入れてみよう

  • 確認テスト2:数式をpythonで書け

u = np.dot(x, W1) + b
  • 確認テスト3:中間層の出力を定義しているソースを抜き出せ
z1 = functions.relu(u1)

Section2:活性化関数

2-1 要点

  • 活性化関数とは、閾値を境にして出力が切り替わる関数のこと
  • 活性化関数に「ステップ関数」を利用すると、単純パーセプトロンになる
  • ニューラルネットワークでは活性化関数に「シグモイド関数」や「ReLU関数」を利用している

2-2 実装

# シグモイド関数
def sigmoid(x):
    return 1/(1 + np.exp(-x))

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

# ステップ関数
def step_function(x):
    return np.where( x > 0, 1, 0) 

2-3 確認テスト

  • 確認テスト1:線形と非線形の違いを図に書いて簡易に説明せよ

  • 確認テスト2:ソースコードより該当する箇所を抜き出せ

z = functions.sigmoid(u)

Section3:出力層

3-1 要点

  • ニューラルネットワークでは、「分類問題」と「回帰問題」の両方に用いることが可能
  • 回帰問題では出力層の活性化関数に「恒等関数」を用いて、誤差関数に「平均二乗誤差」を用いる
  • 分類問題では出力層の活性化関数に2値分類では「シグモイド関数」を多値分類では「ソフトマックス関数」を用いて、誤差関数に「交差エントロピー誤差」を用いる

3-2 実装

# 平均二乗誤差
def mean_squared_error(d, y):
    return np.mean(np.square(d - y)) / 2

# クロスエントロピー
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

3-3 確認テスト

  • 確認テスト1:なぜ引き算でなく二乗するのか
    負の値の誤差を足し合わせると、総和が0になる可能性があるため

  • 確認テスト2:1/2はどういう意味を持つのか
    誤差逆伝播で実施する誤差関数の微分の計算を簡略化するため

  • 確認テスト3:①〜③の数式に該当するソースコードを示し、一行ずつ説明せよ

① np.exp(x) / np.sum(np.exp(x))
② np.exp(x)
③ np.sum(np.exp(x))

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)
        return y.T

    x = x - np.max(x) # オーバーフロー対策
    return np.exp(x) / np.sum(np.exp(x))
  • 確認テスト4:①〜②の数式に該当するソースコードを示し、一行ずつ説明せよ

① -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
② -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7))

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]
    # 1e-7を足す理由はlog(0)のとき、-∞に発散するのは防止するため
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size

Section4:勾配降下法

4-1 要点

  • ニューラルネットワークは誤差関数が最小値となる最適なパラメータ(重みとバイアス)を学習的に見つける
  • しかし、一般的に誤差関数は複雑で最小値を見つけることは難しいため、勾配を利用して最小値に近づけるように探すことが勾配降下法である

4-2 実装

勾配降下法を利用するためには、誤差逆伝播法を用いるため、実装演習はSection5の実装で行う

4-3 確認テスト

  • 確認テスト1:該当するソースコードを探せ
network[key]  -= learning_rate * grad[key]
grad = backward(x, d, z1, y)
  • 確認テスト2:オンライン学習とはなにか
    学習データの一つを取り出してパラメータの更新をする手法

  • 確認テスト3:数式の意味を図に書いて説明せよ

Section5:誤差逆伝播法

5-1 要点

  • 算出された誤差を、出力層側から順に微分し、前の層前の層へと伝播
  • 最小限の計算で各パラメータでの微分値を解析的に計算する手法
  • 計算結果(=誤差)から微分を逆算することで、不要な再帰的計算を避けて微分を算出できる

5-2 実装

# 誤差逆伝播
def backward(x, d, z1, y):

    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)
        
    print_vec("偏微分_dE/du2", delta2)
    print_vec("偏微分_dE/du2", delta1)

    print_vec("偏微分_重み1", grad["W1"])
    print_vec("偏微分_重み2", grad["W2"])
    print_vec("偏微分_バイアス1", grad["b1"])
    print_vec("偏微分_バイアス2", grad["b2"])

    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)

5-3 確認テスト

  • 確認テスト1:既に行った計算結果を保持しているソースコードを抽出せよ
delta2 = functions.d_mean_squared_error(d, y)
  • 確認テスト2:2つの空欄に該当するソースコードを探せ
delta2 = functions.d_mean_squared_error(d, y)
grad['W2'] = np.dot(z1.T, delta2)

Discussion

ログインするとコメントできます