ゼロから作るDeep Learning メモ
https://scrapbox.io/files/6125c00f934b83001d5eec95.jpg
TFjsと学習済みモデルを利用してアプリに組み込む程度のことはできるが、そのモデルがどのように作られているのか基本的なところの理解を深めたいのよ
1章 Python入門
Anacondaインストール
Macに入ってたpythonは2系だったのでAnacondaのインストールから
brew install anaconda
condaコマンドにパスを通す
/opt/homebrew/anaconda3/bin/conda init fish で終わった(ように見えたが...
python --version
Python 3.8.8
だがVS CodeのTerminal内のシェルでなぜかうまくPATHが設定されなかったので結局自前で fish_add_path した VS CodeでPython使うにあたって割と躓いた
Pylanceからの reportMissingImports
Python interpreterの選択で明示的にcondaのenvironmentを選択するとうまくいった
2章 パーセプトロン
perceptron
アルゴリズムの名前
ニューラルネットワーク(ディープラーニング)の起源らしい
パーセプトロンは、複数の信号を入力として受け取り、ひとつの信号を出力します。
パーセプトロンが出す信号は2値
入力信号x1, x2とその重みw1, w2
積の総和が閾値θを超えるかどうかの2値
$ y=\begin{cases} 0 & (w_1 x_1 +w_2 x_2 \leq \theta )\\ 1 & ( w_1 x_1 +w_2 x_2 >\theta )\end{cases}
閾値を超える=> 「ニューロンが発火する」
バイアス$ b
θの条件を変形
$ y=\begin{cases} 0 & (b+w_1 x_1 +w_2 x_2 \leq 0 )\\ 1 & (b+ w_1 x_1 +w_2 x_2 >0 )\end{cases}
閾値からバイアスへの変換$ \theta = -b
0が基準になって計算しやすい
計算結果が0を上回れば1で、それ以外は0
配列の計算へ書き換え
$ w_1 x_1 +w_2 x_2 = $ \begin{pmatrix} x_1 & x_2 \end{pmatrix} \cdot \begin{pmatrix} w_1 \\ w_2 \end{pmatrix}
ANDゲートの実装
code:py
def AND(x1: float, x2: float) -> Literal0, 1: bias = -1
tmp = np.sum(x * w) + bias
return 1 if tmp > 0 else 0
AND、NAND、ORがw, bの違いだけで同じ構造になる
XOR
1層のパーセプトロンではXORは表現できない
線形な領域で分離できない
多層のパーセプトロンによって実現する
XORをNAND, AND, ORの組み合わせで実装する(NOTは単項の入力なのでパーセプトロンにはならない)
OR: $ A + B
AND: $ A \cdot B
NAND: $ \overline{A \cdot B}
XOR: $ \overline{A} \cdot B + A \cdot \overline{B}
$ = \overline{A} \cdot B + A \cdot \overline{B} + \overline{A} \cdot A + B \cdot \overline{B} ($ \overline{X} \cdot X は空集合なので足しても影響ない)
$ = A \cdot (\overline{A} + \overline{B}) + B \cdot (\overline{A} + \overline{B}) (分配法則)
$ = A \cdot (\overline{A \cdot B}) + B \cdot (\overline{A \cdot B}) (否定の和は積の否定)
$ = (A + B) \cdot (\overline{A \cdot B}) (分配法則)
つまり ORとNANDのANDで実装できる
code:py
def XOR(x1: float, x2: float) -> Signal:
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
XORは2層パーセプトロンで実装できた
そして多層パーセプトロン multi-layered perceptron へ
3章 ニューラルネットワーク
パーセプトロンは重みとバイアスを人間が設定する
ニューラルネットワーク: 適切な重みパラメータをデータから自動学習する
ニューラルネットワークの構成
入力層
中間層(隠れ層)
この層のニューロンは人の目には見えない
出力層
バイアスをパラメータとして明示的に示した表現$ y=h(b+ w_1 x_1 + w_2 x_2)
$ h(x) は0以下 or 超過を0/1に変換する2値化関数
活性化関数 activation function
$ h(x)が入力信号の総和が どのように活性化(発火)するかを決定する
また式の変形
入力信号の総和を計算と、活性化関数による評価の2段階であることを明示化
$ \begin{array}{l} {a = b + w_1 x_1 + w_2 x_2 } \\ {y = h(a)} \end{array}
シグモイド関数
パーセプトロンの活性化関数はステップ関数(階段関数)
閾値を超えるかどうかで切り替わる関数
ステップ関数以外の活性化関数
ニューラルネットワークの世界へ!
$ h(x) = \frac{1}{1 + \exp(-x)}
https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/SigmoidFunction.png/1200px-SigmoidFunction.png
ステップ関数のPython実装
code:py
def step_function(x: float):
return 1 if x > 0 else 0
numpyの配列(ベクトル)に対して計算できるようにする
code:py
def step_function(x: np.ndarray):
y = x > 0
return y.astype(np.int8)
本には np.int と書かれていたがPylanceに怒られる。型チェックでエラーになるようだ
0と1だけあればいいので np.int8にした
ワンライナー
code:py
def step_function(x):
return np.array(x > 0, dtype=np.int8)
/lacolaco/lacolaco.icon これに限らないがこの本のこれまでの読者が写経コードをGitHubに上げているおかげかCopilotでほぼ完全なコードが出てきてウケる
さすが20万部売れてるだけある
シグモイド関数の滑らかさがニューラルネットワークの学習において重要な意味を持ちます
ニューラルネットワークで流れる信号は2値ではない
0〜1の実数で値が流れる
非線形関数
活性化関数は非線形関数であることが条件
線形関数は関数合成できてしまう(単層になる)
ReLU関数
Rectified Linear Unit
正規化線形関数 とも
負数にならないよう正規化された線形関数、ということか
0以下は0、それ以上はそのまま
$ h(x) = \begin{cases} x & (x > 0) \\ 0 & (x \leq 0) \end{cases}
実装はシンプル np.maximum(0, x)
多次元配列によるニューラルネットワークの実装
層の中のノードの数が増えても1回の計算でいいのが利点
3層ニューラルネットワークの実装
$ A^{(1)} = XW^{(1)} + B^{(1)}
$ A^{(1)} = \begin{pmatrix} a^{(1)}_1 & a^{(1)}_2 & a^{(1)}_3 \end{pmatrix}
$ X = \begin{pmatrix} x_1 & x_2 \end{pmatrix}
$ B^{(1)} = \begin{pmatrix} b^{(1)}_1 & b^{(1)}_2 & b^{(1)}_3 \end{pmatrix}
$ W^{(1)} = \begin{pmatrix} w^{(1)}_{11} & w^{(1)}_{21} & w^{(1)}_{31} \\ w^{(1)}_{12} & w^{(1)}_{22} & w^{(1)}_{32} \end{pmatrix}
A: 「重み付き和」
出力層の活性化関数 $ \sigma()
隠れ層の活性化関数とは区別する
identity_function 恒等関数
$ W^{(n)}の形がn+1層目のノードの数を決める
n層目のノードの数 x n+1層目のノードの数 の行列
$ B^{(n)}はそれに追従する
出力層の設計
分類問題と回帰問題
分類問題: あるデータがどのクラスに属するか(クラスタリング)
$ \sigma()= ソフトマックス関数
回帰問題: 入力データから数値の予測をする
$ \sigma()= 恒等関数
恒等関数
入力をそのまま返す
ソフトマックス関数
$ y_k = \frac{\exp(a_k)}{\sum_{i=1}^{n}{\exp(a_i)}}
$ nは出力層のノードの数
出力ノードの重み付き和$ a_i
(指数関数を無視すると)ノードの重み付き和を各ノードの重み付き和の総和で割っている
つまり、出力層全体に対する割合のようなものが出力される
他のすべてのノードの重み付き和が活性化に影響する
出力層全体
$ \sum_{k=1}^{n}{y_k} = \frac{\exp(a_1) + ... + \exp(a_n)}{\sum_{i=1}^{n}{\exp(a_i)}} = 1
出力層の活性化関数の出力の総和は1になる
ソフトマックス関数が計算してるのは本質的には重み付き和の比率なので重み付き和の数値そのものには意味がない
指数関数を変形して定数$ Cを引いても結果は変わらない
だから分類問題のような「確率」の計算に使える
出力層の各ノードが選択肢ひとつひとつに対応する
実際には分類問題で重要なのは最尤ノードだけなのでソフトマックス関数は省略される
重み付き和の大小関係だけでよい
学習フェーズでは計算するが推論フェーズでは省略されるのが一般的(計算コストが高い)
まず出力層のニューロンの数から決める
その問題によって分類したいクラスの数だけ設定される
推論処理
ニューラルネットワークの順方向伝播 (forward propergation)
MNISTの手書き数字推論
ニューラルネットワークの実装
入力層: 784 (28 * 28)
出力層: 10
隠れ層のニューロン数は任意
1層目: 50
2層目: 100
4章 ニューラルネットワークの学習
ニューラルネットワークの学習
最適な重みパラメータの自動的な獲得
損失関数の値が最小になるパラメータを探索する
データから特徴量を抽出するアプローチ
特徴量のパターンを学習する
データから特徴量をどのように取り出すかは人間の設計による
ディープラーニングは「end-to-end machine learning」と呼ばれることもある
最初から最後まで機械で学習する
損失関数 loss function
ニューラルネットワークの現在の状態を表す
ニューラルネットワークの性能の悪さを示す指標
2乗和誤差 sum of square error
$ E = \frac{1}{2} \sum_k{(y_k - t_k)^2}
$ y_kはニューラルネットワークの出力のk番目
$ t_kは教師データのk番目
差の二乗の総和の0.5倍
交差エントロピー誤差 cross entropy error
$ E = -\sum_k{t_k \log y_k}
教師データをone-hot表現とすると単純に $ E = -\log {y_k}
$ {y_k}が1のとき $ \log1は0
$ {y_k}が0に近づくほどEは大きくなる
ミニバッチ学習
訓練データ全件で学習するのは時間がかかるため無作為に選んだ少数で学習する
np.random.choice()
微分
勾配(gradient) すべての変数の偏微分のベクトル
勾配が示す方向は各場所において関数の値を最も減らす方向
損失関数の最小値を探索するのに利用されるが
その点の近傍において値を小さくする方向なので関数の最小値かどうかはわからない
極小の場所がわかるだけ
関数が複雑だと勾配が指す方向が最小値じゃなくなることが多い
勾配法
ある地点から勾配方向に一定距離進み、また勾配を求め繰り返し移動する
ニューラルネットワークの学習でよく用いられる
$ x_0 = x_0 - \eta \frac{\sigma f}{\sigma x_0}
$ \etaが 学習率
1回の学習でどれだけパラメータを変動させるかの係数
偏微分の値に学習率を掛けた値ぶんだけ引いていく
ニューラルネットワークに対する勾配
損失関数を$ Lとすると、重みパラメータに関する勾配は
$ \frac{\sigma L}{\sigma W}
どの重みパラメータをどう変化させると損失関数がどう変化するか
確率的勾配降下法 stochastic gradient descent, SGD
無作為に(確率的に)選ばれたミニバッチに対して勾配降下法を行うこと
損失関数に数値微分を使うのは遅い
次章の誤差逆伝播法で改善される
5章 誤差逆伝播法
順伝播
計算を左から右へ進める
局所的な計算が伝播していくことで全体の結果が得られる
逆伝播
微分を効率よく計算できる
局所的な微分を右から左へ伝播していく
連鎖律
合成関数の微分
ある関数が合成関数で表される場合、その合成関数の微分は、合成関数を構成するそれぞれの関数の微分の積によって表すことができる。
$ \frac{\sigma z}{\sigma x} = \frac{\sigma z}{\sigma t} \frac{\sigma t}{\sigma x}
は〜〜賢い人は賢いことを考える...
感動している
numpyの ndarray#T 便利すぎんか?
特に説明なく出てきた気がするがびっくりした
ソフトマックス関数の損失関数としての「交差エントロピー誤差」
逆伝播が $ y-t の形になる
キレイ!
そうなるように作られた
ソフトマックス層の出力と教師ラベルの差がそのまま逆伝播になる
これが誤差逆伝播法
6章 学習に関するテクニック
学習のパラメータ最適化の手法あれこれ
確率的勾配降下法 (SGD)の欠点
関数が等方的じゃない場合に非効率な動き
次元ごとに傾きが違うときに弱い
Momentum
運動量パラメータと速度の概念
抵抗を受けながら動く
動き出しは遅い
傾きで加速する
反対方向の傾きで減速する
AdaGrad
Adaptive Gradient
更新されるごとにこれまでの勾配の2乗和を記憶し続ける
学習係数を減退させ、よく動いたパラメータは学習が緩やかになっていく
Adam
AdaGrad + Momentum
多くの研究ではいまでもSGDが使われている
最近はAdamが好まれがち
重みパラメータ
重みは小さい方が過学習が起きにくい(汎化性能が高い)
/lacolaco/lacolaco.icon なんとなくわかりそうな気がするが、なんで?
重みが大きいと入力が少し変動したときにも出力が大きく変わってしまうけど、逆に重みが小さいと入力の少しの変動なら出力への影響が小さい。出力の上下動が抑えられて過学習しづらくなる…みたいな感じ。
重みがいくつ以上なら大きいという基準もない
過学習が起きているとき、とりあえず重みを小さくしてみて改善したら重みが大きすぎたということになる感じ
THE・経験則
重みは均一になってはいけない
すべてのパラメータが均一に更新されるようになる
ネットワーク化した意味がなくなる
100個のニューロンがほぼ同じ値を出力するならそれは1つのニューロンと同じ
各層のアクティベーションの分布は適度に広がりを持つこと
Xavierの初期値
線形な活性化関数を想定している
Heの初期値
ReLU専用の初期値
Batch Normalization / Batch Norm
アクティベーションの分布が広がりを持つように強制的に補正するレイヤーを追加する
利点
初期値への依存度が下がる
学習が速い
過学習
訓練データだけに適応しすぎてしまうこと
原因
パラメータが大量で表現力が高いモデル
訓練データが少ない
Weight decay 荷重減衰
重みパラメータが大きな値を取ることにより過学習が発生することが多いため、抑制しよう
ノルム: ベクトルの長さのような何か
L1ノルム: 各要素の絶対値の和
L2ノルム: 各要素の2乗和
L∞ノルム: 各要素の絶対値のうち最大値
Dropout
訓練時にランダムに選ばれた隠れ層のニューロンを除去する
推論時には使う
アンサンブル学習の擬似的な実装
アンサンブル学習: 異なる個別のモデルの推論を平均するやりかた
ハイパーパラメータ
ハイパーパラメータを適切に設定しないと性能が出ない
試行錯誤するところ
ハイパーパラメータの検証にテストデータを使ってはいけない
ハイパーパラメータがテストデータに対して過学習してしまう
ハイパーパラメータ調整用の検証データを別に確保しておく
訓練データで学習、検証データでハイパーパラメータの評価、テストデータで汎化性能の評価
7章 畳み込みニューラルネットワーク
CNN / Convolutional Neural Network
全結合
隣接する層のニューロン間すべてが結合している
Convolution->ReLU->Pooling
序盤に置かれる
全結合層の問題
データを1次元に落とす 元の形状を無視する
Convolutionレイヤは元の形状を維持する
特徴マップ
畳み込み演算は、画像処理で言うところの「フィルター演算」に相当します
フィルターのパラメータ = CNNの重み
パディング
入力データの周囲に固定データを埋める
目的: 出力データのサイズを調整する
畳み込み演算を何度も行う中でサイズが減って1になると畳み込みができなくなる
ストライド
フィルターの適用間隔
大きくすると出力サイズが小さくなる
3次元の畳み込み演算
2次元の入力特徴マップが複数枚
チャンネルごとにフィルター、出力は1つ
立体には立体のフィルター(入力とフィルターは同じ次元)
フィルターの個数 FNが出力特徴マップを増やす
Pooling
空間を縮小する演算
学習するパラメータがない
ウインドウサイズだけ(それもストライドと一致する)
チャンネル数は変化しない
僅かな入力データのズレを吸収できる
im2col
フィルターの適用対象領域を行列化して演算しやすくする
CNNの可視化
フィルターは学習が進むにつれて規則性が出てくる
エッジ(境目)やブロブ(局所的な塊)に反応する
「ここにエッジがある」というようなプリミティブな情報が後段に渡される
CNNの層が深くなると抽出される情報はより抽象的になる
エッジ・ブロブ=>模様=>パーツ=>モノ
8章 ディープラーニング
ディープラーニング=層を深くしたニューラルネットワーク
Data Augumentation
データ拡張
入力画像をアルゴリズムによって人工的に拡張
回転、移動、切り出し、反転...
簡単に訓練データを増やせる
層を深くすることのモチベーション
理論的にはよくわかってない
深くすると浅いときよりネットワークのパラメータが少なくていい
小さなフィルターで重ねがけすれば大きなフィルターと同じ効果が得られる
解きやすいシンプルな問題の積み重ねとして解かせる
高速化のために数値計算に不要なビットを削減するトレンド
強化学習
試行錯誤の過程から学習させる
エージェント
行動した結果の報酬
よりよい報酬を目指して自立的に学習する
DQN
AlphaGo