ニューラルネットワークの基礎を学びたい(1)

#今回は何するの？
###ニューラルネットワークの仕組みを知る
表題に書きましたが、ニューラルネットワークの仕組みを理解し、応用できるように基礎を学んでみましたので、それをまとめていきたいと思います。
今回はこちらを参考にさせていただきました。


https://qiita.com/takahiro_itazuri/items/d2bea1c643d7cca11352


#勉強開始
###ニューラルネットワークってなに？
これは…記載しなくてもわかるわぁ〜という人も多いような…？
まぁ、後で見返した時のためにも記載しておきましょうか
>ニューラルネットワーク(Neural Network)
>※(NNと書かれることもあります)
>脳の神経回路の一部を模した数理モデル、または、パーセプトロンを複数組み合わせたものの総称
>入力値を関数に入れて、ある値を出力します。

ふむふむ、重要な部分は**「脳の神経回路の一部を模した数理モデル、または、パーセプトロンを複数組み合わせたものの総称」**ですね。
さて、ここでさらに注目するポイントは**パーセプトロン**ですね。
>「パーセプトロン(perceptron)」とは人間の脳にあるニューロンを模倣しています。

人間の脳にある…ニューロン？なにそれ美味しいの？という方もいるかもしれないので補足

>ニューロン(neuron)とは生物の脳を構成する神経細胞のことで、情報処理と伝播能力において特に優れている。脳の神経構造は非常に複雑である。
>成人の脳の場合では、この細胞が100億から1000億程度であると言われている。
>三次元的に集まって、回路網を形成している。

だ、そうです。ふむふむ、この回路網は神経細胞が繋がっていることから**「神経回路網」**といわれ、この神経回路網が**「ニューラルネットワーク」**ということですね。OK?では次にいきましょう。

>ニューロンは複数のニューロンから樹状突起で電気信号を入力として受け取り、ある一定以上の刺激を受けると、軸索の末端部分から電気刺激を出し、次のニューロンに刺激を与えます。
>パーセプトロンはニューロンと同じように入力に重み(weight)を付けて複数から受け取り、その重み付け総和を活性化関数(activation function)を通すことで出力値を決定します。



#数学的表現
###数学的表現したらどうなるの…
数学が苦手な人がいれば…この段階から「頑張って…」というしか言えなくなりますが、私もあまり数学は苦手な方なので、気になる部分は頑張って補足を追加して行きたいと思います。
では、やっていきましょうか。
>行列は大文字、ベクトルは小文字で表現します

>入力を$n$次元ベクトル $o^{(i)}$。重み係数を$m$行$n$列の行列$W$、活性化関数を$\phi$、出力を$m$次元ベクトル$o^{(o)}$とすると、先ほどのパーセプトロンは以下のように表せます。
>$$o^{(o)} = \phi(Wo^{(i)})$$

ふむ、ここは数学的表現をしていただいてるだけですね。
ここで、疑問に思うものとしてあげられるのは「重み係数」と「行列」、「活性化」、「次元ベクトル」でしょうか？活性化関数は、次で解説されているので、その他の3つを補足していきましょうか。行列は数学で習うのでそこまで補足はいらないように思いますが…。まぁいいでしょう。

######補足:重み係数
>重み係数(weight_coefficient)、重み(ウエイト)ともいう。
>複数の変数から合計変数を求める際に、各変数につける総称のこと
>この重み係数の大小と正負によって、それが付けられた変数の重要度と役割が理解される。
>例えば、2変数x、yから合成変数fを作る際、$f=0.87x+0.5y$といった一時結合を用いた場合、このように$α>β$のとき、$xがy$よりも合計変数$f$に大きく関係していることが言える。

http://www.ipc.hokusei.ac.jp/~z00105/wiki_res/wiki.cgi?%BD%C5%A4%DF%B7%B8%BF%F4#:~:text=weight_coefficient%20%E9%87%8D%E3%81%BF%EF%BC%88%E3%82%A6%E3%82%A8%E3%82%A4%E3%83%88%EF%BC%89%E3%81%A8%E3%82%82%E3%81%84%E3%81%86,%E5%BD%B9%E5%89%B2%E3%81%8C%E7%90%86%E8%A7%A3%E3%81%95%E3%82%8C%E3%82%8B%E3%80%82

######補足:行列
>行列とは、数学の線型代数学周辺分野における行列(ぎょうれつ:*matrix*)は、数や記号や式などを縦と横に矩形状(くけいじょう)に配列したものである。
>行・列
>横に並んだ一筋を**行**、縦に並んだ一筋を**列**と呼ぶ。
>例えば、下記のような行列
>
```math
\begin{vmatrix}
1 & 9 & -13\\
20 & 5 & -6
\end{vmatrix}
```
>は2つの行と3つの列によって構成されているため、(2,3)型または「2×3型」の行列と呼ばれる。

https://ja.wikipedia.org/wiki/%E8%A1%8C%E5%88%97

行列についての説明は、まだ結構あるんですが、長いので行列ってなに？という部分だけで…

######補足:次元ベクトル
最初に言っておきますが、ベクトルはその場面によって意味が若干変わってきますので、これもまた長くなりそうですが…私も後で見直した際に細かく書いてあると楽なので書いていきましょう

>ベクトル(独:Vektor)またはベクター(英:vector)
>ベクトルはドイツ語:Vektorに由来し、ベクターは英語:vectorに由来する。
**物理学などの自然科学の領域ではベクトル**、
**プログラミングなどのコンピュータ関係ではベクターと表記される**
また技術文書などでは、しばしばJIS規格に準拠する形で、長音を除いた**ベクタ**という表記が用いられる。

>数学理学
>>数学
>>ベクトル空間の元。線形性を持つ、すなわち和とスカラー倍を取る事ができる量。一般の(広い意味での)ベクトル。
>>数ベクトル
>>余ベクトル(双体ベクトル)、ベクトルの共変性と反変性。

>>数学・物理
>>幾何ベクトル・空間ベクトル - 幾何学空間における、大きさと向きを持った量。有向線分として捉えることができる。

>>>ベクトル積 - 数学的には、**n次元ベクトル空間**においてn-1個のベクトルに1つのベクトルを対応させるn-1項演算の一種。**3次元の場合のみ二項演算となり「積」らしくなるので、3次元に限って「ベクトル積」と呼ぶ場合も多い。**演算子に×を用いるので、クロス積とも呼ばれる。また、3次元では外積に一致するので、ベクトルの外積とも呼ばれる。

>>一階のテンソル - 線形性を持つ郡が作用する空間(テンソル)のうち階数が1つであるもの。郡を行列で表示したとき、数ベクトルとして表現される。幾何ベクトルは、幾何的な回転操作に対してベクトルとして振る舞う。

>>ベクトル場(数学) - 数学では、空間の各点がベクトル量を持つようなある種の関数
>>ベクトル場(物理学) - 物理学では、数学的な定義より限定的に、ある種の「場」を意味することがある
>>ベクトル解析 - ベクトルの演算記法を利用した解析学の方法。三次元空間または二次元空間上の問題を扱う際にしばしば利用され、電磁気学や流体力学など広い範囲で応用される。

>>物理学
>>擬ベクトル - 回転に対する変換性は通常のベクトルと同じであるが、反転に対する変換性が異なる量。軸性ベクトルとも呼ばれる。
>>4元ベクトル - 相対理論記述を必要とする分野で使われる4次元のベクトルで、ローレンツ変換に対してベクトルとして振る舞う。
>>ベクトル粒子 - 場の量子論においてベクトル場で表せるような粒子。スピン1を持つ。スピン統計より、必ずボーズ粒子であり、ベクトルボソンとも呼ばれる。光子、ウィークボソン、グルーオンといったゲージ粒子や、ロー中間子などの一部のメソン(ベクトルメソンと呼ばれる)が該当する。

>コンピュータ
>>1次元の配列(コンテナ)として表現されるデータ構造。
>>>C++言語のStandard Template Library(STL)における(std::vector)
>>ベクトル演算 - 並列計算の手法。ベクトル演算の対象となるデータ構造をベクトルと呼ぶ。
>>ベクトル計算機 - ベクトル演算が可能な処理系を目指す。
>>ベクトル化 - ループ処理をベクトル演算に変換すること。ベクトル計算機に対する最適化の目的で用いられる。他方2次元コンピュータグラフィックスの分野においては、点の集合で画像を表現したビットマップ画像を下記ベクタ形式に変換する処理を目指す。
>>ベクタ形式 - コンピュータグラフィックス(CG)の形式。2次元CGをコンピュータ内部で表現するデータ画像の代表的な2つのうち1つ。各図形が形状や大きさ、色、線の長さや方向、傾き、表示エリア内での位置などを基に表現する。
>>動きベクトル - 動画データの表現方法の一つ。フレーム間の移動量。

https://ja.wikipedia.org/wiki/%E3%83%99%E3%82%AF%E3%83%88%E3%83%AB

結構、ベクトルの種類ありますね。この中で重要になるのは**コンピュータのベクトル演算**ですね。
さて、補足はここまでとして、**「活性化関数」**に行きましょう。

#活性化関数
###活性化関数ってなにさ…
>脳のニューロンの話に少し戻しますと、入力が一定以上の場合に電気刺激を出す部分が活性化関数になります。このように一定以上の入力があるときに定数を出力する関数はステップ関数と呼ばれ、次のように書くことができます。
>
ステップ関数

>```math
\phi(x) = \left\{
\begin{array}{}
1 & (x \lt 0) \\
0 & (x \geq 0)
\end{array}
\right.
>```
>しかし、今回実際に用いるのはsigmoid関数です。その理由は誤差逆伝搬法の時に、活性化関数の微分値を用いるからです。sigmoid関数は以下のような式です。

>sigmoid関数

>```math
\phi(x) = \frac{1}{1+e^{-x}}
>```

>またsigmoid関数の微分は以下のようになります。
>$$\phi'(x) = x(1 - x)$$
>このように、sigmoid関数の微分は非常に簡単な形になり、**微分値を計算コストが低い事はニューラルネットワークでは非常に重要です。**

引用部分を太字にしましたが、なんで微分する必要があるんじゃぁ！？と思う人もいるかもしれません。私は思っていました。
ということで、なんで微分するかということを回答すると
=**微分値の計算コストが低い事はニューラルネットワーク(NN)では非常に重要**ということですね。
計算が楽だとそれにかけるコストが大幅に下がりますからね。うんうん
(記事を読ませていただき、「そういうことだったのか！！」と思いました。)
さて、ここで私は思いました。「活性化関数ってどんなのがあるんだ？」とね。
補足として他の活性化関数も見て行きましょうと言いたいところなのですが、量がかなりあったのでまたの機会に…。

参考として、活性化関数について記載されていた記事を見つけましたので、貼らせていただきます。

https://qiita.com/kuroitu/items/73cd401afd463a78115a

#ニューラルネットワーク
###あれ、さっきもこの話しなかったっけ？
はい、しました。まぁ、あれは人間的な意味の話なので、今回は?**機械的にどういう仕組みでやるのか？**ということを見て行きましょう
>ニューラルネットワーク
ニューラルネットワーク(neural network)は、パーセプトロンを複数個並べたものを層(layer)と呼び、その層を複数並べることで構成されます。

よく見る図ではありますが、まぁ…時間がある時にでも書きましょう…(時間があれば)

>注目した層に対して、1つの左の層から入力を受け取り、一つ右の層へ出力を行います。層間の繋がりがネットワークのようになっているので、ニューラルネットワークと呼ばれます。
>ニューラルネットワークはこの層間の繋がりの部分で重みが付けられて、次の層へ入力されます。この重み係数を更新していくことを**学習**と呼びます。この重み係数は、**「より正しい出力をするためにどの入力を選択的に重視するか」**を意味しています。

いい感じに分かってきましたね。さて、次は数学的表現を見て行きますよ。

(数学的表現の部分でも、図があるのですが、前でも言いましたが時間があったら書きます。時間があれば…ねっ！！)
###数学的表現
>今回は3層のニューラルネットワークを仮定します。添え字として、入力層(input layer)は$i$、隠れ層(hidden layer)は$h$、出力層(output layer)は$o$とし、数式では上付き括弧で記述し、ニューロンのインデックスを下付きで記述します。
>また各層のニューロンの数については、入力層が$n_i$個、隠れ層が$n_h$、出力層が$n_o$とします。
>またニューロンが縦線で二つに分かれており、左に$x$、右に$o$があります。$x$は入力の重み付け和の結果、$o$は活性化関数に$x$を入力した時の出力です。
ここでは隠れ層に着目すると、次のような関係があります。


>重み付け和
>$$x_j^{(h)} = \sum_{i}W_{ij}^{(i)}o_i^{(i)}$$

>活性化関数
>$$o_j^{(h)} = \phi(x_j^{(h)})$$

>重み係数$W^{(i)}$については、添え字$i$は入力層のノードのインデックス、添え字$i$は隠れ層のノードのインデックスを意味します。したがって、重み係数は次のようになります。

>
```math
W=
\left(
\begin{array}{ccccc}
W_{11} & W_{21} & \cdots & W_{n_i1}\\
W_{12} & W_{22} & \cdots & W_{n_i2}\\
\vdots & \vdots& \ddots &\vdots \\
W_{1n_h} & W_{2n_h} & \cdots& W_{n_in_h} 
\end{array}
\right)
>```

>**※普通の行列と添え字が逆になっているのに気をつけてください。**
>このように層ごとに伝播していくことを**順伝搬**と言います。

OK？どうですかな？ここまでついてこれていますか？
私は全く関係ないところで苦戦しました（Markdown記法に慣れてないのでね)
何度も読み返せば理解できると思いますよ。特に解説する部分がない…
次に行きましょうか。

#誤差逆伝搬法
>まず目的関数は損失関数*(loss function)*であり、これを最小化する最適化問題を解きます。誤差逆伝播法*(back propagation)*は勾配降下法*(gradient descent)*を用いています。
>勾配降下法は勾配(偏微分)と逆向きの方向に(誤差が小さくなる方向に)更新することを反復的に繰り返すことで、極小解を探す方法です。
>**※最小解ではないことに注意してください。**

OK？ここまでついてこれてるかな？
では、さらに続けましょうか

###損失関数
>損失関数として最も単純なものは**二乗誤差**を用いるものです。理想の出力であるベクトル(教師ベクトル)を$t$とすると、出力層における誤差は次のように書けます。
>
```math
E=\frac{1}{2}\sum_{i=1}^{n_o} (t_i - o_i^{(o)})^2
```
>このように二次形式の誤差を利用することの利点は、微分を行うことで一次元になることと、更新幅は勾配の大きさに合わせて変わることです。
>例えば、絶対誤差を用いた場合は勾配の大きさは常に一定になるので、更新幅も一定となり、極小解を行ったり来たりしてしまい、上手く収束しない可能性があります。

収束させるのが大事ですね。さて、損失関数も種類がありそうなので少し調べてみましょうか。
ですが、種類がまた多いのでサイトを貼り付けます。また今度まとめますね。

https://ai-trend.jp/basic-study/neural-network/nn_loss_function/

###隠れ層から出力層への重みによる偏微分
>勾配計算(偏微分)を行います。損失関数$E$を隠れ層から出力層への重み$W^{(h)}$で偏微分すると、連鎖律より次のようになります。
>
```math
\frac{\partial E}{\partial W_{jk}^{(h)}}=
\frac{\partial E}{\partial o_{k}^{(o)}}
\frac{\partial o_k^{(o)}}{\partial x_{k}^{(o)}}
\frac{\partial x_k^{(o)}}{\partial W_{jk}^{(h)}}\\
=\frac{\partial E}{\partial o_{k}^{(o)}}\phi'(x_k^{(o)})o_j^{(h)}
```
>ここで、次のようにベクトル$\delta^{(o)}$を定義する。
$$\delta_k^{(o)} = \frac{\partial E}{\partial o_k^{(o)}}\phi'(x_k^{(o)})$$
>したがって、次の式が導入される。
$$\frac{\partial E}{\partial W_jk^{(h)}} = \delta_k^{(o)}o_j^{(h)}$$

###入力層から隠れ層への重みによる偏微分
>
```math
\frac{\partial E}{\partial W_ij^{(i)}}=\sum_k(\frac{\partial E}{\partial o_k^{(o)}}\frac{\partial o_k^{(o)}}{\partial x_k^{(o)}}\frac{\partial x_k^{(o)}}{\partial o_j^{(o)}})\frac{\partial o_j^{(h)}}{\partial x_j^{(h)}}\frac{\partial x_j^{(h)}}{\partial W_ij^{(i)}}\\
=\sum_k(\frac{\partial E}{\partial o_k^{(o)}}\phi'(x_k^{(h)})W_{jk}^{(h)})\phi'(x_j^{(h)})o_i^{(i)}
```
>
>ここで、次のようにベクトル$\delta^{(h)}$を定義する。
>
```math
\delta_j^{(h)} = \phi'(x_j^{(h)})\sum_k(\delta_k^{(o)}W_{jk}^{(h)})
```
>
>したがって、次の式が導出される。
>
```math
\frac{\partial E}{\partial W_{ij}^{(i)}}=\delta_j^{(h)}o_i^{(i)}
```
>
>この式は、隠れ層から出力層への重みの勾配の式に、非常に類似しています。添え字以外で異なるのは$\delta$の部分です。
>$\delta$が異なる理由は、出力層に対しては教師ベクトルを与えることができる一方で、それ以外の層に対して教師ベクトルはないからです。
>ここで先程の$\delta(h)$と$\delta(o)$の関係式です。
>出力層以外の層の$\delta$は、一つ後ろの層の$\delta$に対して、順伝搬時に利用した重み係数で分配・統合することにより、計算します。
>このように誤差に関するベクトル$\delta$が後ろから伝搬していくので、誤差逆伝搬法と呼ばれます。

>行列の形で書き直しておくと、次のようになります。

>
```math
\delta^{(h)} = W^{(h)^T}\delta^{(o)}\\
=
\left(
\begin{array}{ccccc}
W_{11}^{(h)} & W_{12}^{(h)} & \cdots & W_{1n_o}\\
W_{21}^{(h)} & W_{22}^{(h)} & \cdots & W_{2n_o}\\
\vdots & \vdots& \ddots &\vdots \\
W_{n_h1} & W_{n_h2} & \cdots& W_{n_hn_o}^{(h)} 
\end{array}
\right)
\left(
\begin{array}{ccccc}
\delta_1^{(o)}\\
\delta_2^{(o)}\\
\vdots\\
\delta_{n_o}^{(o)}\\
\end{array}
\right)
```

>
>このように重みの部分は順伝搬の時の転置行列になります。

さて、ここで「転置配列ってなんだよぉ！！！」という疑問が浮びますが、スルーしましょう(あ、参考になりそうなものを貼っておきます)

https://ja.wikipedia.org/wiki/%E8%BB%A2%E7%BD%AE%E8%A1%8C%E5%88%97

###更新式
>上記のように求まった偏微分値を用いて、次の式で重み係数を更新して行きます。
>$$W \leftarrow W + \eta\frac{\partial E}{\partial W}$$

>$\eta$は学習効率*(learning rate)*と呼ばれるもので、予め定数で指定しておきます。
>このように人間が手で決めなければいけないパラメータは**ハイパーパラメータ**と呼ばれています。

さて、座学は終わりです。いい感じにニューラルネットワークの知識とその構造、計算方法等は揃いましたね。
後で、補足を追加したり追記したりしますね。

次こそ、コードを記載したい…
では、またいつもの挨拶文を置いて終わりにしましょう。

#挨拶
どうも、組込みエンジニアとして、とある企業におります粉です。

・では、組込みエンジニアですがAIエンジニア、機械学習エンジニアとして転職したいので、自身でいろいろと勉強を行っております。

・さて、なんでAIエンジニアや機械学習エンジニアになりたいの？と疑問をもたれる方も多いと思いますので、簡単に説明させていただきます。
１、元はAIを作ってみたい、社会問題、労働に関してもっと働きやすい環境が必要なのではないか？と考えたため様々な課題をAIなどの先端技術でアプローチをかけてみたいと思い、AIエンジニアを目指しました。
２、ですが、今やっているのは組込みエンジニアのため、使用する技術、言語が違うといったことから、独学でやってみようと思いまして、最近勉強をはじめました。
３、転職を目指す、自身の夢を叶えたいという願いもありますので、こうやって学んだことを記事しています。

・いろいろとお話ししましたが、まずは、私よりも知識、技術が豊富な先輩方から多くのことを意見、修正点、アドバイスをいただけることを楽しみにしています。

・まだまだ新参者、理解が足りていない部分もあるので記事を書きながら、修正を繰り返して理解を深めれればと思っております。(AIを研究している企業や研究機関に転職したいです。

・こんな手法もあるぞ、やり方はこうだ！！などの意見も募集しております。