LoginSignup
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-09-30
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
  3. You can use dark theme
What you can do with signing up
3