讓我們逐步解釋這段程式碼中的每個動作和參數的意思: ### 1. 匯入必要的模組: ```python import numpy as np # 相關運算 import time as t # 用於計時 from math import floor, ceil # floor: 往下取整數, ceil: 往上取整數 ``` 這段代碼匯入了 NumPy 模組來進行數學運算,匯入了 time 模組來計時,並從 math 模組中匯入了 `floor` 和 `ceil` 函數來進行整數的向下和向上取整。 ### 2. 設置 NumPy 的輸出選項: ```python np.set_printoptions(threshold=np.inf) # 請勿無窮大的要素數 np.set_printoptions(suppress=True) # 抑制顯示浮點數 ``` 這段代碼設置了 NumPy 的打印選項,第一行是為了在打印陣列時顯示所有元素(無限制),第二行是為了抑制顯示浮點數的科學計數法。 ### 3. 定義 CNN 第一層的參數和架構: ```python ''' Layer1 Layer: CNN input:32x32x1 filter:5x5x6 stride:1 pad:0 output:28x28x6 activation: tanh ''' ``` 這段註釋說明了第一層 CNN 的參數: - 輸入:32x32x1,表示輸入圖像大小為32x32像素,1個通道(灰階圖像)。 - 卷積核:5x5大小,有6個卷積核。 - 步幅(stride):1 - 填充(pad):0 - 輸出:28x28x6,表示輸出特徵圖大小為28x28,6個特徵圖。 - 激活函數:tanh ### 4. 生成隨機的輸入特徵圖: ```python ### 32x32x1 image input feature map ### fmap = np.random.randint(1, 256, size=(1, 32, 32), dtype=np.int32) # 標準數數 print("\n-----------input feature map-----------\n") print(fmap) ``` 這段代碼生成了一個隨機的 32x32x1 的輸入特徵圖,其中像素值在1到255之間,並打印出來。 ### 5. 定義卷積核: ```python ### 5x5x6 convolution kernel ### filter = np.random.rand(6, 1, 5, 5) # 標籤0,1用小數 stride = 1 pad = 0 ``` 這段代碼生成了6個5x5大小的卷積核,每個卷積核對應1個輸入通道(灰階圖像)。步幅設置為1,填充設置為0。 ### 6. 計算卷積層的輸出特徵圖: ```python ### 28x28x6 output feature maps ### ofmap = np.zeros((filter.shape[0], floor((fmap.shape[1] + 2 * pad - filter.shape[2]) / stride) + 1, floor((fmap.shape[2] + 2 * pad - filter.shape[3]) / stride) + 1)) ``` 這段代碼初始化了一個用來存儲卷積層輸出的28x28x6大小的特徵圖(全零初始化)。 ### 7. 執行卷積運算: ```python start = t.time() print("\n-----------Start Layer 1 CNN computation-----------\n") for depth in range(ofmap.shape[0]): # depth=6,height=28,width=28,channel=1,i=5,j=5 for height in range(ofmap.shape[1]): for width in range(ofmap.shape[2]): for channel in range(filter.shape[1]): for i in range(filter.shape[2]): for j in range(filter.shape[3]): ofmap[depth][height][width] += fmap[channel][i + height * stride][j + width * stride] * filter[depth][channel][i][j] ``` 這段代碼逐個元素地計算卷積層輸出的特徵圖。遍歷每個輸出特徵圖的深度、高度和寬度,並對應地計算輸入特徵圖和卷積核的點積。 ### 8. 激活函數: ```python ### activation function ### for depth in range(ofmap.shape[0]): for height in range(ofmap.shape[1]): for width in range(ofmap.shape[2]): ofmap[depth][height][width] = 2 / (1 + np.exp(-2 * ofmap[depth][height][width])) - 1 ifmap = ofmap print(ifmap) ``` 這段代碼對卷積層的輸出應用雙曲正切(tanh)激活函數,並將結果存儲在 `ifmap` 中。 ### 9. 定義池化層的參數和架構: ```python ''' Layer2 Layer: pooling input:28x28x6 filter:2x2x6 stride:2 pad:0 output:14x14x6 ''' ``` 這段註釋說明了池化層的參數: - 輸入:28x28x6,表示輸入特徵圖大小為28x28,6個特徵圖。 - 池化核:2x2大小,有6個池化核。 - 步幅(stride):2 - 填充(pad):0 - 輸出:14x14x6,表示輸出特徵圖大小為14x14,6個特徵圖。 ### 10. 定義池化核: ```python ### 2x2x6 ### filter = np.ones((6, 1, 2, 2)) stride = 2 pad = 0 ``` 這段代碼生成了6個2x2大小的池化核,每個池化核對應1個輸入通道(灰階圖像)。步幅設置為2,填充設置為0。 ### 11. 計算池化層的輸出特徵圖: ```python ### 14x14x6 ### ofmap = np.zeros((filter.shape[0], ceil((ifmap.shape[1] + 2 * pad - filter.shape[2]) / stride) + 1, ceil((ifmap.shape[2] + 2 * pad - filter.shape[3]) / stride) + 1)) print("\n-----------Start Layer 2 pooling computation-----------\n") for depth in range(ofmap.shape[0]): # depth=6,height=14,width=14,channel=1,i=2,j=2 for height in range(ofmap.shape[1]): for width in range(ofmap.shape[2]): for channel in range(filter.shape[1]): for i in range(filter.shape[2]): for j in range(filter.shape[3]): ofmap[depth][height][width] += ifmap[channel][i + height * stride][j + width * stride] * filter[depth][channel][i][j] ``` 這段代碼初始化了一個用來存儲池化層輸出的14x14x6大小的特徵圖(全零初始化),並逐個元素地計算池化層的輸出特徵圖。遍歷每個輸出特徵圖的深度、高度和寬度,並對應地計算輸入特徵圖和池化核的點積。 ### 12. 平均池化: ```python # average pooling ifmap = ofmap / (filter.shape[2] * filter.shape[3]) print(ofmap) ``` 這段代碼對池化層的輸出進行平均池化,將每個池化區域的值除以池化核的大小,並將結果存儲在 `ifmap` 中。 這樣,每一步都詳細解釋了程式碼的作用和參數的意思,幫助理解 CNN 的基本運算過程。