「[[.NET 開発基盤部会 Wiki>http://dotnetdevelopmentinfrastructure.osscons.jp]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。

-[[戻る>ニューラルネットワーク]]

*目次 [#s4413bb4]
#contents

*概要 [#ha063b73]
学習フェーズでは、信号は逆方向に伝播する。

-学習とは、重みパラメタを自動獲得するためのもの。

-実際のニューラルネットワークの重みパラメタは、
--数千、数万になるため、手動での設定は不可能。
--更に層を深めた深層学習(deep learning)では数億にも登る。

-重みパラメタの自動獲得のため、損失関数という指標を導入する。
--損失関数を基準に??その値が最小になるように重みパラメタを探す。
--このような値を探し出すためには、勾配法という手法を用いる。

*学習はデータ駆動 [#mbc3dc7d]
-MNISTデータセットの分類処理を行うアルゴリズムを考え出すのは困難。~
しかし、機械にデータを学習させる機械学習を用いれば分類処理が実現可能。
>人間が(暗黙的な学習によって、)これらを判別することはできる。~
従って、人間の脳も、一部は、このようなデータ駆動で動いているのかもしれない。

-このように、アルゴリズムを捻り出すのではなく、データを有効活用して解決する方法に、~
画像から[[特徴量>#be25bbf1]]を抽出し、[[特徴量>#be25bbf1]]のパターンを機械学習の技術で学習するという方法がある。

*機械学習 [#z9e63650]

**特徴量 [#be25bbf1]
-入力された学習データから特徴量と呼ばれる数値を抽出する。
-学習データにどのような特徴があるかを数値化したもの。
-特徴量の抽出は人間が設計し実装する必要がある。
-抽出した特徴量を元に機械はパターン・経験則をモデルを使って学習する。

***特徴量選択 [#l29fa353]
入力画像データから、本質的なデータを抽出できるように設計された変換器を指す。
-画像データの特徴量は通常、ベクトルを使用して表される。
-SIFT, SURF, HOGなど、人が設計した変換器によって画像データをベクトル化する。

**機械学習 [#yb9b7687]
ベクトル化された画像データを機械学習のSVMやKNNなどの識別器で学習させる。

*深層学習(deep learning) [#h9af97d2]
特徴量についても機会が学習する。

**損失関数 [#l7995afd]
-深層学習(deep learning)では一つの指標を手がかりに最適なパラメタを探索する。
-この指標を損失関数を呼び、パラメタに対して連続的に変化する関数を用いる。
-これは学習時に、微分によって傾きが0になってパラメタの更新できなくなることを防ぐため。

***2乗和誤差 [#o54ca9e8]
ニューラルネットワークの出力と正解となる教師データの各要素の差の二乗の総和の2分の一。

-式
                  2
 E = 1/2 Σ (yk-tk)
          k

--説明
---k:データの次元数
---yk:ニューラルネットワークの出力
---tk:教師データ

--例(k=10)
---yk:ニューラルネットワークの出力~
=[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]~
[[ソフトマックス関数>ニューラルネットワーク(推論)#b77bdfd7]]の出力(全て足して1.0になる。)

---tk:教師データ~
=[0,   0,    1,   0,   0,    0,   0,   0,   0,   0]~
※ 正解ラベルを1、ソレ以外を0とする、one-hot表現

-Python
--実装
 """This is a test program."""
 
 import numpy as np
 
 def mean_squared_error(yk, tk):
     """損失関数(2乗和誤差)"""
     return 0.5 * np.sum((yk - tk)**2)
 
 tk = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
 yk = np.array([0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0])
 print(mean_squared_error(yk, tk))
 yk = np.array([0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0])
 print(mean_squared_error(yk, tk))

--出力~
正解(tk)に対応する確率(yk)が高ければ高いほど、1に近いデータになる。
---tkで2が正解の場合、ykで2の確率が一番高いとしたデータの場合のE。
 0.0975
---tkで2が正解の場合、ykで7の確率が一番高いとしたデータの場合のE。
 0.5975

-前者の損失関数(2乗和誤差)の出力Eの方が''大きく''、~
前者のykがより適合していることを示している。

***交差エントロピー誤差 [#p5eb632c]
-正解ラベルのykデータの底がeの自然対数 log eを計算する。
- y = -log xでは、x=0-1の場合、xが0に近づくほど、yは大きな値になる。

-式
 E = - Σ tk log yk
        k

--説明
---log:logは底がeの自然対数 log e
---k:データの次元数
---yk:ニューラルネットワークの出力
---tk:教師データ

--例(k=10)~
[[同上>#o54ca9e8]]

-Python
--実装
 """This is a test program."""
 
 import numpy as np
 
 def mean_squared_error(yk, tk):
     """損失関数(交差エントロピー誤差)"""
     delta = 1e-7 # log(0)はマイナス∞になるのを微小な値を足して防止。
     return -np.sum(tk * np.log(yk + delta))
 
 tk = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
 yk = np.array([0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0])
 print(mean_squared_error(yk, tk))
 yk = np.array([0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0])
 print(mean_squared_error(yk, tk))

--出力~
正解(tk)に対応する確率(yk)が低ければ低いほど、大きいデータになる。

---tkで2が正解の場合、ykで2の確率が一番高いとしたデータの場合のE。
 0.510825457099
---tkで2が正解の場合、ykで7の確率が一番高いとしたデータの場合のE。
 2.30258409299

-前者の損失関数(交差エントロピー誤差)の出力Eの方が''小さく''、~
前者のykがより適合していることを示している。

**ミニバッチ学習 [#h15bc15d]
-ミニバッチ学習では、上記の損失関数を訓練用データセットに対して適用する。
-ここでは、訓練用データセットに対する損失関数の総和を指標とする。

-式
 E = -1/N ΣΣ tnk log ynk
           n k

--説明
---[[交差エントロピー誤差>#p5eb632c]]の式をN個のデータを含む訓練用データセット用に拡張する。
---最後に、Nで割って正規化する(データ1個あたりの平均の損失関数を求める)。

--例([[MNISTデータセット>ニューラルネットワーク(推論)#c18ded2a]]を使用する)

-Python

--実装

---one-hot表現の場合
 """This is a test program."""
 
 import numpy as np
 
 def mean_squared_error(ynk, tnk):
     """損失関数(交差エントロピー誤差)"""
     print("tnk:" + str(tnk))
     print("ynk:" + str(ynk))
 
     batch_size = ynk.shape[0]
     print("batch_size:" + str(batch_size))
     delta = 1e-7 # log(0)はマイナス∞になるのを微小な値を足して防止。
     return - 1 / batch_size * (np.sum(tnk * np.log(ynk + delta)))
 
 TNK = np.array([[0, 0, 1, 0, 0, 0, 0, 0, 0, 0], \
     [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]])
 YNK = np.array([[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0], \
     [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]])
 print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))
 YNK = np.array([[0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0], \
     [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]])
 print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))

---one-hot表現でない場合
 """This is a test program."""
 
 import numpy as np
 
 def mean_squared_error(ynk, tnk):
     """損失関数(交差エントロピー誤差)"""
     print("tnk:" + str(tnk))
     print("ynk:" + str(ynk))
 
     batch_size = ynk.shape[0]
     print("batch_size:" + str(batch_size))
 
     delta = 1e-7 # log(0)はマイナス∞になるのを微小な値を足して防止。
     ynk = ynk + delta
      
     print("arange:" + str(ynk[np.arange(batch_size), tnk]))
     return - 1 / batch_size * (np.sum(np.log(ynk[np.arange(batch_size), tnk])))
 
 TNK = np.array([2, 2])
 YNK = np.array([[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0], \
     [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]])
 print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))
 YNK = np.array([[0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0], \
     [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]])
 print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))

--出力~
[[同上>#p5eb632c]]

*勾配法 [#yce9392c]
勾配法では、(パラメタの[[微分>#q81f98e1]]を計算して得た)[[勾配>#e083729e]]の情報を使って進む方向を決める。

**微分 [#q81f98e1]
ある瞬間の変化の量(=グラフの接線の傾き的な)。

-微分とは何か? - 中学生でも分かる微分のイメージ~
https://sci-pursuit.com/math/differential-1.html

***式 [#p773004c]
 df(x)  yの増量        f(x+h) - f(x)        f(x+h) - f(x)
 ── = ───  = lim  ──────  = lim  ──────
  dx    xの増量   h→0 (x+h) - (x)     h→0      h

***解析的 [#g5ab24b9]
式の展開によって微分を求める(誤差がない)。

      2
 y = x

 df(x)  yの増量        (x+h)^2 - x^2        (x^2+2hx+h^2) - (x^2)
 ── = ───  = lim  ──────  = lim  ──────────
  dx    xの増量   h→0 (x+h) - (x)     h→0           h

  dy         2hx+h^2
 ── = lim ──── = lim 2x+h  = 2x
  dx    h→0    h      h→0

***数値微分 [#w4dd09ea]
計算によって微分を求める(誤差がある)。

-Python

--実装
 """This is a test program."""
 
 import numpy as np
 import matplotlib.pyplot as plt
 
 def numerical_diff(f, x):
     """数値微分"""
     h = 1e-4 # 微小な値hとして1の-4乗を用いる
     return (f(x + h) - f(x - h)) / (2 * h) # 前方差分から中心差分にして誤差減
 
 def function_1(x):
     """f(x)=0.01x^2+0.1x"""
     return 0.01 * x ** 2 + 0.1 * x
 
 X=5
 print("X=" + str(X) + " : " + str(numerical_diff(function_1, X)))
 X=10
 print("X=" + str(X) + " : " + str(numerical_diff(function_1, X)))
 
 X = np.arange(0.0, 20.0, 0.1)
 Y = function_1(X)
 plt.xlabel("x")
 plt.ylabel("f(x)")
 plt.plot(X, Y)
 plt.show()

--出力
---グラフ
#ref(graf.png,left,nowrap,グラフ,60%)

---傾き
 X=5 : 0.1999999999990898
 X=10 : 0.2999999999986347

--参考~
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/gradient_1d.py
#ref(gradient_1d.png,left,nowrap,微分,60%)

***偏微分 [#z8297246]

-偏微分の意味と高校数学への応用 | 高校数学の美しい物語~
http://mathtrain.jp/henbibunimi

-例1:2つの引数の2乗和を計算する式
--式
               2    2
 f(x0, x1) = x0 + x1

--Python
---実装
 """This is a test program."""
 
 import numpy as np
 
 def function_2(x):
     """f(x0, x1) = x0^2 + x1^2"""
     return x[0] ** 2 + x[1] ** 2
     # or return np.sum(x ** 2)

-例2:2つの引数の2乗和を計算する式の偏微分
--式
  df(x0, x1)
 ─────  = 2 * x0
     dx0
 
  df(x0, x1)
 ─────  = 2 * x1
     dx1

--Python
---実装
 numerical_diff"""This is a test program."""
 
 import numpy as np
 
 def numerical_diff(f, x):
     """数値微分"""
     h = 1e-4 # 微小な値hとして1の-4乗を用いる
     return (f(x + h) - f(x - h)) / (2 * h) # 前方差分から中心差分にして誤差減
 
 def function_tmp1(x0):
     """x0=3, x1=4 の場合の x0 に対する偏微分用"""
     return x0 ** 2 + 4.0 ** 2
 
 def function_tmp2(x1):
     """x0=3, x1=4 の場合の x1 に対する偏微分用"""
     return 3.0 ** 2.0 + x1 ** 2
 
 print(numerical_diff(function_tmp1, 3.0))
 print(numerical_diff(function_tmp2, 4.0))

---出力 (x0=3, x1=4)
 6.00000000000378   (2 * x0 = 2 * 3 = 6)
 7.999999999999119   (2 * x1 = 2 * 4 = 8)

**勾配 [#e083729e]
勾配の示す方向は、各場所で関数の値を最も減らす方向。

-式
  df(x0, x1)    df(x0, x1)
 ─────   , ─────
     dx0           dx1

--説明
---すべての変数の偏微分をベクトルとしてまとめたものを勾配と呼ぶ。
---上記の例なら、x0=3, x1=4の場合、勾配は、(6, 8)となる。

-Python
--実装
 """This is a test program."""
 
 import numpy as np
 
 def numerical_gradient(f, x01):
     """偏微分"""
     h = 1e-4 # 微小な値hとして1の-4乗を用いる
 
     grad = np.zeros_like(x01) # x01と同じ形状で要素が0。
 
     for idx in range(x01.size):
         tmp_val = x01[idx]
         # 前方差分から中心差分にして誤差減
         # f(x + h)
         fxh1 = f(tmp_val + h)
         # f(x - h)
         fxh2 = f(tmp_val - h)
         # (f(x + h) - f(x - h)) / 2 * h
         grad[idx] = (fxh1 - fxh2) / (2 * h)
 
     return grad
 
 def function_2(x):
     return np.sum(x**2)
 
 print(numerical_gradient(function_2, np.array([3.0, 4.0])))
 print(numerical_gradient(function_2, np.array([0.0, 2.0])))
 print(numerical_gradient(function_2, np.array([3.0, 0.0])))

--出力
 [ 6.  8.]
 [ 0.  4.]
 [ 6.  0.]

--参考~
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/gradient_2d.py
---x = -2.0 - 2.5, y = -2.0 - 2.5 の範囲の0.25刻みのメッシュグリッドを生成
---メッシュグリッドはm行n列の配列なので、これをflatten()メソッドでm*nにベクトル化。
---np.array([X, Y])で 2 行 m*n 列の配列にする。
#ref(gradient_2d.png,left,nowrap,偏微分,60%)

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS