21. MLP

# Multi Layer Perceptron
　自動微分と分けるのはいいのですが、CNNと分けるべきかというのは非常に疑問です。しかし、分けてしまったので、このまま書いてみたいと思います。

　Multi Layer Perceptron（多層パーセプトロン）は、深層学習を習う上で一番初めに聞く項目だと思います。画像のような一つのデータが2次元の行列のような形式ではなく、一行にすべて並んでいる状態を入力とします。最も単純なパーセプトロンは、
```math
z = \boldsymbol{w}^T x + w_0
```
のように複数の入力を一つの出力へ変換するものです。これは人間の神経細胞を模倣しているというような話をよく聞いたのですが、どこまで本当の話なんでしょうか？ともかく、形式としてはこのように何らかの変換を掛けるものです。さらに、さらにこれを複数出力にしたうえで、層も複数にし、かつ各層の出力に非線形関数を適用する（もしかしたら定義的には非線形関数は別に要らない？）ものが多層パーセプトロンです。
```math
X_1 = \phi_0 \left( X_0 \boldsymbol{W}_0 + w_0 \right) \\
X_2 = \phi_1 \left( X_1 \boldsymbol{W}_1 + w_1 \right) \\
\cdots
```
非線形関数をかませる理由としては、ない場合において、
```math
X_N = \boldsymbol{W}_N \cdots \boldsymbol{W}_0 X_0 = \boldsymbol{W}' X_0
```
と結局一つの線型変換でかけてしまうので、複数層を構成する必要がないからです。なぜこれほど深層学習が近似能力が高いかという理由を研究されている方もいらっしゃいます。深層学習分野では実験が先行しており、理論があまり追随できていないようです。物理系の実験（特に加速器実験）と違い、ひとつづつの実験はかなり安い（巨大IT企業にとっては）ので、実験が先行しているのではないでしょうか。確かに理論が解明されれば、適用方法や範囲、挙動がより明確にわかり、安全に安心して利用ができるようになると思います。数式に慣れていないので全く追えていませんが。


# 活性化関数
　活性化関数は大量にあり、[Pytorchの実装](https://pytorch.org/docs/stable/nn.html#non-linear-activations-weighted-sum-nonlinearity)でも30種類あります。おそらくこれだけでなく、発表されてはいるがPytorch実装に取り込まれていないものも多数あるはずです。いくつかの活性関数を可視化してくれている[サイト](https://itnext.io/ultimate-guide-to-activation-functions-37740be5942)もあります。

　どの活性化関数を使うかもデータセットとニューラルネットワークの構造に依存するため、一概に決めることができません。どの活性化関数も少なくとも論文発表時にはState-of-the-artであったはずなので、悪くなることはないのではと思います。といってもあまり使われないものもあります。Simoid関数が中間層に使われることはあまりないはずです。原因としては、入力が少しでも大きくなると微分が０になり、Backpropagationでのパラメータ更新が正常に行えなくなることがあるからです。ReLU関数$ = \text{max}(0,x)$はそのような心配がないので、とりあえずつかうのであれば、ReLUだと思います。　

　一般的には（実装の面倒くささのためがほとんど）ネットワーク全体でだいたい同じ活性化関数を使います。[ResNet](https://arxiv.org/abs/1512.03385)では、Residual Blockと呼ばれる構造を作り、それを何度も繰り返します。この時活性化関数は同じものです。本当に最適化するのであれば、ReLU→Sigmoid→ReLU6→GELUなどいろいろな関数も試すべきですが、無限に時間があるわけではないで、そのようにしていると思います。


# 正規化層（？　Normalizing Layer）
　活性化関数は主に入力データの一つ一つに作用します。N行M列のデータ（行ごとに一つのサンプル、各列が特徴量）が入力された場合に同じ行の中のデータを使って変換を行う関数（Softmaxなど）、同じ行の中でも完全に独立に変換を行う関数（ReLUなど）ありますが、ほかの行のデータが変換に影響を与えないことは共通です。

　この層は一度にニューラルネットワークに流されるデータ（ミニバッチ）の影響も受けます。多分最も古いものがBatchNormです（[特許が取得されている](https://aru47.hatenablog.com/entry/2021/01/25/064749#Batchnorm)）。簡単に言うと入力データを正規化（平均０、分散１）にするものです。正規分布であると学習が容易になる（入力の分布が同一であると容易？）ようで（要出典）、もともとはそのような思想でしょうか。

　可視化してくださっている[ページ](https://hotcocoastudy.hatenablog.jp/entry/2019/10/12/112213)がありました。ぱっと見わかりづらいですが、どの軸（Width、Hight、Channel）と範囲（バッチ）で正規化を行うかのはずです。



# 損失層（Loss Layer）
　こちらは、学習がどれだけうまくいっているかを算出するものです。ほかの層はスカラーでも行列でも出力できると思いますが、こちらはスカラーしか出ないはずです。正解とモデルの出力を同時に入力し、スカラーを出力します。「損失」なので、小さければ小さいほど良いことを示します（統計学を勉強していて面白いなと思ったのは、かなり消極的な肯定しかしなことです。できることが増える以上に、できないことや適用できない範囲が増える印象です。）。自動微分が適用できるためには微分可能な関数で定義されていなければなりません。

　これらの層は基本となる演算の層（加減乗除、累乗、絶対値など）の組み合わせで作ることも可能ですが、フレームワークで用意されているものを使う方が無難です。自動微分ではすべての層の入力を保持していなければなりませんが、損失層のように内部で固定の複数の処理を行う場合は、その必要がありません。そのためメモリの消費を軽くすることができます。

　また、損失関数の中には[このような関数](https://pytorch.org/docs/stable/generated/torch.nn.BCEWithLogitsLoss.html#torch.nn.BCEWithLogitsLoss)もあります。通常モデルの出力部分はSimoidやSoftmaxを掛け、その出力を損失関数に投入します。しかしこれは数値的に不安定です。logに0を入れてしまったりということが発生します（FortLearner実装中にも発生しました）。これらを避けるためにも組み込まれているものを使うようにしましょう。




CNN系で使われるものはそちらに回します。


# Optimizer
　最適化を行うものでこちらも多数[用意](https://pytorch.org/docs/stable/optim.html?highlight=optimizer#torch.optim.Optimizer)されています。SGDが最も単純なもので、
```math
\theta_1 = \theta_0 - \eta \nabla_\theta L
```
のように更新を行います。ほかのアルゴリズムはこれに新しい交がついたり、学習率$\eta$を適応的に変化させたりというものです。数年前にみたOpmtimizer自体の様々な形を試している論文がありました。見ていて何の意味があるんだろうと思いました。

　うまく調整すればSGD+momentumでもよい性能が出るのですが、非常に非常に面倒なので素直にAdamを使うことをお勧めします。劇的な精度向上はない（例えばAccuracyが0.5から0.8など）と思っていたのですが、SGDから差し替えると大幅に向上があり、かなりおどろきました。



# 実装済みの部分
　FortLearnerではかなり初歩的な部分しかできていません。加減乗除などの基本的な演算処理を行う層を除けば、
* DenseLayer
* ReLU、Sigmoid、Softmax

くらいです。次はパラメータを持つ活性化関数層、例えばParametricReLUなどを実装して拡張がどれほど面倒なのかを確認したいと思っています。





MLPとは？という感じになってしまいましたが、以上です。