updated on 2020-06-20
脳機能に見られるいくつかの特性に類似した数理的モデルのこと。
「教師あり学習」とは「入力データ」と「正解データ」がセットになった訓練データです。 コンピュータに対して大量の「入力データ」と「正解データ」を投入することでコンピュータが入力データの特徴を読み取り、正解データを学習します。
教師あり学習の例
Input (x) | Output (y) | Application |
---|---|---|
家の特徴 | 価格 | 不動産 (標準のNN) |
広告、ユーザー情報 | 広告えおクリックするか? (0/1) | オンライン広告 (標準のNN) |
画像 | オブジェクト (1,2, ... ,1000) | 画像タグ付け (CNN) |
オーディオ | テキスト転写 | 音声認識 (RNN) |
英語 | 中国語 | 機械翻訳 (RNN) |
画像、レーダー情報 | 他の車の位置 | 自動運転 (custom or complex hybrid) |
構築化されたデータ
size | bedrooms | ... | price | |
---|---|---|---|---|
0 | 2104 | 3 | 400 | |
1 | 3000 | 3 | 330 | |
2 | 2160 | 3 | 369 | |
... | ... | ... | ... | |
3000 | 1600 | 4 | 540 |
User Age | Ad id | ... | click | |
---|---|---|---|---|
0 | 41 | 93241 | 1 | |
1 | 80 | 321203 | 0 | |
2 | 18 | 1341 | 1 | |
... | ... | ... | ... | |
3000 | 107 | 14214 | 1 |
構築化されていないデータ
オーディオ、画像、文章
・データ
膨大なデータを使用できるようになり、大きなNNが強力になった
・計算リソース
CPUやGPUのおかげで大きなNNを訓練できるようになった。
・アルゴリズム
計算を手助けする革新的なアルゴリズムが発展の手助けしている。
ロジスティック回帰は小さなニューラルネットワークと見ることもできます。
ロジスティック回帰は中間の特徴抽出層(隠れ層)が無いので線形分類しかできないので分類精度は多層のニューラルネットワークには及びませんが、線型がゆえに結果を解釈しやすいという利点があります。
二項分類(にこうぶんるい、英: Binary classification)は、オブジェクトの集合を個々のオブジェクトがある特定の属性を持つかどうかで2種類にグループ分けする分類作業である。二値分類(にちぶんるい)、2クラス分類とも呼ばれ、多クラス分類において分類先のクラス数が2の場合と考えることができる。
ロジスティック回帰(ロジスティックかいき、英: Logistic regression)は、ベルヌーイ分布に従う変数の統計的回帰モデルの一種である。連結関数としてロジットを使用する一般化線形モデル (GLM) の一種でもある。
ロジスティック回帰のコスト関数は、二乗和誤差関数ではなく、交差エントロピー誤差関数である。
理由は、二乗和誤差関数を使うと、非凸な最適化問題を解くことになり、結果的には複数の局所解を持つ最適化問題を扱うことになり、勾配降下法では一つの大域解を見つけることができない。
$$ { 二乗和誤差: L(\hat{ y }, y)= \frac{ 1 }{ 2 } ( y- \hat{ y })^2 \\ 交差エントロピー誤差: L= -(y\log \hat{ y }+(1-y)\log (1-\hat{ y })) } $$
$$ { 交差エントロピー誤差は、ワンホットベクトルのように、ラベルごとの出力を行う場合 \\ n ^{ [last]}を出力層のノードのサイズ、tを教師ラベル、\hat{ y }を予測値として \\ L= - \sum_{k=1}^{ n ^{ [last] } } t _{ k } \log \hat{ y } _{ k } \\ バッチ数mで処理する場合 \\ L= - \frac{ 1 }{ m } \sum_{i=1}^m \sum_{k=1}^{ n ^{ [last] } } t _{ k } ^{ (i) } \log \hat{ y } _{ k } ^{ (i) } \\ ロジスティック回帰の場合ラベルごとの確率は、ラベル1の確率の実測値と予測値y, \hat{ y }とラベル0の確率の実測値と予測値(1-y),(1-\hat{ y })の2つであり \\ L = -(y\log \hat{ y }+(1-y)\log (1-\hat{ y })) \\ バッチ数mで処理する場合 \\ L= - \frac{ 1 }{ m } \sum_{i=1}^m (y^{ (i) }\log \hat{ y }^{ (i) }+(1-y^{ (i) })\log (1-\hat{ y }^{ (i) })) \\ となる}$$
コスト関数Jはお椀のような凸の形となり、凸関数の局所最適値(極小値)は大域的にも最適(最小)である。
これが、ロジスティック回帰でコスト関数Jを使用する最大の理由の一つ。
ループで一つずつ計算するより、ベクトルでまとめて計算した方が処理が早い
理由は、並列処理で計算されるため。
並列処理は CPUでもGPUでもできる。ただし、GPUの方が驚くほど効率的に並列処理ができる。
以下に速度の違いの例を示す。
import numpy as np import time a=np.random.rand(10000000) b=np.random.rand(10000000) tic=time.time() c=np.dot(a,b) toc=time.time() print(c) print("vectorized version: "+str(1000*(toc-tic))+"ms") c=0 tic=time.time() for i in np.arange(10000000): c += a[i]*b[i] toc=time.time() print(c) print("For loop: "+str(1000*(toc-tic))+"ms")
実行結果
2499230.9202982187 vectorized version: 7.845163345336914ms 2499230.920298015 For loop: 6281.563758850098ms
L1 loss function
$$\begin{align*} & L_1(\hat{y}, y) = \sum_{i=0}^m|y^{(i)} - \hat{y}^{(i)}| \end{align*}$$
コード: yが実測値,\(\hat{ y }\)が予測値
(python) # GRADED FUNCTION: L1 def L1(yhat, y): """ Arguments: yhat -- vector of size m (predicted labels) y -- vector of size m (true labels) Returns: loss -- the value of the L1 loss function defined above """ ### START CODE HERE ### (≈ 1 line of code) loss = np.sum(abs(y-yhat)) ### END CODE HERE ### return loss
yhat = np.array([.9, 0.2, 0.1, .4, .9]) y = np.array([1, 0, 0, 1, 1]) print("L1 = " + str(L1(yhat,y)))
L1 = 1.1
L2 loss function
$$\begin{align*} & L_2(\hat{y},y) = \sum_{i=0}^m(y^{(i)} - \hat{y}^{(i)})^2 \end{align*}$$
コード: yが実測値,\(\hat{y}\)が予測値
(python) # GRADED FUNCTION: L2 def L2(yhat, y): """ Arguments: yhat -- vector of size m (predicted labels) y -- vector of size m (true labels) Returns: loss -- the value of the L2 loss function defined above """ ### START CODE HERE ### (≈ 1 line of code) loss = np.sum((y-yhat)**2) ### END CODE HERE ### return loss
yhat = np.array([.9, 0.2, 0.1, .4, .9]) y = np.array([1, 0, 0, 1, 1]) print("L2 = " + str(L2(yhat,y)))
L2 = 0.43
原点などからある点までの距離(大きさ)をノルム
L0ノルム: 0以外の値を持つ次元の数
L1ノルム: 各次元の値の絶対値の和 「マンハッタン距離」という距離の考え方と同じ計算式
L2ノルム: 各次元の値を2乗した和の平方根
以下のベクトルを例にする
(グラフのコード)
import numpy as np import matplotlib.pyplot as plt plt.figure() # 矢印(ベクトル)の始点 O = np.array([0,0]) # 矢印(ベクトル)の成分 X = np.array([4,3]) X2 = np.array([-3,2]) X3 = np.array([4,-1]) # 矢印(ベクトル) plt.quiver(O[0],O[1], X[0],X[1], angles='xy',scale_units='xy',scale=1) plt.quiver(O[0],O[1], X2[0],X2[1], angles='xy',scale_units='xy',scale=1) plt.quiver(O[0],O[1], X3[0],X3[1], angles='xy',scale_units='xy',scale=1) # グラフ表示 plt.xlim([-5,5]) plt.ylim([-5,5]) plt.grid() plt.draw() plt.show()
X = np.array([4,3])
X2 = np.array([-3,2])
X3 = np.array([4,-1])
X_batch = np.array([X, X2, X3])
>>> X_batch
array([[ 4, 3],
[-3, 2],
[ 4, -1]])
np.linalg.norm(X_batch, ord=0, axis=1, keepdims=True)
L0ノルム
X, X2, X3の中で0でない値はそれぞれ2つ
>>> np.linalg.norm(X_batch, ord=0, axis=1, keepdims=True) array([[2.], [2.], [2.]])
L1ノルム
X, X2, X3それぞれで、各成分の絶対値を足し合わせる
>>> np.linalg.norm(X_batch, ord=1, axis=1, keepdims=True) array([[7.], [5.], [5.]])
L2ノルム
X, X2, X3それぞれで、各成分の2乗和のルートを計算
>>> np.linalg.norm(X_batch, ord=2, axis=1, keepdims=True)
array([[5. ],
[3.60555128],
[4.12310563]])
または
>>> np.sqrt(np.sum(np.abs(X_batch**2),axis=1, keepdims=True))
array([[5. ],
[3.60555128],
[4.12310563]])
L2正則化されたベクトル
>>> l2_norm = np.linalg.norm(X_batch, ord=2, axis=1, keepdims=True)
>>> X_batch / l2_norm
array([[ 0.8 , 0.6 ],
[-0.83205029, 0.5547002 ],
[ 0.9701425 , -0.24253563]])
X1は [ 0.8 , 0.6 ]
X2は [-0.83205029, 0.5547002 ]
X3は [ 0.9701425 , -0.24253563]
にL2正則化された
活性化関数は層により異なることもある。
まず使わない。例外は出力層で2値分類をしている場合。
それ以外ではtanh関数がいつも常に優れている。
0~1の値を出力するので、2値分類では理にかなっている。
シグモイド関数よりほとんどいつも良い。
-1~1の範囲を取り、活性化関数の平均が0に近いので、出力の平均が0に近くなり、データをより中央に揃えるため、次の層での学習が簡単になる。
sigmoidとtanh関数の問題点は、zがとても小さい時やとても大きい時に、関数の勾配、微分、傾きがとても小さくなること。
勾配が小さすぎると、学習が難しくなる状態になる。次の層、またその次の層、と徐々に勾配が小さくなっていく問題を勾配消失問題と呼ぶ。
NNの勾配消失問題を改善するために、活性化関数に正規化機能の無いReLU関数(ランプ関数)を使う
活性化関数を用いるとモデルの表現力を増すため。
表現力が増す理由は、入力の加重和に対して、活性化関数を用いて変換を行うと様々な値の出力が行えます。様々な値の出力を持つパーセプトロンを組み合わせるとより複雑な表現が可能になります。
$$ { シグモイド関数: g(z)= \frac{ 1 }{ 1+ e ^{ -z } } \\ \ \frac{ d }{ dz } g(z) = \frac{ 1 }{ 1+e ^{ -z } } \left( { 1-\frac{ 1 }{ 1+e ^{ -z } } } \right) =g(z)(1-g(z)) \\ \\ ハイパボリックタンジェント関数: g(z)=tanh(z)= \frac{ e ^{ z }-e ^{ -z } }{ e ^{ z }+e ^{ -z } } \\ \ \frac{ d }{ dz } g(z) = 1-(tanh(z)) ^{ 2 }=1-g(z) ^{ 2 } \\ \\ ReLU関数: g(z) = max(0,z)=\begin{cases} 0 & (z<=0) \\ z & (z>0) \end{cases} \\ \ \frac{ d }{ dz } g(z) = \begin{cases} 0 & (z<=0) \\ 1 & (z>0) \end{cases} \\ \\ Leaky ReLU関数: g(z) = max(0.01z, z) = \begin{cases} 0.01z & (z<=0) \\ z & (z>0) \end{cases} \\ \ \frac{ d }{ dz } g(z) = \begin{cases} 0.01 & (z<=0) \\ 1 & (z>0) \end{cases} \\ \\ } $$
重みを均一な値にすることはNGです。(例えば、重みを全て0)
順伝播時に全て同じ値が伝達されてしまうため、全て同じように更新されてしまうため、各層に複数のニューロンを持つ意味が全くなくなる。
これでは、たくさんの重みを持つ意味がなくなってしまう。
重みは (ガウス分布の乱数)×0.01、バイアスは0で初期化
重みの初期化に、ガウス分布の乱数に0.01をかける理由
たとえ出力層だけであっても、シグモイドやtanhを使用する場合、zの値(z=WX+b)がとても大きい、もしくは小さくなり、シグモイドやtanhの勾配が非常に小さくなるため、学習に時間がかかるから。
順伝播の各層の出力計算は、一つ前の層の計算結果を使用するため、ベクトルではなくforループで計算するしかないので、forループを使って構わない。
深層ネットワークが良い成績を上げる理由の2つ。
1つ目: 初めの層で細かいレベルの簡単な特徴を学び、後の深めの層では検出した簡単なものを組み合わせて、より複雑なものを検出できるようにするため。
上記の顔検出器の場合
隠れ層1: どの向きに境界があるのかを見つけている。
隠れ層2: 検出した境界を使ってグループ化して顔の部分を形成
隠れ層3: 目や耳、鼻や顎などの様々な部品を組み合わせることで、様々な種類の顔を認識したり検出することができる
2つ目: 狭くて(隠れユニットの数が比較的少ない)深いニューラルネットワークで計算する関数があるが、浅いネットワーク(十分な隠れ層がない)で同じような関数を作って計算する場合は指数関数的により多くの隠れユニットが必要となる。
上記の画像は、深いニューラルネットと同じ計算をする場合、浅いニューラルネットワークが指数関数的に隠れユニットを必要とすることの一例。
入力が(X1,X2,X3, ... Xn)でXORから出力計算をする時、狭いニューラルネットワークの層の数はlognであり、浅いニューラルネットワークの場合は2のn乗(または2のn-1乗)である。
画像左側がバッチ無しの時の計算
右側がバッチ数mでの計算
\(a ^{ [l] }はl層目の出力であり、入力xは入力層(0層目)の出力と言えるので、a ^{ [0] } とも表現できる\)
逆伝播する勾配は、バッチ数m列の行列で伝播していくが、W や bなどの学習を行うパラメータの勾配のみ、バッチ数で平均化する。
ニューラルネットワークの理論と実装をわかりやすく説明された講座でした。
書籍でニューラルネットワークを勉強するよりも、僕はこの講座の方がお勧めできます。
自信持ってお勧めできるので、初心者~中級者くらいで、ニューラルネットワークを勉強しようと思っている人は是非!
今回の講座はこちら!!
https://www.coursera.org/learn/neural-networks-deep-learning/