# ANN 理論
## Activation Function (激勵函數)
讓資料可以被微分
[Activation Function List](https://en.wikipedia.org/wiki/Activation_function)
## Backpropagation (反向傳播)
利用更新 權重 來影響 ANN 的結果 (看 [SDG](#SGD-隨機梯度下降法))

### Cost Function (成本函數)
#### MSE (均方誤差)
基本上是變異數
$$Error=(\hat{X}-X)$$
$$MSE=\frac{1}{n}\sum_{i=1}^{n}(Error_{i})^2$$
$\hat{X}$是AI預期值
$X$是真實輸出值
$n$是觀察的數目
### SGD (隨機梯度下降法)

梯度下降常會遇到卡在 Local Minmum ,因為其原則是 ==如果斜率是負的就繼續==,但在遇到 Local Minmum 就會卡住,所以要用Momentum
SGD 公式
$$W_x^{t+1}=W_x^t-a\nabla f(W_x^t)$$
$a$是Learning Rate (學習率)
$W_x^{t+1}$是新的權重
$W_x^t$是舊的權重
$t$是更新次數
<!-- Momentum 公式 -->
將$Error$對$Weight_{i}$微分:
$\frac{dError}{dWeight_{i}}=\frac{\sum (x_i-f(x_i))^2}{dWeight_{i}}=\frac{\sum x_i^2+f(x_i)^2-2x_if(x_i)}{dWeight_{i}}$
$=(\sum\frac{x_i^2+f(x_i)^2-2x_if(x_i)}{df(x_i)})*\frac{df(x_i)}{dWeight_{i}}=\sum(2f(x_i)-2)*\frac{df(x_i)}{dWeight_{i}}$
其中對每一次的batch而言,$\sum(2f(x_i)-2*x_i)$是常數。
$\frac{df(x_i)}{dWeight_{i}}$可以透過自動微分快速取得。
## 自動微分
如果假設
$$e=(a+b)(b+1)$$
那他等於
$c=a+b$
$d=b+1$
$e=cd$
轉成樹會長這樣

帶入值($a=2,b=1$)會長這樣

使用前向模式微分
公式是 $\Sigma子節點\times邊$

使用後向模式微分
公式是 $\Sigma父節點\times邊$

實作
`nodes.go`
```go=
package gmath
type calcPartial func(float64, float64) (float64, float64)
type calcValue func(float64, float64) float64
type Node struct {
// is (left side value,right side value)(left side partial,right side partial)
calcPartial calcPartial
// is (left side value,right side value)(this value)
calcValue calcValue
// diff
partial float64
// calc number & const number value
value float64
rSide, lSide *Side
}
type Side struct {
// child node
node *Node
// diff
partial float64
}
func Const(value float64) *Node {
return &Node{
value: value,
}
}
func Varible(value float64) *Node {
return &Node{
value: value,
}
}
func (s *Node) Calc() {
s.forward()
s.partial = 1
s.backward()
}
func (s *Node) Set(value float64) *Node {
s.value = value
return s
}
func (s *Node) Partial() float64 {
return s.partial
}
func (s *Node) forward() float64 {
if s.calcValue != nil {
s.value = s.calcValue(s.lSide.node.forward(), s.rSide.node.forward())
}
if s.calcPartial != nil {
s.lSide.partial, s.rSide.partial = s.calcPartial(s.lSide.node.value, s.rSide.node.value)
}
return s.value
}
func (s *Node) backward() {
if s.rSide != nil {
s.rSide.node.partial += s.partial * s.rSide.partial
s.rSide.node.backward()
}
if s.lSide != nil {
s.lSide.node.partial += s.partial * s.lSide.partial
s.lSide.node.backward()
}
}
func newNode(calcValue calcValue, calcPartial calcPartial) *Node {
return &Node{
calcPartial: calcPartial,
calcValue: calcValue,
}
}
func newSide(value *Node) *Side {
return &Side{
node: value,
}
}
```
`math.go`
```go=
package gmath
import "math"
func Add(value ...*Node) *Node {
return buildOP(valueAdd, partialAdd, value...)
}
func Sub(value ...*Node) *Node {
return buildOP(valueSub, partialSub, value...)
}
func Mult(value ...*Node) *Node {
return buildOP(valueMult, partialMult, value...)
}
func Div(value ...*Node) *Node {
return buildOP(valueDiv, partialDiv, value...)
}
func buildOP(calcValue calcValue, calcPartial calcPartial, value ...*Node) *Node {
node := newNode(calcValue, calcPartial)
for _, v := range value {
if node.rSide == nil {
node.rSide = newSide(v)
continue
}
if node.lSide == nil {
node.lSide = newSide(v)
continue
}
newnode := newNode(calcValue, calcPartial)
newnode.rSide, node = newSide(node), newnode
}
return node
}
func partialAdd(lvalue float64, rvalue float64) (float64, float64) {
return 1, 1
}
func partialSub(lvalue float64, rvalue float64) (float64, float64) {
return -1, -1
}
func partialMult(lvalue float64, rvalue float64) (float64, float64) {
return rvalue, lvalue
}
func partialDiv(lvalue float64, rvalue float64) (float64, float64) {
return -math.Pow(lvalue, -2) * math.Pow(rvalue, -1), -math.Pow(rvalue, -2) * math.Pow(lvalue, -1)
}
func valueAdd(lvalue float64, rvalue float64) float64 {
return lvalue + rvalue
}
func valueSub(lvalue float64, rvalue float64) float64 {
return -lvalue - rvalue
}
func valueMult(lvalue float64, rvalue float64) float64 {
return lvalue * rvalue
}
func valueDiv(lvalue float64, rvalue float64) float64 {
return 1 / (lvalue * rvalue)
}
```
使用
```go=
a := Varible(2)
b := Varible(3)
c := Add(Mult(a, b), a)
c.Calc()
// 4 2
print(a.Partial(), b.Partial())
```
## 基本數學概念
### 微分
假設有一個函數$f(x)=x^{n}$,將它微分完會變成$f'(x)=nx^{n-1}$
範例
$f(x)=x^{5}$ 會變成 $f'(x)=5x^{4}$
$f'$代表微分一次,$f''$是兩次,以此類推
### 偏微分
偏微分是選擇曲面中的一條切線,並求出它的斜率
假設$f(x,y)=x^2+2xy+y^2$,
$$\frac{\partial f}{\partial y}=2x+2y$$
意思是指將$y$當成自變量,而$x$當成常數的微分結果
將$y$當成自變量,$x$當成常數的函數標示為$f_x(y)=\cdots$
### 向量
敘述一個空間的基本元素
~~空間角可以用內積公式取得~~
<!-- 貌似不行,但cos theta確定可以 -->
$\vec{x}$=($x_{1}$,$x_{2}$,$x_{3},...)$
$|\vec{x}|=\sqrt{x_{1}^{2}+x_{2}^{2}+x_{3}^{2}+\cdots}$
<!-- 以下省略 -->
$\cos\theta$=$\frac{\vec{x}\vec{y}}{|\vec{x}||\vec{y}|}$
$\cos\theta$又稱相關係數
### 向量微分
先跟Nabla說聲好
$$\nabla f(x)$$
一維度的向量x的梯度$f'(x)$
多維度的向量x的梯度用$\nabla f(x)$
<!-- 我不懂... -->
<!-- +1 by Hugo -->
<!-- working on it -->
### 矩陣
這是一個矩陣
$$
\begin{bmatrix}
a & b \\
c & d
\end{bmatrix}
$$
方陣
二階方陣
$$
\begin{bmatrix}
1 & 0 \\
0 & 1
\end{bmatrix}
$$
矩陣裡只有加減乘,不一定能除
除的話要乘以其-1次方
$A^1*A^{-1}=方陣$
這是轉置矩陣(上面有個T),如果轉置2次等於沒轉置
$$
\begin{bmatrix}
a & b \\
c & d
\end{bmatrix}^T=
\begin{bmatrix}
a & c \\
b & d
\end{bmatrix}
$$
$$
\begin{bmatrix}
a_1 \\
a_2
\end{bmatrix}
\begin{bmatrix}
b_1 b_2
\end{bmatrix} =
\begin{bmatrix}
a_1*b_1+a_2*b_2
\end{bmatrix}
$$

### exp
$exp(x)=e^x=2.71828182846...^x$
e是一個實數無理數,e=2.71828182846...
推導如下:
假設銀行年利率為1,一年算一次利息,設總金額為$x$,則$f(time)=x*2^{time}$
那如果改為每極短時間記一次利息,且時間反比於利息。
則新年利率為
$$\lim_{n\to\infty}(1+\frac{1}{n})^n$$
e在微積分時十分便利。
## 參考資料
- [build artificial neural network scratch part 1](https://www.kdnuggets.com/2019/11/build-artificial-neural-network-scratch-part-1.html)
- [偏微分](https://zh.wikipedia.org/wiki/%E5%81%8F%E5%AF%BC%E6%95%B0)
- [梯度最佳解相關算法](https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8%E5%AD%B8%E7%BF%92-%E5%9F%BA%E7%A4%8E%E6%95%B8%E5%AD%B8-%E4%B8%89-%E6%A2%AF%E5%BA%A6%E6%9C%80%E4%BD%B3%E8%A7%A3%E7%9B%B8%E9%97%9C%E7%AE%97%E6%B3%95-gradient-descent-optimization-algorithms-b61ed1478bd7)
- [梯度下降](https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8%E5%AD%B8%E7%BF%92-%E5%9F%BA%E7%A4%8E%E6%95%B8%E5%AD%B8-%E4%BA%8C-%E6%A2%AF%E5%BA%A6%E4%B8%8B%E9%99%8D%E6%B3%95-gradient-descent-406e1fd001f)
- [自動微分](https://blog.csdn.net/aws3217150/article/details/70214422)
- [Backpropagation | Brilliant Math & Science Wiki](https://brilliant.org/wiki/backpropagation/)
- [矩陣](https://zh.wikipedia.org/wiki/%E7%9F%A9%E9%98%B5)
- [backprop](http://galaxy.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html)
- [機器學習](https://medium.com/%E9%9B%9E%E9%9B%9E%E8%88%87%E5%85%94%E5%85%94%E7%9A%84%E5%B7%A5%E7%A8%8B%E4%B8%96%E7%95%8C/%E6%A9%9F%E5%99%A8%E5%AD%B8%E7%BF%92ml-note-sgd-momentum-adagrad-adam-optimizer-f20568c968db)
- [error function](https://stackoverflow.com/questions/22601258/error-function-in-artificial-neural-network-trained-using-backpropogation)
- [backpropagation](https://brilliant.org/wiki/backpropagation/)
- [自動微分](http://fancyerii.github.io/books/autodiff/)
*[Local Minmum]:區域最小值
*[ANN]:人工神經網路
*[權重]:W