1. はじめに
本記事はJDLA E資格の認定プログラム「ラビットチャレンジ」における機械学習のレポート記事である。
本記事では深層学習講座Day1「ニューラルネットワーク」について要点をまとめている。
2. Neutal Networkの全体像
確認テスト2.1.
2.1.1. ディープラーニングは結局何をしようとしているか、2行以内で述べよ。
回答:回答できなかった
解答:誤差を最小化するパラメータを発見すること
2.1.2. 次の中のどの値の最適化が最終目的か、全て選べ。
①入力値[X] ②出力値[Y] ③重み[W] ④バイアス[b] ⑤総入力[u] ⑥中間層入力[z] ⑦学習率[$\rho$]
回答:出力値[Y]と重み[W]
解答:重み[W]とバイアス[b]=パラメータ
確認テスト2-2.
次のネットワークを紙に書け。
入力層:2ノード1層
中間層:3ノード2層
出力層:1ノード1層
入力層・出力層は1層。中間層の数は任意
ニューラルネットワークでできること
・回帰
・結果予想(売上予測、株価予想)
・ランキング(競馬順位予想、人気順位予想
・分類
・猫写真の判別
・手書き文字認識
・花の種類分類
ニューラルネットワークは数ある分析手法のひとつ
主な回帰分析手法
・線形回帰
・回帰木
・ランダムフォレスト
・ニューラルネットワーク
主な分類分析手法
・ベイズ分類
・ロジスティック回帰
・決定木
・ランダムフォレスト
・ニューラルネットワーク
深層学習の実用例
・自動売買(トレード)
時系列データによる株価予測など
→本当によいものは秘中の秘で世に出ることはない。
→世の中にある基礎情報から自分で理解して構築していく必要がある。
・チャットボット
コールセンターの自動化
FAQシステム→対応の効率化
時系列データの利用=RNN
・翻訳
Google翻訳等
Seq2SeqやWord2Vecなどの応用
・音声解釈
Amazon Echo, Google Home
ドラゴンスピーチ(音声認識・音声入力ソフト)
AIを使う部分使わない部分があるが、音声解釈ではAIを使ったプロダクトが効果を発揮している。
音声分野で自分でプロダクトをつくりたい
精度を上げるためのデータ集め=個人では非常に困難
既存のライブラリ、Googleなどで提供されているAPIの活用
音声周りのInput-Output考察するためには知識が必要
→仮説の基データの圧縮方法などを考察できる
精度を上げる方法=音声圧縮の方法の工夫
→APIを用いてできることは限られる(音声の圧縮方法が決められていたり、制限されていたりする)
→その制限をいかにクリアするか
・囲碁・将棋AI
AlphaGo(囲碁)、Ponanza(将棋)など
強化学習の側面が強い(強化学習とCNNのハイブリッドなど)
DQNなども活用されている
3. Section1:入力層~中間層
確認テスト3-1.
入力層から中間層への情報の伝播を動物の例に合わせて図示せよ。
確認テスト3-2.
以下の数式をPythonで書け。(numpyを使用)
$ u = w_{1}x_{1} + w_{2}x_{2} + w_{3}x_{3} + w_{4}x_{4} + b = Wx + b $
回答:u = W.dot(x) + b
解答:u1 = np.dot(x, W1) + b1
確認テスト3-3.
ソースコード「1_1_forward_propagation」の単層単数ノードのコードから中間層の出力を定義しているソースを抜き出せ。
回答:z = functions.relu(u)
解答:z = functions.relu(u)
入力値x = np.array([2, 3])
をu = np.dot(x, W) + b
で総入力化し、
中間層の出力z = functions.relu(u)
としている。
W:重み b:バイアス
functions.relu(u)
:活性化関数
確認テスト3-4.
ソースコード「1_1_forward_propagation」の順伝播(単層・単ユニット)において、
重みおよびバイアスを乱数で初期化せよ。
W = np.zeros(2) # Wの各値に「0」を代入
W = np.ones(2) # Wの各値に「1」を代入
W = np.random.rand(2) # Wの各値に0~1の間の乱数を代入
W = np.random.randint(5, size=(2)) # Wの各値に0~4の間の整数の乱数を代入
b = np.random.rand() # bの値に0~1の間の乱数を代入
b = np.random.rand() * 10 -5 # bの値に-5~5の間の乱数を代入
4. Section2:活性化関数
活性化関数とは
ニューラルネットワークにおいて次の層への出力の大きさを決める非線形の関数。
入力値によって、次の層への信号のON/OFFや強弱を定める働きをもつ。
確認テスト4.1.
線形と非線形の違いを簡易に説明せよ。
活性化関数の使い方
中間層用の活性化関数と出力層用の活性化関数で使い方が異なる
入力層→中間層間および中間層→中間層間
ReLU関数
シグモイド(ロジスティック)関数
ステップ関数
→DLの基になった構造の部分で使用されていた
現在のDLでは使われていないが、ReLU、シグモイドの話をする上で少し触れておきたい
ステップ関数
def step_function(X):
if x > 0:
return 1
else
return 0
$f(x) = \biggl\{
\begin{array}{ll}
1 & (x \geqq 0) \\
0 & (x \lt 0)
\end{array}$
閾値を超えたら発火する関数出力は常に0, 1(微細なニュアンスの表現はできない)
パーセプトロン(ニューラルネットワークの前身)で利用されていた。
課題:0~1間を表現できず、線形分離可能なものしか学習できない
シグモイド関数
def sigmoid(x):
return 1/(1 + np.exp(-x))
$ \smash{ f(x) = \dfrac{1}{1 + e^{-x}} }$
0~1間を緩やかに変化する関数。
信号の強弱を伝えられるほうになり、予想ニューラルネットワーク普及のきっかけとなった。
課題:大きな値では出力の変化な微小なため、勾配消失問題を引き起こすことがあった。
厳密に言えばシグモイド関数は実際には0を扱わない。計算処理上、0として扱うことはあるが、実際には微小な値となっている。
→スパース化
ReLU関数
def step_function(X):
if x > 0:
return x
else
return 0
$f(x) = \biggl\{
\begin{array}{ll}
x & (x \geqq 0) \\
0 & (x \lt 0)
\end{array}$
今最も使われている活性化関数
勾配消失問題の回避およびスパース化に貢献し、よい成果をもたらしている
閾値を超えたら超えたときの数値をそのまま伝えている
→勾配消失問題の抑止
超えていないときは0を伝える
→スパース化の回避
初手としてReLUを使うのはありだが、ReLUにこだわる必要はない。
場合によってはハイパボリックタンジェントなどのほうが適していることもある。
全結合NN - 単層複数ノード
中間層を増やしたもの→中間層の数は任意かつノード数も任意。
→総ノード数分W,bが必要になる。
活性化関数の効果で、一部の出力は弱く、一部は強く伝播される。
→より価値を持たせ、もっと細部に対しての学習ができるようになる。
→その反面、計算負荷が高くなる問題もある。
確認テスト4.2.
ソースコード「1_1_forward_propagation」の単層複数ノードのコードから中間層の出力を定義しているソースを抜き出せ。
※単層複数ノード = 単層複数ユニット
回答:z = functions.sigmoid(u)
解答:z = functions.sigmoid(u)
確認テスト4.3.
ソースコード「1_1_forward_propagation」 - 順伝播(単層・複数ユニット)において、
重みおよびバイアスを乱数で初期化せよ。
・重みの初期化
・バイアスの初期化
・各初期化方法
W = np.zeros((4,3)) # Wの各値に「0」を代入
W = np.ones((4,3)) # Wの各値に「1」を代入
W = np.random.rand(4,3) # Wの各値に0~1の間の乱数を代入
W = np.random.randint(5, size=(4,3)) # Wの各値に0~4の間の整数の乱数を代入
b = np.random.rand(3) # bの各値に0~1の間の乱数を代入
b = np.random.rand(3) * 10 -5 # bの各値に-5~5の間の乱数を代入
5. Section3:出力層
5.1. 誤差関数
誤差の計算:例として二乗誤差
確認テスト5.1.
なぜ引き算ではなく二乗しているか述べよ
回答:引き算の場合、正の値が出る場合と負の値が出る場合があるため
解答:値を正にするため
確認テスト5.2.
二乗誤差の1/2の意味は何か?
回答:わからなかった。
解答:微分を簡単にするため
5.2. 出力層の活性化関数
なぜ出力層の活性化関数と中間層の活性化関数を分けたのか
→値の強弱について
中間層:閾値の前後で信号の強弱を調整している
出力層:信号の強弱を調整することはなく、信号の大きさ(比率)をそのままに変換
→確率出力について
分類問題の場合、出力の出力は0~1の範囲に限定し、総和を1とする必要がある。
こういったことから出力層と中間層で利用される活性化関数が異なる。
出力層で用いられる主な活性化関数
・恒等写像
回帰問題に使用
二乗誤差
・シグモイド関数
二値分類
交差エントロピー
・ソフトマックス関数
他クラス(多値)分類
交差エントロピー
なぜ活性化関数と誤差関数はこの組み合わせなのか
→簡単に言うと、この組み合わせが計算上相性が良い
確認テスト5.3.
ソフトマックス関数の①~③数式に該当するコードを示し、1行ずつ処理の説明をせよ。
前提:ベクトルを使うため、多次元のものに関しては使用できない。
他クラス分類=各確率をサマライズすると1→つまり結果同士はお互いに干渉しあっている
# ソフトマックス関数
def softmax(x):
if x.ndim == 2: # 多次元非対応(二次元にのみ対応)
x = x.T # xの転置をxと再定義
x = x - np.max(x, axis=0) # xにおいて、xの中で最大の値をそれぞれ減算する
y = np.exp(x) / np.sum(np.exp(x), axis=0) # 「x各値のシグモイド関数値」を「『x各値のシグモイド関数値』の合計値」で除算。
return y.T # 転置したxから算出したyなので、戻り値はyを転置したものになる
x = x - np.max(x) # オーバーフロー対策
return np.exp(x) / np.sum(np.exp(x))
①y.T:各分類の、ソフトマックスで演算した結果の行列
②np.exp(x):xをシグモイド関数で演算したもの
③np.sum(np.exp(x), axis=0) :「xをシグモイド関数で演算したもの」の合計値。スカラ値
参考:ndimの使い方 (numpy)~制御工学の基礎あれこれ~
NumPy配列の最大値/最小値に関する関数 | hydroculのメモ
NumPyでのaxis指定 - Qiita
ソフトマックス関数 - Python数値計算入門
[Python] [4] 活性化関数の実装サンプルまとめ(ステップ / シグモイド / ReLU / 恒等関数 /ソフトマックス関数) - UNISIA-SE Tech Blog
二乗誤差
平均二乗誤差
mean_squared_error(d, y):
return np.mean(np.square(d - y)) / 2
注意:
np.square(x):xの二乗
np.sqrt(x):xの平方根
参考:Numpyでの二乗・平方根・絶対値 - 雑用エンジニアの技術ノート
確認テスト5.4.
交差エントロピーの①~②数式に該当するコードを示し、1行ずつ処理の説明をせよ。
# クロスエントロピー
def cross_entropy_error(d, y):
if y.ndim == 1:
d = d.reshape(1, d.size) # 1行d.size列に変形
y = y.reshape(1, y.size) # 1行y.size列に変形
# 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
if d.size == y.size:
d = d.argmax(axis=1)
batch_size = y.shape[0] # yの1行目の列数(この場合はy.sizeと同等)
#
return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
①② -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
→微小な値(1e-7)を与えることで0にならないようにしている。
学習率などでも行われている工夫
参考:配列を形状変換するNumPyのreshapeの使い方 - DeepAge
NumPyのndarrayのインスタンス変数shapeの意味 - DeepAge
連番や等差数列を生成するnumpy.arange関数の使い方 - DeepAge
Kerasのライブラリ:
スパースクロスエントロピー
教師データが「one-hot-vector」でないInteger型整数値
このときはスパースカテゴリカルクロスエントロピーが使われることもある。
6. Section4:勾配降下法
勾配降下法の種類
・勾配降下法
・確率的勾配降下法
・ミニバッチ勾配降下法
深層学習の目的
学習を通して誤差を最小にするネットワークを作成すること
誤差$E(w)$を最小化するパラメータ$w$を発見すること
6.1. 勾配降下法
$w^{t+1} = w^{t} - \varepsilon \times \nabla E$
$w^{t+1}$:次のパラメータ
$w^{t}$:現在のパラメータ
$\varepsilon$:学習率
$\nabla E$:誤差$E$をパラメータで微分したもの
確認テスト6.1.
次のパラメータを算出しているコードおよび∇Eを算出しているコードを抜き出せ。
次のパラメータを算出しているコード
回答:network[key] -= learning_rate * grad[key]
解答:network[key] -= learning_rate * grad[key]
$\nabla E$を算出しているコード
回答:grad = backward(x, d, z1, y)
解答:grad = backward(x, d, z1, y)
学習率ε(ハイパーパラメータ)
学習率が大きすぎた場合、最小値にいつまでもたどり着かず発散してしまう。
学習率が小さすぎた場合、発散することはないが、収束するまでに時間がかかってしまう。
3次関数(多次関数)で考えた場合、
→学習率が小さすぎると大域的極小解に到達する前に局所的な極小解にはまる恐れがある
勾配降下法の種類
Momentum
AdaGrad
Adadelta
Adam
RMSProp
など
6.2. 確率的勾配降下法(SGD)
$w^{t+1} = w^{t} - \varepsilon \times \nabla E_{n}$
データに対するアプローチの違い
勾配降下法 :全サンプルの平均誤差
確率的勾配降下法:ランダムに抽出したサンプルの誤差
利点
データが冗長な(データが同じようなものが多い、価値が低いデータが多い)場合などの計算コストの軽減
局所的極小解に収束するリスクの軽減
オンライン学習が可能
確認テスト6.2.
オンライン学習とは何か
回答:学習データが入ってくるたび、新たに入ってきたデータのみを使って学習を行う方式
解答:新規で入ってきたデータのみで学習させていく方法
参考:オンライン学習とバッチ学習 - Developers.IO
ニューラルネットワークのミニバッチ、オンライン学習 | AVILEN AI Trend
6.3. ミニバッチ勾配降下法
データに対するアプローチの違い
確率的勾配降下法 :ランダムに抽出したサンプルの誤差
ミニバッチ勾配降下法 :ランダムに分割したデータの集合(ミニバッチ)D_tに属するサンプルの平均誤差
確率的勾配降下法は全データにおけるランダム抽出だが、ミニバッチ勾配降下法は特定のデータ集合に対するアプローチ
利点
確率的勾配降下法のメリットを損なわず、計算コストを軽減できる
→CPUを利用したスレッド並列化やGPUを利用したSIMD並列化
確認テスト6.3.
以下の数式を図にせよ。
$w^{t+1} = w^{t} - \varepsilon \times \nabla E_{t}$
回答:回答できなかった
解答:以下の図
誤差をパラメータで微分した値$\nabla E$の導出方法
1.数値微分を使う方法
微小な数値を生成し、その勾配を計算することで疑似的に計算する一般的な手法
$ \smash{\dfrac{\partial E}{\partial w_{m}} \approx \dfrac{E(w_{m} + h) - E(w_{m} - h)}{2h} }$
デメリット
各パラメータw_mそれぞれについてE(w_m + h), E(w_m - h)を計算するために順伝播の計算を繰り返し行う必要があり計算負荷が大きい
2.誤差逆伝播法を使う方法
後述
7. Section1:誤差逆伝播法
7.1. 誤差逆伝播法
算出された誤差を、出力層側から順に微分し、前の層の方向に伝播させていく方法
最小限の計算で各パラメータでの微分値を解析的に計算する手法
誤差から微分を逆算することで不要な再帰的計算を避けて微分を算出できる
「誤差をパラメータで微分した値」を計算リソースの消費を抑えたうえでどう算出するか?
実装のポイント:
誤差:他クラス分類、二値分類など誤差関数と活性化関数の組み合わせが明白なものに関してはを複合化することもある
二値分類→交差エントロピーとシグモイド関数の複合導関数
多クラス分類→交差エントロピーとソフトマックス関数の複合導関数
確認テスト7.1.
誤差逆伝播法では不要な再帰的処理を避けることができる。
既におこなった計算結果を保持しているソースコードを抽出せよ。
# 誤差逆伝播
def backward(x, d, z1, y):
print("\n##### 誤差逆伝播開始 #####")
grad = {}
W1, W2 = network['W1'], network['W2']
b1, b2 = network['b1'], network['b2']
# 出力層でのデルタ
delta2 = functions.d_sigmoid_with_loss(d, y)
# b2の勾配
grad['b2'] = np.sum(delta2, axis=0)
# W2の勾配
grad['W2'] = np.dot(z1.T, delta2)
# 中間層でのデルタ
delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1)
# b1の勾配
grad['b1'] = np.sum(delta1, axis=0)
# W1の勾配
grad['W1'] = np.dot(x.T, delta1)
print_vec("偏微分_dE/du2", delta2)
print_vec("偏微分_dE/du2", delta1)
print_vec("偏微分_重み1", grad["W1"])
print_vec("偏微分_重み2", grad["W2"])
print_vec("偏微分_バイアス1", grad["b1"])
print_vec("偏微分_バイアス2", grad["b2"])
return grad
7.2. 誤差勾配の計算
$\smash { E(y) = \dfrac{1}{2}\sum_{j=1}^{J}(y_{j}- -d_{j})^{2} = \dfrac{1}{2}||y - d||^{2} }$ :誤差関数=二乗誤差関数
$y = u^{(L)}$ :出力層の活性化関数=恒等写像
$u^{(l)} = w^{(l)}z^{(l-1)} + b^{(l)}$ :総入力の計算
参考:活性化関数のまとめ(ステップ、シグモイド、ReLU、ソフトマックス、恒等関数) - Qiita
やっぱりよく分からない活性化関数とは - AI人工知能テクノロジー
7.3. Jupyter演習
演習
1. 順伝播関数内、中間層の活性化関数をReLU関数からシグモイド関数への変更
→誤差逆伝播法にも変更すべき箇所あり
・活性化関数にReLUを使用した場合
・活性化関数にシグモイド関数を使用した場合
上記「ReLU」「シグモイド関数」それぞれで目盛りが同一ではないため比較は難しいが、
どちらもエポックを重ねるごとに誤差が収束していっていることがわかる。
2. 入力値の設定を0~1の乱数→-5~5の乱数に変更
エポックを重ねても誤差が収束しなくなっている。
確認テスト7.2.
以下のふたつの偏微分を表すソースコードを示せ。
$\smash{ \dfrac{\partial E}{\partial y}\dfrac{\partial y}{\partial u} }$ →恒等写像のため$y=u$、よって$\smash{ \dfrac{\partial E}{\partial y}\dfrac{\partial u}{\partial u} = \dfrac{\partial E}{\partial y} }$
delta2 = functions.d_mean_squared_error(d, y)
$\smash{ \dfrac{\partial E}{\partial y}\dfrac{\partial y}{\partial u}\dfrac{\partial u}{\partial w_{ji}^{(2)}} = \dfrac{\partial E}{\partial y}\dfrac{\partial u}{\partial w_{ji}^{(2)}} }$
grad['W2'] = np.dot(z1.T, delta2)