---
# System prepended metadata

title: AI Model Design and Quantization

---

# AI Model Design and Quantization

### 1\. Model Architecture 

- **卷積層（Conv Layers）**

   - **結構**
      - 模型包含五個卷積塊（conv1 到 conv5）使用 3x3 卷積核，每個 block 由 Conv2d（卷積層）、BatchNorm2d（批次正規化）和 ReLU（激活函數）組成

   - **設計說明**

      - 從3通道輸入開始，逐步擴展到64→192→384→256→256通道，透過漸進式擴展逐步提取更複雜的特徵，同時控制參數量。

      - 每個卷積層後都使用 Batch Normalization 和ReLU激活函數，加速訓練收斂並減輕梯度消失問題，並且透過池化層來減少空間維度

- **池化層（Pooling Layers）**

   - **結構**

      - 使用 2x2 池化核，包含三個最大池化層（pool1、pool2、pool3），每次將特徵圖尺寸減半（32x32 → 16x16 → 8x8 → 4x4）

   - **設計說明**

      - 漸進式降維可以減少計算量，同時保留重要特徵

- **全連接層（Fully Connected Layers）**

   - **結構**

      - 三個全連接層（fc6、fc7、fc8），將特徵攤平後從 4096（256x4x4）降維到 256、再到 128，最後輸出 10 類（CIFAR-10）。fc6 和 fc7 使用 ReLU，fc8 直接輸出結果

   - **設計說明**

      - 漸進式降維有助於控制參數量並防止過擬合

- **融合方法（fuse_modules）**

   - **功能**：將每個卷積塊中的 Conv2d、BatchNorm2d 和 ReLU 融合為單一模組，支援後續量化（PTQ）。

- **設計考量**

   - 為了在硬體上實現 CNN 推論，相比原始VGG模型，大幅減少了深度和參數量
   - 原始 VGG 並沒有使用 Batch Normalization，加入的目的是加速模型收斂並穩定訓練

   

### 2\. Loss/Epoch and Accuract/Epoch Plotting 


- 模型從 epoch 20 後開始過擬合，驗證損失在早期下降後停滯，且驗證準確度低於訓練準確度

![loss_acc](https://hackmd.io/_uploads/HJuPbDKiyl.png)



### 3\. Accuracy Tuning 

- **超參數列表與設置原因**

   - **Batch Size：64**

      - 選擇中等大小的 **Batch Size** 能在訓練速度和收斂穩定性間取得平衡。太小的 **Batch Size** 會導致梯度更新不穩定，太大則可能降低模型泛化能力。

   - **Learning Rate：0.01**

      - 初始學習率 0.01 提供適當的參數更新步伐，避免收斂過快或過慢

   - **Momentum：0.9**

      -  Momentum 0.9 幫助加速梯度下降，跨越局部最小值，提升收斂速度

   - **Learning Rate Scheduler**

      - **step_size：20**

         - 每 20 個 epoch 將學習率降低為原來的十分之一，使模型能先以較大步伐快速找到最優解區域，再以較小步伐精細調整，避免在最優解附近震盪，提高最終準確率

   - **Epochs：60**

      - 設定足夠多的訓練輪數確保模型能夠充分學習，同時搭配學習率調度器，在 20 和 40 輪時降低學習率，使模型在訓練後期能更精細地調整參數

### 4\. Explain how the Power-of-Two Observer in your QConfig is implemented. 

- **Explain how to caluclate *scale* and *zero-point*.**

   - **計算 scale 的步驟**

      - **獲取數據範圍**
         - min_val 和 max_val 是觀察到的數據最小值和最大值

      - **計算 abs_max**

         - 計算 min_val 和 max_val 取絕對值後的最大值，如果 abs_max = 0 或無效（比如 NaN），設為 1e-6，避免除以零

      - **設置量化範圍**

         - 如果 dtype = torch.quint8（無符號 8 位整數）：
            - quant_min = 0，quant_max = 255。

         - 如果 dtype = torch.qint8（有符號 8 位整數）：
            - quant_min = -128，quant_max = 127。

      - **計算初始 scale**

          ```python 
          scale = abs_max / ((quant_max - quant_min) / 2                           
      - **調整 scale 為 Power of Two**

         - 使用 scale_approximate 

            - **檢查有效性**：

               - 如果 scale <= 0 或無效，返回 2⁻⁸（預設 max_shift_amount = 8）。

           - **計算對數**：

               ```python 
                log2_scale = math.log2(scale)。
               ```

           - **四捨五入**：
               
               ```python 
                nearest_power = round(log2_scale)。              
            -  **限制範圍**：

               - 如果 nearest_power > 8，設為 8。
               - 如果 nearest_power < -8，設為 -8。

              - **轉成 tensor**
            ```python
            scale = torch.tensor(scale, dtype=torch.float32)
   - **計算 zero_point 的步驟**

      - **根據數據類型設置**

         - 如果 dtype = torch.quint8：
            - zero_point = 128。
         - 如果 dtype = torch.qint8：
            - zero_point = 0。

      - **轉成 tensor**

         - zero_point = torch.tensor(zero_point, dtype=[torch.int](torch.int)64)

- Explain how `scale_approximate()` function in `class PowerOfTwoObserver()` is implemented.

   - 說明 `scale_approximate()`

      - **檢查有效性**：

         - 如果 scale <= 0 或無效，返回 2⁻⁸（預設 max_shift_amount = 8）。

            ```python
            if scale <= 0 or not math.isfinite(scale):
                return 2.0 ** (-max_shift_amount)
            ```

      - **計算對數**：

         - log2_scale = math.log2(scale)。

            ```python
            log2_scale = math.log2(scale)
            ```

      - **找到最接近的整數冪次：**

         - 用 round 函數將對數四捨五入到最接近的整數，這個整數就是 2 的冪次（exponent）

            ```python
            nearest_power = round(log2_scale)。
            ```

      - **限制範圍**：

         - 如果 nearest_power > 8，設為 8。
         - 如果 nearest_power < -8，設為 -8。

         ```python
         if nearest_power > max_shift_amount:
             nearest_power = max_shift_amount
         elif nearest_power < -max_shift_amount:
             nearest_power = -max_shift_amount
         ```

      - **計算最終 scale**：

         - `scale = 2.0 ** nearest_power。`

- When writing `scale_approximate()`, is there a possibility of overflow? If so, how can it be handled?

   - 在撰寫 `scale_approximate()` 時有可能會發生 overflow 的現象，不過可以透過撰寫保護程式避免發生 overflow

   - 如何避免 overflow 發生

      - **輸入值檢查**

         - 防止了非正數或非有限數（如 NaN 或無窮大）導致的問題

         ```python
         if scale <= 0 or not math.isfinite(scale):
             return 2.0 ** (-self.max_shift_amount)
         ```

      - **對數計算溢位處理**

         - 當 **`scale`** 接近 0 時，**`log2(scale)`** 可能會趨近負無窮大，導致 **`c`** 趨近正無窮大。

         ```python
           # 計算 scale 的對數（底為 2）
           log2_scale = math.log2(scale)
         
           # 檢查 log2_scale 是否為無限值
           if not math.isfinite(log2_scale):
               return 2.0 ** (-max_shift_amount)  # 返回最小允許的 scale
         ```

      - **最終結果範圍限制**

         - 將最終的指數值限制在 **`-max_shift_amount`** 到 **`max_shift_amount`** 之間，防止生成過大或過小的數值。

         ```python
            if nearest_power > max_shift_amount:
                 nearest_power = max_shift_amount
             elif nearest_power < -max_shift_amount:
                 nearest_power = -max_shift_amount
         ```



