# **4.1.4~4.1.7**
## **1.多個樣本的正向計算**
* 前面已經舉過單個樣本的正向計算,現在把它推到多個樣本
- - -
多個樣本(m個樣本$\mathbf{x}^{(i)}$)的資料特徵可以組成一個矩陣$X$:
$$
\mathbf{X}=\begin{bmatrix}
\begin{array}{}
\mathbf{x}^{(1)}\\
\mathbf{x}^{(2)}\\
\vdots\\
\mathbf{x}^{(m)}\\
\end{array}
\end{bmatrix}
$$
每個樣本所對應的層輸出向量 $\mathbf{z}^{(1)[l]}$、$\mathbf{a}^{(1)[l]}$的矩陣
$\mathbf{Z}^{(l)}$、$\mathbf{A}^{(l)}$可以表示成
$$
\textbf{Z}^{[l]}=\begin{bmatrix}
\begin{array}{}
\mathbf{z}^{(1)[l]}\\
\mathbf{z}^{(2)[l]}\\
\vdots\\
\mathbf{z}^{(m)[l]}\\
\end{array}
\end{bmatrix}
$$
$$
\textbf{A}^{[l]}=\begin{bmatrix}
\begin{array}{}
\mathbf{a}^{(1)[l]}\\
\mathbf{a}^{(2)[l]}\\
\vdots\\
\mathbf{a}^{(m)[l]}\\
\end{array}
\end{bmatrix}
$$
其中,$\mathbf{z}^{(i)[l]}$、$\mathbf{a}^{(i)[l]}$分別是第$i$個樣本的第$l$層的加權和、啟動值,他們分別作為矩陣$\mathbf{Z}^{(l)}$、$\mathbf{A}^{(l)}$的第$i$行
把矩陣展開來寫:
$$
\textbf{Z}^{[l]}=\begin{bmatrix}
\begin{array}{}
\mathbf{z}^{(1)[l]}\\
\mathbf{z}^{(2)[l]}\\
\vdots\\
\mathbf{z}^{(m)[l]}\\
\end{array}
\end{bmatrix}=
\begin{bmatrix}
\begin{array}{}
\mathbf{a}^{(1)[l-1]}\mathbf{W}^{[l]}+\mathbf{b}^{[l]}\\
\mathbf{a}^{(2)[l-1]}\mathbf{W}^{[l]}+\mathbf{b}^{[l]}\\
\vdots\\
\mathbf{a}^{(m)[l-1]}\mathbf{W}^{[l]}+\mathbf{b}^{[l]}\\
\end{array}
\end{bmatrix}
$$
可以簡化成:
$$\textbf{Z}^{[l]} = \textbf{A}^{[l-1]}\textbf{W}^{[l]} + \textbf{b}^{[l]}$$
同樣的$\mathbf{A}^{(l)}$是$\mathbf{Z}^{(l)}$的啟動值:
$$
\textbf{A}^{[l]}=\begin{bmatrix}
\begin{array}{}
\mathbf{a}^{(1)[l]}\\
\mathbf{a}^{(2)[l]}\\
\vdots\\
\mathbf{a}^{(m)[l]}\\
\end{array}
\end{bmatrix}=
\begin{bmatrix}
\begin{array}{}
\mathbf{g}^{[l]}\mathbf{z}^{(1)[l]}\\
\mathbf{g}^{[l]}\mathbf{z}^{(2)[l]}\\
\vdots\\
\mathbf{g}^{[l]}\mathbf{z}^{(m)[l]}\
\end{array}
\end{bmatrix}
$$
可以簡化成:
$$\textbf{A}^{[l]} = \textbf{g}^{[l]}(\textbf{Z}^{[l]})$$
## **2.損失函數**
* 損失函數:與真實值進行誤差評估(也可稱損失或者代價)
***
### **1.均方差損失函數**
* 使用在一般的回歸問題
* 將所有預測值和真實值的差距平方取平均值作為誤差
* 公式:\
真實值:$F=(f^{(1)},f^{(2)},\ \cdots ,f^{(m)})^T$\
\
預測值:$Y=(y^{(1)},y^{(2)}\ , \ \cdots ,y^{(m)})^T$\
\
均方差損失如下\
\
$$L(F,Y)=\frac{1}{m}\sum_{i=1}^{m}||f^{(i)}-y^{(i)}||^2$$
\
為了讓求導梯度更好看,會將上式除以2,亦即
\
$$L(F,Y)=\frac{1}{2m}\sum_{i=1}^{m}||f^{(i)}-y^{(i)}||^2$$
***
### **2.二分類交叉熵損失函數**
* 使用在二分類問題
* 公式:\
\
真實標籤:$F=(f^{(1)},f^{(2)},\ \cdots ,f^{(m)})^T$\
\
預測機率:$Y=(y^{(1)},y^{(2)}\ , \ \cdots ,y^{(m)})^T$
$$L(f,y)=-\frac{1}{m}\sum_{i=1}^{m}[y^{(i)}log\ (f^{(i)})+(1-y^{(i)})\ log\ (1-f^{(i)})]$$
***
### **3.多分類交叉熵損失函數**
* 使用在多分類問題
* 公式:\
$f_c^{(i)}$表示第$i$個樣本屬於$c$類別的機率\
\
$y_c^{(i)}$用 $1$ or $0$ 表示第$i$個樣本是否屬於類別$c$ ( 即用 **one-hot** )\
\
根據softmax回歸,多分類交叉熵損失函數的公式如下:
$$L(\mathbf{f},\mathbf{y})=\frac{1}{m}\sum_{i=1}^{m}L_i(\mathbf{f}^{(i)},\mathbf{y}^{(i)})$$
$$=-\frac{1}{m}\sum_{i=1}^{m}\mathbf{y}^{(i)} \cdot log\ (\mathbf{f}^{(i)})$$
* 舉例
對於三分類問題,即$C=3$,某個樣本的$\mathbf{f}^{(i)}$和$\mathbf{y}^{(i)}$的值分別如下:
$$\mathbf{f}^{(i)}=
\begin{bmatrix}
f_1^{(i)} & f_2^{(i)} & f_3^{(i)}
\end{bmatrix}=
\begin{bmatrix}
0.3 & 0.5 & 0.2
\end{bmatrix}
$$
$$\mathbf{y}^{(i)}=
\begin{bmatrix}
y_1^{(i)} & y_2^{(i)} & y_3^{(i)}
\end{bmatrix}=
\begin{bmatrix}
0 & 0 & 1
\end{bmatrix}
$$
則交叉熵損失如下:\
$$-(0 \times log(0.3) + 0 \times log(0.5) + 1 \times log(0.2))=-log(0.2)$$
可以看出交叉熵損失只取決於真實的類別所對應的那一項(1那一項)
我們也可以加入正則向避免參數過大
$$L(\mathbf{f},\mathbf{y})=\frac{1}{m}\sum_{i=1}^{m}L_i(\mathbf{f}^{(i)},\mathbf{y}^{(i)})+ \lambda\sum_{i=1}^{L}||\mathbf{W}^{[l]}||_2^2$$
***
## **3.神經網路的訓練與實作**
* 設神經元的數目為$n^{(l)}$,前一層的輸出值的數目是$n^{(l-1)}$,那麼該層的神經元泉質矩陣$W^{(l)}$是一個$n^{(l-1)}\times n^{(l)}$ 的矩陣
```python=
W1=np.random.randn(n_1_1,n_1)*0.01
```
***
### 1.製作一個兩層的神經網路,進行參數的初始化並return
```python=
def initialize_parameters(nx, nh, no):#只有兩層
#nx:輸入的特徵數
#nh:中間層的神經元數
#no:輸出層的神經元數
np.random.seed(2) # 固定的種子,使每次運行這段程式時隨機數的值都是相同的
W1 = np.random.randn(nx, nh) * 0.01
b1 = np.zeros((1, nh))
W2 = np.random.randn(nh, no) * 0.01
b2 = np.zeros((1, no))
#檢驗是否符合,否的話停止執行
assert (W1.shape == (nx, nh))
assert (b1.shape == (1, nh))
assert (W2.shape == (nh, no))
assert (b2.shape == (1, no))
parameters = {"W1": W1, "b1": b1, "W2": W2, "b2": b2}
return parameters
nx, nh, no = 2, 4, 3
parameters = initialize_parameters(nx, nh, no)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
```
```python=
#print
W1 = [[-0.00416758 -0.00056267 -0.02136196 0.01640271]
[-0.01793436 -0.00841747 0.00502881 -0.01245288]]
b1 = [[0. 0. 0. 0.]]
W2 = [[-1.05795222e-02 -9.09007615e-03 5.51454045e-03]
[ 2.29220801e-02 4.15393930e-04 -1.11792545e-02]
[ 5.39058321e-03 -5.96159700e-03 -1.91304965e-04]
[ 1.17500122e-02 -7.47870949e-03 9.02525097e-05]]
b2 = [[0. 0. 0.]]
```
***
### 2.進行正向計算
* (第一層是進行$tanh(x)$,第二層是$sigmoid(x)$而這邊進行到將第二層加權過後的$\mathbf{Z}^{(2)}$輸出,沒有進行$sigmoid(x)$)
```python=
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def forward_propagation(X, parameters):
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
Z1 = np.dot(X, W1) + b1
# Z1 的形狀:(3, 2) (2, 4)+(1, 4) => (3, 4)
A1 = np.tanh(Z1)
Z2 = np.dot(A1, W2) + b2
# Z2 的形狀:(3, 4) (4, 3) + (1, 3) => (3, 3)
# A2 = sigmoid(Z2),第二個神經元,這邊選擇不做
assert (Z2.shape == (X.shape[0], 3))
return Z2
X = np.array([[1., 2.], [3., 4.], [5., 6.]]) # 每一行對應於一個樣本
Z2 = forward_propagation(X, parameters)
print("Z2=",Z2)
```
```python=
#print
Z2= [[-1.36253581e-04 4.87491807e-04 -2.47960226e-05]
[-1.64985210e-04 1.01574088e-03 -5.99877659e-05]
[-1.96135525e-04 1.54048069e-03 -9.36558871e-05]]
```
***
### 3.計算交叉熵
```python=
def softmax(Z):
exp_Z = np.exp(Z - np.max(Z, axis=1, keepdims=True))
#減去最大的指數項,讓最大值的指數向=0
return exp_Z / np.sum(exp_Z, axis=1, keepdims=True)
```
```python=
def softmax_cross_entropy(Z, Y, onehot=False):
m = len(Z)
F = softmax(Z)
if onehot:
loss = -np.sum(Y * np.log(F)) / m
else:
y.flatten()
log_Fy = -np.log(F[range(m), y])
loss = np.sum(log_Fy) / m
return loss
```
```python=
def softmax_cross_entropy_reg(Z, Y, parameters, onehot=False, reg=1e-3):
W1=parameter[0]
W2=parameter[2]
loss = softmax_cross_entropy(Z, Y, onehot)
#損失
reg_term = reg * (np.sum(W1**2) + np.sum(W2**2))
#正則化向
L = loss + reg_term
assert isinstance(L, float)
#檢查是否為浮點數
return L
```
```python=
y = np.array([2, 0, 1]) # 每一行對應於一個樣本
loss = softmax_cross_entropy_reg(Z2, y, parameters)
print(loss)
```
```python=
#print
1.098427770814438
```
* 包起來讓我們只輸入資料和目標值就可以計算交叉熵
```python=
def compute_loss_reg(f, loss, X, Y, parameters, reg=1e-3):
Z2 = f(X, parameters)
return loss(Z2, Y, parameters, reg)
reg = 1e-3
L = compute_loss_reg(forward_propagation, softmax_cross_entropy_reg,
X, y, parameters, reg)
print(L)
```
***
### 4.製作一個丟入f和parameters就能返回的權重的函數
* 這在2.4章節實作過,但他預設是使用lambda函數,所以需要適時調整
```python=
def numerical_gradient(f, params, eps=1e-6):
numerical_grads = [] # 儲存數值梯度的列表
for x in params:
grad = np.zeros(x.shape) # 初始化梯度為零陣列
# 創建用於遍歷 x 的迭代器,設定返回多維索引和可讀寫操作的標誌
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
# 簡單來說就是對每個x去算他的梯度
# 遍歷每個元素
while not it.finished:
idx = it.multi_index # 當前元素的多維索引
old_value = x[idx] # 儲存原始值
# 對該元素進行微小變化,計算函數在新值下的變化
x[idx] = old_value + eps
fx_plus = f() # 計算 f(x + eps)
x[idx] = old_value - eps
fx_minus = f() # 計算 f(x - eps)
# 根據數值變化計算該元素的數值偏導數
grad[idx] = (fx_plus - fx_minus) / (2 * eps)
x[idx] = old_value # 恢復原始值
it.iternext() # 移動到下一個元素
numerical_grads.append(grad) # 將該參數的數值梯度加入列表
return numerical_grads # 返回數值梯度列表
#計算權重值
def f():
return compute_loss_reg (forward_propagation, softmax_cross_entropy_reg, X, y, parameters,reg)
num_grads = numerical_gradient (f, parameters)
print(num_grads[0])
print(num_grads[3])
#在這邊應該要用
#print(num_grads["W1"])我不清楚他單純是忘記前面用dict還是想表達使用list或numpy的狀況...
#print(num_grads["W2"])
```
***
### 5.簡單的梯度下降優化器
```python=
def max_abs(grads):#取最大的梯度
return max(np.max(np.abs(grad)) for grad in grads)
def gradient_descent_ANN(f, X, y, parameters, reg=0.0,
alpha=0.01, iterations=100, gamma=0.8, epsilon=1e-8):
losses = []
for i in range(iterations):
loss = f() # 計算當前損失
grads = numerical_gradient(f, parameters) # 計算梯度
if max_abs(grads) < epsilon:
print("Gradient is small enough!")
print("Number of iterations:", i)
break
for param, grad in zip(parameters, grads):
#zip:把將 parameters和grads 中的對應元素分別配對
param -= alpha * grad # 更新模型參數
losses.append(loss) # 儲存當前損失值
return parameters, losses
```
題目 : 代表預測值跟真實值的誤差叫什麼函數?