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