---
# System prepended metadata

title: 2024-09-20 DL
tags: [DL, session, Keras]

---

# 2024-09-20 DL

- 主題：**Keras**
- 放映影片：`Video_2024-08-14_115653._類神經網路_03.mp4` 播放完畢
- 搭配講義：[**DL Lecture 01 (How to construct an NN - Part)**](https://drive.google.com/file/d/1ugcciON1F1hrOS72I_7BOiqn_6VIxsGe/view?usp=drive_link)
- 分工
    - 主持人： @Ot4v6SRLSZ2amlJNMb6Ynw 
    - 教學組
        - 領讀人： @sin-iu-ho 
        - 編譯組： @sin-iu-ho  @munin 
    - 紀錄組
        - 文字組： @munin 
        - 影音組： @as854398  （音質太差，不錄影）

## 課程內容筆記

- 目的：搭建輸入為 3，隱藏層 1 層，輸出為 1 的類神經網路。
![image](https://hackmd.io/_uploads/ry6ZNNiTC.png =200x)

- 整體類神經網路的學習方式
![image](https://hackmd.io/_uploads/BkwUX4s6R.png =600x)


- 損失函數幫助估計誤差，而優化函數用來調整權重，最終使損失最小化。
	- 損失估計 (Loss Estimation)：損失函數是用來衡量模型預測值與實際值之間誤差的函數。在訓練模型時，我們希望通過優化過程將這個誤差最小化。
	- 權重調整 (Weight Adjustment) ：這部分涉及模型在損失函數基礎上進行參數更新，以最小化損失函數，達到優化目標。
	
- 準備輸入資料

- 搭建第一層隱藏層
    ```python=
    Model.add(Dense(128, activation = 'relu', input_dim = 3))
    ```
    - 建立第一個隱藏層的同時，也要將輸入層設定好
	- `input_dim = 3` 代表 3 個輸入
	- 但也可以將輸入層另外拉出設定（下方示範）

- 輸出層
    ```python=
    Model.add(Dense(1, activation = 'sigmoid'))
    ```
	- 輸出層，只輸出 1 個
	- 因為此處是解決二元分類的問題（輸出的資料 y 只有 0 和 1），所以 `activation` （激活函式）僅能選擇 `sigmoid`

- 查看模型搭建的狀況
    ```python=
    Model.summary()
    ```
	- 此處模型的參數量 3x128+128=512 為可以訓練的參數

- 損失估計（Loss function）
    ```python=
    Model.compile(optimizer = 'rmsprop', loss = 'binary_crossentropy', metrics = ['accuracy'])  
    ```
	- `optimizer`：優化器設定
	- `loss`：損失函數，（老師說）這不是人類可以了解ㄉ
	- `metrics`：評比（表現評量）
		- `accuracy`：正確率

- 權重調整（SGD+BP） 
    ```python=
    Model.fit(X,y,epochs = 1000, batch_size = 3, verbose = 1) 
    ```
	- 利用前述的 `optimizer`（優化器）和 `loss`（損失函數），會根據損失函數計算出的誤差來調整每個神經元之間的權重。具體來說，模型會通過 `反向傳播（backpropagation）` 來計算每一個神經元對總損失的貢獻，並根據這些貢獻使用優化器來更新神經元間的權重，這樣模型就能逐步減小預測錯誤。
	- 訓練過程中，將訓練資料放入神經網路中，模型會計算出預測結果，與實際結果相比較後得出損失值，接著根據優化器更新神經元的權重，使模型在後續的訓練中表現得更好。

    :::spoiler 什麼是反向傳播（backpropagation）？

    反向傳播（backpropagation）是一種優化神經網路的技術，用來更新網路中每個神經元的權重。它的目的是根據模型的預測誤差來調整這些權重，從而使預測結果更接近實際值。

    具體來說，反向傳播過程分為以下幾個步驟：

    1. **前向傳播（Forward Propagation）**：將輸入資料放入神經網路，經過隱藏層的計算，最終得到輸出結果（模型的預測值）。

    2. **計算損失**：將網路輸出的預測值與實際的目標值進行比較，使用損失函數來計算誤差（損失值），表示模型預測與實際值的差距。

    3. **反向傳播誤差**：從輸出層開始，反向計算每個神經元對損失的影響。這個過程通過鏈式法則（chain rule），逐層向前追溯，計算每個神經元的偏導數（即權重的梯度），這樣模型知道該如何調整權重來減小誤差。

    4. **更新權重**：使用優化器（如梯度下降法），根據計算出的梯度來更新每個神經元的權重，使預測誤差逐漸減小。這是通過調整網路的參數（權重和偏差）來實現的。

    反向傳播的核心思想就是讓模型逐步學習如何減少誤差，從而在給定的訓練資料上進行更準確的預測。
    :::
    
    - `batch_size`（批次大小）
		- 在訓練模型時，`batch_size` 定義了每次更新權重時，所使用的樣本數量。
		- 例如，若 `batch_size` 設為 3，則模型會從資料集中選擇 3 筆資料，進行一次前向傳播、計算損失、再進行反向傳播來更新權重。
		- 在這個案例中，若資料集有 8 筆資料，最多只能有 8 個資料點，因此可以選擇批次大小不超過 8。
	- `epochs`（迭代次數）
		- 算是一種 iteration ，涉及計算 Loss、metrics，表示完整訓練資料集被送入模型進行前向傳播和反向傳播的次數。
		- 每個 `epoch` 包含若干次的前向與反向計算，直到遍歷完所有訓練資料。
		- 每個 `epoch` 是一個完整的「回合」，並不等於一次計算，而是所有資料都經過一次模型訓練的過程。
	- `verbose`（顯示訓練狀況）
		- 控制訓練過程中的輸出訊息，顯示訓練進度和結果,預設為1(auto)。
		    - 設為 `0` 則不顯示任何信息
		    - 設為 `1`，會以`進度條`顯示每個 `epoch` 的詳細訓練信息 例如:
Epoch 1/2
186219/186219 [==============================] - 85s 455us/step - loss: 0.5815 - acc: 
0.7728 - val_loss: 0.4917 - val_acc: 0.8029
            - 設為`2`會以純數字$\frac{n}{totalEpochs}$形式顯示每個epoch訓練信息，而不顯示進度條。 例如:
Epoch 1/2- 88s - loss: 0.5746 - acc: 0.7753 - val_loss: 0.4816 - val_acc: 0.8075
    
- 訓練循環（training loop）：compile() & fit()

- 預測
	- 通常採用 predict() 或 evaluate() 方法
    ```python=
    import numpy as np
    X_test = np.array([[0,0,0]])
    Model.predict(X_test) 
    Model.evaluate(X,y)
    ```
	- `predict(X_test)`：因為是預測 0 或 1 的二元分類問題，所以這邊會得出 0-1 之間的浮點數
	- `evaluate(X,y)`：顯示模型對資料集的表現如何，計算 Loss function 與 metrics

- 把輸入層從第一層隱藏層放出來的方式，指定輸入層的 shape
    ```python=
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Input # 

    Model = Sequential()
    Model.add(Input(shape=(3,))) # 
    Model.add(Dense(128, activation = 'relu'))
    Model.add(Dense(1, activation = 'sigmoid'))
    ```
    
- 課後討論速記
	- 資料集英文大小寫的差別
		- 向量：一維結構，使用小寫字母表示。
		- 矩陣：二維結構，使用大寫字母表示，像一個「面」。
		- 張量：三維或更高維度結構，通常也是大寫字母表示。
	- 解決回歸問題的有哪些損失函數？[MAE](https://hackmd.io/@learnai2024/mldl-encyclopedia#Mean-square-error)、[RMSE](https://hackmd.io/@learnai2024/mldl-encyclopedia#RMSE)
	- Keras 中的 Sequential API 和 Functional API 的差異
		- Sequential API（序列式 API） 
			- 此處老師使用的案例是 Sequential API ，當模型的結構是簡單的逐層堆疊，且每一層的輸入來自上一層的輸出，這種「一層一層」的線性／序列式結構適合用 Sequential API 來實現。
			- 應用場景如：影像分類、回歸任務等。
		- Functional API（函數式 API）
			- 當模型的結構較為複雜，涉及到多輸入、多輸出、共享層或需要自定義拓撲結構時，可以使用 Functional API。
			- 應用場景如：多模態的輸入（處理影像和文本的模型）、多輸出模型等。
	- Model 是 Sequential 的實例，Model 之後所接的方法，都是 Sequential 的方法，例如 add()、compile()、fit() 等。
	- 資料集通常分為 Training Data、Evaluation Data、Testing Data（對訓練模型來說的新資料）
	- 什麼是 overfitting 與 generalization？
		- [Overfitting](https://hackmd.io/@learnai2024/mldl-encyclopedia#Overfitting)（過擬合）：使用有看過的資料（訓練集）表現很好，但使用沒看過的資料（測試集或其他沒使用過的資料）表現很差。
		- [Generalization](https://hackmd.io/@learnai2024/mldl-encyclopedia#Generalization)（泛化能力）：使用有遇過（訓練集）或沒遇過（測試集）的資料皆表現很好。
	- `input_dim` 和 `input_shape` 都是在定義神經網路的輸入層時常見的參數
		- `input_dim` 是維度，而 `input_shape` 是形狀。
		- `input_dim` ：用來指定輸入的維度（即向量的維度數量）
			- 適用情境：通常用在一維資料（如序列或特徵向量）上。這個參數多出現在像是 `Dense` 層這類的一維資料輸入。
			- 定義：`input_dim = 3` 表示輸入是一個一維向量，該向量的長度是 `3`，這代表這個模型的每個樣本有 `3` 個特徵值。
			- Example：`input_dim=3` 表示輸入是一個一維向量 `[x1, x2, x3]`，所以輸入的形狀其實是 `(3,)`。
		- `input_shape`：用來定義輸入資料的形狀（包括各維度的大小），需要傳遞一個 tuple。
			- 適用情境：適合用於多維資料（如二維圖片、三維視頻）或一維資料。它可以在任何層（如卷積層、LSTM層等）中使用來定義輸入的完整形狀。
			- 定義：`input_shape = (3, )` 表示輸入是一個一維向量，它的形狀是 `(3,)`。這樣的寫法明確指出了輸入的形狀，不僅是維度，也指明了該維度的具體大小。
			- Example：如果你有一個 28x28 的灰階圖片，你會用 `input_shape=(28, 28, 1)`（三維資料）。
	- 為什麼二元分類的問題，隱藏層的 activation 是用 `ReLU` 函式，而輸出層是用 `Sigmoid`？
		- [ReLU](https://hackmd.io/@learnai2024/mldl-encyclopedia#ReLU)
		![image](https://hackmd.io/_uploads/rkI37Vsa0.png =400x)
		- [sigmoid](https://hackmd.io/@learnai2024/mldl-encyclopedia#Sigmoid-function)
		![image](https://hackmd.io/_uploads/HkSt7VjTA.png =400x)
		- 避免梯度消失（下回讀書會討論）
		- 引入非線性（nonlinearity）。
		    - 當所有層都是線性組合時，無論堆疊多少層，最後的結果還是可以通過一個線性函數來表示，這樣就無法有效學習和擬合非線性關係。
            - 啟動函數（如 ReLU、Sigmoid、Tanh 等）能夠將輸入轉換成非線性的輸出，使神經網路能夠擬合複雜的非線性資料，從而增強模型的表現能力。

	- 以買iphone來舉例sigmoid : 會買的情況為1，不會買的情況為0，在這sigmoid中算是一種機率分布，我們可以設定一個斷點/門檻；當其超過50%直接強制變成1(會買)，小於50%則是直接變成0(不會買)，但為了顧及非線性因此會使用sigmoid函數。(這裡聽起來有可能是指除了sigmoid函數也可能會有其他函數也可以用買iphone來舉例) @sin-iu-ho 麻煩確認一下

        :::spoiler 參考資料 {#ref}
        - 同學提供 in Chat Room
            1. ReLU 的使用時機： 隱藏層中的首選激活函數：ReLU 是目前神經網絡中最常用的激活函數，尤其是在深度網絡的隱藏層中，因為它計算高效，並且能有效解決梯度消失問題。 適合大多數任務的隱藏層：無論是圖像分類、自然語言處理還是其他任務，只要是隱藏層，ReLU 通常是首選激活函數，因為它能使模型學習到更深層的特徵。 大數據集和深層神經網絡：ReLU 能夠快速收斂，特別適合於大規模數據集和深層網絡。

            2. Sigmoid 的使用時機： 輸出層適合二分類問題：Sigmoid 常用於二分類問題的輸出層，因為它可以將輸出值壓縮到 [ 0 , 1 ] [0,1] 之間，解釋為概率。當模型需要預測某個樣本屬於某個類別的概率時，Sigmoid 是理想選擇。 適合概率預測：當你需要將輸出值解釋為概率（如二分類或多標籤分類），Sigmoid 是非常合適的。
        :::


## 問題與討論

==陸續新增中==

:::spoiler 我們今天所搭建的神經網路適用哪一種機器學習任務？
二元分類（binary classification）
::: 

:::spoiler 我們用了 Keras 的哪一種 API 來搭建神經網路？
- [x] Sequential：線性或序列式的網路架構。（講義 p.28）
- [ ] Functional
:::

:::spoiler 我們用了 [`Sequential`](https://www.tensorflow.org/api_docs/python/tf/keras/Sequential) 類別的哪些方法？
- [x] `add`
- [x] `summary`
- [x] `compile`
- [x] `fit`
- [x] `evaluate`
- [x] `predict`
- [ ] `save`
- [ ] `load_weights`
:::

:::spoiler 如何搭建一個含有 128 個神經元的密集層（全連接層）？
使用 [`tf.keras.layers.Dense`](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Dense)

假設使用 ReLU 作為啟動函數。

```python=
from tensorflow.keras.layers import Dense
model.add(Dense(128, activation='relu'))
```

如果這是第一層，還需要指定輸入的形狀。
- 輸入層可以在第一層定義時一併指定
```python=
Model.add(Dense(128, activation='relu', input_dim =3 ))
```
- 也可以在第一個隱藏層之前單獨指定
```python=
Model = Sequential()
Model.add(Input(shape=(3,)))
```
:::




:::spoiler 在範例中，哪一層使用了哪一種啟動函數？為什麼？
```python=
Model = Sequential()
Model.add(Input(shape=(3,)))
Model.add(Dense(128,activation = 'relu', input_shape = (3,) ))
Model.add(Dense(1, activation = 'sigmoid'))
```
在第一個隱藏層是使用 ReLU，在輸出層是使用 sigmoid，理由詳見 [參考資料](#ref)
:::

:::spoiler 我們用哪一種損失函數？為什麼？
二元交叉熵（binary cross entropy），因為是二元分類任務。
:::

:::spoiler `fit` 方法中使用了哪些參數？它們分別代表什麼？
- [x] `epoch`
- [x] `batch_size`
- [x] `verbose`
:::

## 延伸閱讀
- input_dim:在 TensorFlow/Keras 2.x 中，input_dim 是一個早期 Keras API 中的參數，並不是 TensorFlow 2.x 的標準參數。在 TensorFlow 2.x 中，`input_dim` 這個參數實際上已經被 `input_shape` 取代。它不再作為顯式參數出現在現代的 TensorFlow 源代碼中。如果你寫了 model.add(Dense(64, input_dim=100)) 這種語法，它實際上是通過 input_shape=(100,) 的方式來處理的。原因是 Keras 的 Dense 層會在初始化的時候，將這些額外的參數（如 input_dim）視為 input_shape 的簡化形式來自動轉換。那麼問題來了，哪裡的代碼處理了這種自動轉換？事實上，input_dim 參數的處理是發生在 Dense 層的基類 Layer 中，而不是 Dense 層本身。它是通過 Keras 的 Input 或 build 方法來自動解析的。雖然 input_dim 作為早期 API 的遺留參數，已經在新的 Keras API 中被 input_shape 取代，但它仍然可以兼容，因為內部會自動轉換。
[SourceCode](https://github.com/keras-team/keras/blob/v3.3.3/keras/src/layers/layer.py#L59-L1448 ) 
    以下為Dense的父層Layer其中源代碼，實際上由Layer這個類別處理input_dim這個參數
    ``` python
  input_dim_arg = kwargs.pop("input_dim", None)
        if input_dim_arg is not None:
            input_shape_arg = (input_dim_arg,)
    ```
- 反向傳播: {%youtube ibJpTrp5mcE%}

    
## 其他議題

1. [**期初成員問卷**](https://forms.gle/gMWuL2W5uonh1tJN9)
    - 2024-09-27 以前開放作答

2. 讀書會分工
    - 職位安排（待下週確認）
        - 主持人：主持會議流程，協調工作事項分配
        - 教學組
            - 領讀人：講課、導讀書籍，或針對影片帶領問題討論
            - 編譯組：將紀錄組的文字紀錄整合講義內容，在 HackMD 上編寫讀書會筆記
        - 紀錄組
            - 文字組：速記課程內容；收集聊天室話題
            - 影音組：控制課程影片播放；（如有需要）側錄讀書會過程
    - ==讀書會參與者輪流擔任不同職位，以促進參與度==。

3. 音質太差的配套措施
    - [ ] **方案 A**：使用講義搭配 Colab 程式碼，由領讀人主講
    - [ ] **方案 B**：由領讀人（學務組）事先看完影片，再到讀書會上主講；此方案需要領讀人（學務組）大量付出心血，需提高誘因。

4. 新增分享會／讀書會
    - 面試經驗分享：羽喬發起，上課時間預定為 2024-09-25 19:00~:20:00  
    - 數學基礎讀書會： @sin-iu-ho 發起，上課時間預定為
        - 2024-09-24 10:00--12:00
        - 2024-09-26 19:00--21:00