---
# System prepended metadata

title: ANN 理論

---

# ANN 理論

## Activation Function (激勵函數)

讓資料可以被微分

[Activation Function List](https://en.wikipedia.org/wiki/Activation_function)


## Backpropagation (反向傳播)

利用更新 權重 來影響 ANN 的結果 (看 [SDG](#SGD-隨機梯度下降法))

![反向](https://i.imgur.com/MBbiUmW.gif)




### Cost Function (成本函數)

#### MSE (均方誤差)

基本上是變異數

$$Error=(\hat{X}-X)$$

$$MSE=\frac{1}{n}\sum_{i=1}^{n}(Error_{i})^2$$

$\hat{X}$是AI預期值
$X$是真實輸出值
$n$是觀察的數目

### SGD (隨機梯度下降法)

![](https://i.imgur.com/Qr7PnJE.png)

梯度下降常會遇到卡在 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$

轉成樹會長這樣

![](https://i.imgur.com/QDcWpoc.png)

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

![](https://i.imgur.com/bct9Qp8.png)

使用前向模式微分

公式是 $\Sigma子節點\times邊$

![](https://i.imgur.com/nDprToZ.png)

使用後向模式微分


公式是 $\Sigma父節點\times邊$

![](https://i.imgur.com/kIXOESf.png)

實作

`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}
$$

![轉置](https://i.imgur.com/82IbDU3.gif)

### 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