0
0

深層学習 day1

Last updated at Posted at 2022-02-19

1.はじめに

 深層学習day1について学習した内容をまとめました.

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

 ・脳の神経回路網をアルゴリズム化したもの
 ・入力層,中間層,出力層で構成されている.
 ・中間層が多数になるとDNN(Deep Neural Network)と呼ぶ.
 ・回帰や分類に用いることができる.

確認テスト1

 問:ディープラーニングはなにをしようとしているか?

 解答:多数の中間層を設けることで,入力に対する欲しい出力を得るための近似関数を作成する.

確認テスト2

 問:次の中のどの値の最適化が最終目的か.全て選べ.
 ①入力値[X] ②出力値[Y] ③重み[W] ④バイアス[b]
 ⑤総入力[u] ⑥中間層入力[z] ⑦学習率[ρ]

 解答:②重みと④バイアス

確認テスト3

 問:次のネットワークを紙にかけ.
 入力層︓2ノード1層,中間層︓3ノード2層,出力層︓1ノード1層

 解答:
深層学習1_確認テスト_DNN手書き.jpg

3.入力層~中間層

 ①入力値[X] ②出力値[Y] ③重み[W] ④バイアス[b] ⑤総入力[u] ⑥出力[z] ⑦活性化関数[f]とし,
 入力層のノード数が4,中間層のノード数が1の時,
 中間層の総入力uの式は次のようになる.

\boldsymbol{w}=[w_1,w_2,w_3,w_4] \\
\boldsymbol{x}=[x_1,x_2,x_3,x_4] \\
u =\boldsymbol{wx} =w_1x_1+w_2x_2+w_3x_3+w_4x_4+b

確認テスト4

 問:上の式をpythonで書け.

 解答:下記

import numpy as np

# 入力
x = np.random.rand(4)
print("入力", x)

# 重み
W = np.random.rand(4)
print("重み", W)

# バイアス
b = np.random.rand(1)
print("バイアス", b)

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

演習結果
image.png

確認テスト5

 問:1-1のファイルから中間層の出力を定義しているソースを抜き出せ.

 解答:下記

# 2層の総入力
u2 = np.dot(z1, W2) + b2
    
# 2層の総出力
z2 = functions.relu(u2)

4.活性化関数

 ニューラルネットワークにおいて,次の層への出力の大きさを決める非線形の関数.
 入力値の値によって,次の層への信号のON/OFFや強弱を定める働きをもつ.
 以下の3つは主に中間層に用いられる(シグモイド関数は出力層にも用いられる).

ステップ関数

→しきい値を超えたら発火する関数であり,出力は常に1か0.
→パーセプトロン(ニューラルネットワークの前身)で利用された関数.
→0~1間の間を表現できず,線形分離可能なものしか学習できなかった(微分も不可).

f(x) = \left\{
\begin{array}{ll}
1 & (x \geq 0) \\
0 & (x \lt 0)
\end{array}
\right.

image.png

シグモイド関数

 →非線形で微分可能な関数.
 →パーセプトロン(ニューラルネットワークの前身)で利用された関数.
 →0~1の間を緩やかに変化する関数で,ステップ関数ではON/OFFしかない状態に対し,
  信号の強弱を伝えられるようになった.
 →大きな値では出力の変化が微小なため,勾配消失問題を引き起こす事があった.

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

image.png

ReLU関数

 →よく使われている活性化関数
 →入力が負であれば0,正であればそのままの値を出力する.
 →勾配消失問題の回避とスパース化に貢献することで良い成果をもたらしている.

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

image.png

確認テスト6

 問:線形と非線形の違いを図にかいて簡易に説明せよ.

 解答:下記に線形な関数のグラフを示す.
 線形関数は次のような条件を満たす関数である.
 それぞれ上から順に加法性,斉次性と呼ぶ.
 逆に非線形関数は満たさない関数である.

\begin{align}
f(x+y) &= f(x) + f(y) \\ \\
f(kx) &= kf(x)
\end{align}

image.png

確認テスト7

 問:配布されたソースコードより該当する箇所を抜き出せ.

 解答:下記

z1 = functions.sigmoid(u)

演習:活性化関数それぞれのグラフを描画するために作成したコード

import matplotlib.pyplot as plt

def step(x):
  y = np.where(x<=0, 0, 1)
  return y

def sigmoid(x):
  y = 1./(1. + np.exp(-x))
  return y

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

# xの値を生成
x = np.arange(-10, 10, 0.01)

# xに対するyを求める
y = relu(x)

# グラフ描画
plt.plot(x,y)
plt.xlabel("x")
plt.ylabel("y")
plt.show()

5.出力層

 各ラベルの予測確率,または回帰の値を出力する層である.

誤差関数

 正解データ(ラベル)と出力値(予測値)の誤差を示す関数

例:二乗誤差関数

E(w) = \frac{1}{2}\sum_{i=1}^{N}(\hat{y_i}-y_i)^2

確認テスト8

 問:なぜ引き算でなく二乗するか述べよ.また,1/2はどういう意味を持つか述べよ.

 解答:二乗しないと正負の誤差同士で打ち消しあってしまうから.
  →二乗することで誤差の符号が正の値になるようにする.
  1/2は微分したときに出てくる2を約分して計算を楽にするためにある.

出力層における活性化関数

 タスクに応じて用いる活性化関数は異なる.
 ・回帰 → 恒等写像

f(u) = u

・二値分類 → シグモイド関数(ソフトマックス関数でも機能する)
・多値分類 →ソフトマックス関数

f(i,u) = \frac{e^u_i}{\sum_{k=1}^{K}e^{u_k}}

分類問題における誤差関数

交差エントロピー誤差関数

E(w) = -\sum_{i=1}^{N}y_i\log{\hat{y_i}}

確認テスト9

 問:ソフトマックス関数のソースコードを1行ずつ説明せよ.

 解答:下記

# softmaxという名前で関数定義
def softmax(x):
    # もしxの次元数が2であれば
    if x.ndim == 2:
        # xを転置する
        x = x.T
        # xの最大値を引く
        x = x - np.max(x, axis=0)
        # 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入   
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        # yを転置して返す
        return y.T
    # xの最大値を引く
    x = x - np.max(x)
    # 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入
    return np.exp(x) / np.sum(np.exp(x))

確認テスト10

 問:交差エントロピー誤差関数のソースコードを1行ずつ説明せよ.

 解答:下記

# d(ラベル),y(出力値)として関数を定義
def cross_entropy_error(d, y):
    #もしyの次元数が1であれば
    if y.ndim == 1:
        # d,yそれぞれの形状を(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)
    # yの形状の0番目の数を変数batch_sizeに代入する.
    batch_size = y.shape[0]
    # 交差エントロピー誤関数の値を返す(バッチサイズで平均をとっている).
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size

演習(多値分類)

# 多クラス分類

import numpy as np

def print_vec(text, vec):
    print("*** " + text + " ***")
    print(vec)
    #print("shape: " + str(x.shape))
    print("")

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

def softmax(x):
    # もしxの次元数が2であれば
    if x.ndim == 2:
        # xを転置する
        x = x.T
        # xの最大値を引く
        x = x - np.max(x, axis=0)
        # 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入   
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        # yを転置して返す
        return y.T
    # xの最大値を引く
    x = x - np.max(x)
    # 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入
    return np.exp(x) / np.sum(np.exp(x))

def cross_entropy_error(d, y):
    #もしyの次元数が1であれば
    if y.ndim == 1:
        # d,yそれぞれの形状を(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)
    # yの形状の0番目の数を変数batch_sizeに代入する.
    batch_size = y.shape[0]
    # 交差エントロピー誤関数の値を返す(バッチサイズで平均をとっている).
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size

# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
    print("##### ネットワークの初期化 #####")

    #_各パラメータのshapeを表示
    #_ネットワークの初期値ランダム生成
    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.7, 1.0],
        [0.2, 0.5, 0.8, 1.1],
        [0.3, 0.6, 0.9, 1.2]
    ])
    network['b1'] = np.array([0.1, 0.2, 0.3])
    network['b2'] = np.array([0.1, 0.2, 0.3, 0.4])
    
    print_vec("重み1", network['W1'] )
    print_vec("重み2", network['W2'] )
    print_vec("バイアス1", network['b1'] )
    print_vec("バイアス2", network['b2'] )

    return network

# プロセスを作成
# x:入力値
def forward(network, x):
    
    print("##### 順伝播開始 #####")
    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']
    
    # 1層の総入力
    u1 = np.dot(x, W1) + b1

    # 1層の総出力
    z1 = relu(u1)

    # 2層の総入力
    u2 = np.dot(z1, W2) + b2
    
    # 出力値
    y = 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., 2.])

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

# ネットワークの初期化
network =  init_network()

# 出力
y, z1 = forward(network, x)

# 誤差
loss = cross_entropy_error(d, y)

## 表示
print("\n##### 結果表示 #####")
print_vec("出力", y)
print_vec("訓練データ", d)
print_vec("誤差",  loss)

実装結果

##### ネットワークの初期化 #####
*** 重み1 ***
[[0.1 0.3 0.5]
 [0.2 0.4 0.6]]

*** 重み2 ***
[[0.1 0.4 0.7 1. ]
 [0.2 0.5 0.8 1.1]
 [0.3 0.6 0.9 1.2]]

*** バイアス1 ***
[0.1 0.2 0.3]

*** バイアス2 ***
[0.1 0.2 0.3 0.4]

##### 順伝播開始 #####
*** 総入力1 ***
[0.6 1.3 2. ]

*** 中間層出力1 ***
[0.6 1.3 2. ]

*** 総入力2 ***
[1.02 2.29 3.56 4.83]

*** 出力1 ***
[0.01602796 0.05707321 0.20322929 0.72366954]

出力合計: 1.0

##### 結果表示 #####
*** 出力 ***
[0.01602796 0.05707321 0.20322929 0.72366954]

*** 訓練データ ***
[0 0 0 1]

*** 誤差 ***
0.3234202933601941

6.勾配降下法

 深層学習の目的は,誤差関数Eを最小にすることであった.
 →勾配降下法:誤差関数Eが最小になるようなパラメータを探索し,最適化する.
 全サンプルの平均誤差を用いる.
 式は以下になる.

w^{t+1} = w^t - \eta\frac{\partial E}{\partial w}

確認テスト11

 問:勾配降下法の式に該当するソースコードを探してみよう.

 解答:下記

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

確率的勾配降下法(SGD:Stochastic Gradient Descent)

 勾配降下法では,全サンプルを学習に利用していたが,
 SGDでは,全サンプルの中からランダムに選ばれた誤差のみを学習に用いる.
 メリットとして以下が挙げられる.
 ・データが冗⻑な場合の計算コストの軽減
 ・望まない局所的最適解に収束するリスクの軽減
 ・オンライン学習ができる

ミニバッチ勾配降下法

 オンライン学習とバッチ学習の中間的な特徴を持つ学習手法
 →ランダムに分割したデータの集合(ミニバッチ)に属するサンプルの平均誤差を利用
 確率的勾配降下法のメリットを損なわず,計算機の計算資源を有効利用できる.
 →CPUを利用したスレッド並列化やGPUを利用したSIMD並列化
 式は以下になる.

\begin{align}
w^{t+1} &= w^t - \eta\frac{\partial E}{\partial w} \\
E_t &= \frac{1}{N_t}\sum_{n\in{D_t}}^{} E_n \\
N_t &= |D_t|
\end{align}

確認テスト12

 問:オンライン学習とは何か2行でまとめよ.

 解答:学習データが入ってくるたびにパラメータを更新し,学習を進めていく方法である.
  1回の学習あたりのコストが低い,外れ値の影響が受けやすいといった特徴がある.

確認テスト13

 問:勾配降下法の式の意味を図に書いて説明せよ.

 解答:図を以下に示す.
 傾きが負であれば重みを増やし,傾きが正であれば重みを減らすことで
 誤差関数Eを最小化する(どれぐらい変化させるかは学習率で調整).

image.png

7.誤差逆伝搬法

 算出された誤差を出力層側から順に微分し,後ろの層へと伝播する.
 →最小限の計算で各パラメータでの微分値を解析的に計算する手法
 微分時には数値微分が用いられる.
 式は以下に示す.

\frac{\partial E}{\partial w_m} \approx \frac{E(w_m+h)-E_m(w_m-h)}{2h}

 これに加え,チェインルール(連鎖率)を用いることで,
 不要な再帰的計算を避けて微分を算出できる.

image.png

確認テスト14

 問:誤差逆伝播法では,不要な再帰的処理を避ける事が出来る.
 既に行った計算結果を保持しているソースコードを抽出せよ.

 解答:下記

delta2 = functions.d_mean_squarred_error(d, y)
delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)

確認テスト15

 問:以下の図
image.png

 解答:下記

delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)
grad['W1'} = np.dot(x.T, delta1)

8.参考

ニューラルネットワークや勾配降下法,誤差逆伝搬法について:
絶対に理解させる誤差逆伝播法【深層学習】
https://www.youtube.com/watch?v=0itH0iDO8BE

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