search
LoginSignup
3

More than 1 year has passed since last update.

posted at

updated at

Organization

ゼロから作るDeep Learning 2 自然言語処理編 1.3 まとめ

ゼロから作るDeep Learning 2 自然言語処理編 1.3 まとめ

by ohakutsu
1 / 29

これはなに

社内の勉強会で発表するときに使った資料です


1.3 ニューラルネットワークの学習


1.3.1 損失関数

ニューラルネットワークで"良い推論"をするためには、最適なパラメータを設定しなければならない

ニューラルネットワークの学習には、学習がどれだけ上手くいっているかを知るための指標が必要
損失という

ニューラルネットワークの損失を求めるために使うのが損失関数


  • 損失関数
    • 2乗誤差(ゼロから始めるDeep Learning 1 にあった)
    • 交差エントロピー誤差
      • 多クラス分類に用いられることが多い

今節では、損失を求めるのに以下のようなレイヤ構成にする

1e49abb0-2cb2-dd5d-d881-5ee1cf946b46.png


Softmax と Cross Entropy Error のレイヤをまとめて、

bd2831b7-351e-9951-be8e-115321114a48.png

Softmax with Loss とする


Softmaxとはなんぞや
ソフトマックス関数

y_k =  \frac {exp(s_k)}{\displaystyle \sum _{i=1}^{n} exp(s_i)}
  • 特徴
    • 出力が0.0〜1.0の実数
    • 出力をすべて足すと1.0になる
      • 確率として解釈することができる

Cross Entropy Errorとはなんぞや
交差エントロピー誤差

L = - \sum_{k}t_k\space log\space y_k
  • 特徴
    • t はone_hot表現(0か1)の教師ラベルなので、ラベルが1のときの自然対数を返すだけ
    • yが0に近いほど小さくなり、1に近いほど0に収束する

logx.png


ミニバッチ処理を考慮して、

L = - \frac{1}{N} \sum_{n}\sum_{k}t_{nk}\space log\space y_{nk}

を使う


1.3.2 微分と勾配

ニューラルネットワークの学習の目標は、損失をできるだけ小さくするパラメータを見つけること
ここで重要になるのが微分勾配である


微分
→ある瞬間の変化の量
@ohakutsu 中学数学からはじめるAI(人工知能)のための数学入門 - YouTube でわかります

y = f(x)

の x に関する y の微分は

\frac{dy}{dx}

と表せる


変数が複数あっても微分を求めることができる
x をベクトルとして、

L = f(x)
\frac{\partial L}{\partial x} = \left( \frac{\partial L}{\partial x_1}, \frac{\partial L}{\partial x_2}, ..., \frac{\partial L}{\partial x_n} \right)

ベクトルの各要素の微分をまとめたものを勾配と呼ぶ

行列の場合も同様に勾配を考えることができる
W を m×n 行列として、

L = g(W)
\frac{\partial L}{\partial W} = \left(
  \begin{array}{ccc}
    \frac{\partial L}{\partial w_{11}} & \cdots & \frac{\partial L}{\partial w_{1n}} \\
    \vdots & \ddots & \\
    \frac{\partial L}{\partial w_{m1}} & & \frac{\partial L}{\partial w_{mn}}
  \end{array}
\right)

1.3.3 チェインルール

学習時におけるニューラルネットワークは、学習データを与えると損失を出力する
各パラメータに関する損失の勾配が得られると、それを使ってパラメータの更新をすることができる

ニューラルネットワークの勾配をどのように求めるか
誤差逆伝播法

誤差逆伝播法を理解する上でキーとなるのがチェインルール(連鎖律)


  • チェインルール
    • 合成関数に関する微分の法則

↓こんなやつ

y = f(x) \\
z = g(y) \\

書き換えて

z = g(f(x)) \\

x に関する z の微分は

\frac{\partial z}{\partial x} = \frac{\partial z}{\partial y}\frac{\partial y}{\partial x}

どれだけ複雑な関数を扱うとしても、その微分は個別の関数の微分で求めることができる


1.3.4 計算グラフ

計算を視覚的に表すもの

例)

z = x + y

097c0e7d-5245-f3a6-7445-c9e8b198efd7.png
e22cfed3-f846-7607-fb78-0c8299b16700.png

逆方向の伝搬が「逆伝搬」

6981c2fe-1898-d29b-0ba1-aee35c912493.png


以下、代表的な演算ノード

  • 加算ノード
    30e860a6-3518-5221-b139-f82bb44febe2.png

  • 乗算ノード
    951e8bee-fef4-df5b-d9a2-d7de611e25bd.png

  • 分岐ノード
    e4686bbe-ad63-968c-c415-7ee499f24a39.png

  • Repeat ノード
    03e79e0c-7f3e-bbf4-b838-232a872dc3eb.png

  • Sum ノード
    99fe71b2-03f3-1de4-0a7a-5c416584f6ee.png

  • MatMul ノード
    744aada9-deea-379a-a8de-eb76267e1066.png


1.3.5 勾配の導出と逆伝搬の実装

各レイヤを実装していく

bd2831b7-351e-9951-be8e-115321114a48.png


Sigmoidレイヤ

シグモイド関数は

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

シグモイド関数の微分は

\frac{\partial y}{\partial x} = y(1 - y)

Sigmoidレイヤの計算グラフは

c725a5fc-e5b6-c377-ff33-df7bc4451a17.png


Pythonで実装すると

class Sigmoid:
  def __init__(self):
    self.params, self.grads = [], []
    self.out = None

  def forward(self, x):
    out = 1 / (1 + np.exp(-x))
    self.out = out
    return out

  def backward(self, dout):
    dx = dout * (1.0 - self.out) * self.out
    return dx

Affineレイヤ

Affineレイヤの順伝搬は

y = np.dot(x, W) + b

バイアスの加算がブロードキャストされている

df1bab77-9c16-6624-2670-7e30a02b4abd.png


Pythonで実装すると

class Affine:
  def __init__(self, W, b):
    self.params = [W, b]
    self.grads = [np.zeros_like(W), np.zeros_like(b)]
    self.x = None

  def forward(self, x):
    W, b = self.params
    out = np.dot(x, W) + b
    self.x = x
    return out

  def backward(self, dout):
    W, b = self.params
    dx = np.dot(dout, W.T)
    dW = np.dot(self.x.T, dout)
    db = np.sum(dout, axis=0)

    self.grads[0][...] = dW
    self.grads[1][...] = db
    return dx

Softmax with Lossレイヤ

f9240a28-a5d5-0b1f-03aa-e22edfed5c13.png

class SoftmaxWithLoss:
  def __init__(self):
    self.params, self.grads = [], []
    self.y = None  # softmaxの出力
    self.t = None  # 教師ラベル

  def forward(self, x, t):
    self.t = t
    self.y = softmax(x)

    # 教師ラベルがone-hotベクトルの場合、正解のインデックスに変換
    if self.t.size == self.y.size:
      self.t = self.t.argmax(axis=1)

    loss = cross_entropy_error(self.y, self.t)
    return loss

  def backward(self, dout=1):
    batch_size = self.t.shape[0]

    dx = self.y.copy()
    dx[np.arange(batch_size), self.t] -= 1
    dx *= dout
    dx = dx / batch_size

    return dx

1.3.6 重みの更新

誤差逆伝播法によって求めた勾配を使ってニューラルネットワークのパラメータを更新する

ニューラルネットワークの学習は以下の手順で行う

  1. ミニバッチ
    • データが多いと、時間がかかってしまうため、データの一部を全体の近似として使う(ゼロから始めるDeep Learning 1 より)
  2. 勾配の算出
    • 誤差逆伝播法で、各重みパラメータに関する損失関数の勾配を求める
  3. パラメータの更新
  4. 1~3を繰り返す

3. パラメータの更新

2. 勾配の算出で求めた勾配を使い、パラメータを勾配の逆方向(損失を減らす方向)に更新をする
勾配降下法

sample.png


ここでは、重みの更新方法で最も単純なSGDを使う(他にも何種類かゼロから始めるDeep Learning 1に書いてた)

W \leftarrow W - \eta \frac{\partial L}{\partial W} \\
\eta : 学習係数

Pythonで実装すると

class SGD:
  def __init__(self, lr=0.01):
    self.lr = lr

  def update(self, params, grads):
    for i in range(len(params)):
      params[i] -= self.lr * grads[i]

実際のニューラルネットワークのパラメータの更新は以下のようになる

model = TwoLayerNet( ... )
optimizer = SGD()

for i in range(10000):
  ...
  x_batch, t_batch = get_mini_batch( ... ) # ミニバッチの取得
  loss = model.forward(x_batch, t_batch)
  model.backward()
  optimizer.update(model.params, model.grads)
  ...

1.4で実際にニューラルネットワークの学習を行う


おしまい


リンク

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
What you can do with signing up
3