通過 Numpy 手動搭建 CNN


1. 背景知識

  主要參考如下鏈接:

  https://github.com/mbadry1/DeepLearning.ai-Summary
  http://neuralnetworksanddeeplearning.com/chap6.html
  http://www.deeplearningbook.org/contents/convnets.html

  1.1 CNN原理
    CNN 是Neural Network 中的一個子類, *深度學習* 中的主要方法之一。主要用於解決圖像形式的 NN 識別及分類。當進行圖形的神經網絡處理時,對於全鏈接網絡如按每個像素進行處理的化,相應的特征點值與多層網絡的結合,將會使計算量難以接受,這也是引入“卷積”的背景。
    其核心內涵表達式為:

    $$ feature_{map} = input \cdot kernel $$

  1.2 CNN 結構
    按 Andrew Ng 的說法,CNN 的主要結構包括:
        1. 輸入層

     2. 卷積層-i(多個)

        - 池化層(一般用Max.Pooling)

       3. 全鏈接層-j(FullyConnected) (也可能有多個)

       4. 最后一層進行轉換函數運算(Softmax:Sigmoid/Tanh/ReLU...)

     每一層的結構及計算基本是按圖像的像素維度為輸入對象,架構成的矩陣運算,其中卷積的計算——即通過 filter 在輸入圖像上按既定參數進行“遍歷”,並通過矩陣運算的方式獲得結果——的這一過程。

    而按 Nielsen 參考文獻的說法,CNN 的主要組成要素共有三個:

     1. Local receptive fields

           類似前面所描述的 filter ,在運算時所對應的原輸入圖像中的相應區域。

        2. shared weights

            每個filter 的卷積乘子(即hidden layer 上的每個節點)權重值,都被設置成了一樣的,這些結果稱為 share weights,相應的偏差稱為 share bias。

        3. pooling

            一般緊接在卷積層之后使用,用於 “簡化” 從卷積層獲得的信息。

          也因此,諸如在 Ng 的教材中,一般池化的方法采用“最大池化”即簡單直接的取卷積結果2x2矩陣中的最大值。

     在多個filter存在的條件下,每個filter輸出的內容上(即相應的hidden layer輸出量)均可進行池化,而在文中的說明中,池化(尤其最大池化)的內涵即篩選出圖像中的最大特征點(值),而相應的忽略其精確的位置信息,認為相對於圖像的特征值,其位置信息是相對次要的。

 

2. 方法實現

  參考如下鏈接:
  https://www.kdnuggets.com/2018/04/building-convolutional-neural-network-numpy-scratch.html

  尤其相應代碼:https://github.com/ahmedfgad/NumPyCNN (基本沒修改,為學習方便通過注釋進行了解讀)

  2.1 類和方法的定義

    主要是 NumPyCNN 代碼中的 NumPyCNN.py 文件內容。

    原文及注釋盡量保留:

    - 首先導入相關庫:

 1 import numpy
 2 import sys
 3 
 4 """
 5 Convolutional neural network implementation using NumPy.
 6 An article describing this project is titled "Building Convolutional Neural Network using NumPy from Scratch". It is available in these links: https://www.linkedin.com/pulse/building-convolutional-neural-network-using-numpy-from-ahmed-gad/
 7 https://www.kdnuggets.com/2018/04/building-convolutional-neural-network-numpy-scratch.html
 8 It is also translated into Chinese: http://m.aliyun.com/yunqi/articles/585741
 9 
10 The project is tested using Python 3.5.2 installed inside Anaconda 4.2.0 (64-bit)
11 NumPy version used is 1.14.0
12 
13 For more info., contact me:
14     Ahmed Fawzy Gad
15     KDnuggets: https://www.kdnuggets.com/author/ahmed-gad
16     LinkedIn: https://www.linkedin.com/in/ahmedfgad
17     Facebook: https://www.facebook.com/ahmed.f.gadd
18     ahmed.f.gad@gmail.com
19     ahmed.fawzy@ci.menofia.edu.eg
20 """

    - 定義進行卷積計算的函數 “conv_” :

 1 ### 定義函數 conv_ 即卷積操作中,具體的矩陣運算方式:
 2 ### 總的來說,conv_ 函數的實現內容就是用filter在img上進行遍歷,並在每一步進行卷積(矩陣內積),並獲得相應結果值的過程
 3 def conv_(img, conv_filter):
 4     filter_size = conv_filter.shape[1] 
 5     result = numpy.zeros((img.shape)) # 按 img 的尺寸構建結果
 6     #Looping through the image to apply the convolution operation.
 7     
 8    
 9     ### 注意后面定義的 conv 函數,幾個判斷要求保證了輸入的 filter 是正矩陣
10     ### 實際上,就是以filter(矩陣)的中心為基點(r,c),在原圖 img 上進行遍歷的過程,該基點能夠達到的位置
11 
12     for r in numpy.uint16(numpy.arange(filter_size/2.0, 
13                           img.shape[0]-filter_size/2.0+1)): 
14         for c in numpy.uint16(numpy.arange(filter_size/2.0, 
15                                            img.shape[1]-filter_size/2.0+1)):
16             
17             """
18             Getting the current region to get multiplied with the filter.
19             How to loop through the image and get the region based on 
20             the image and filer sizes is the most tricky part of convolution.
21             """
22             
23             ### 即取出與filter尺寸相同的 img 內容。'
24           
25             curr_region = img[r-numpy.uint16(numpy.floor(filter_size/2.0)):r+numpy.uint16(numpy.ceil(filter_size/2.0)), 
26                               c-numpy.uint16(numpy.floor(filter_size/2.0)):c+numpy.uint16(numpy.ceil(filter_size/2.0))]
27            
28             #Element-wise multipliplication between the current region and the filter.
29             ### 將對應的內容(filter 與 img 對應filter尺寸的部分)進行矩陣相乘——即卷積操作
30             curr_result = curr_region * conv_filter
31             conv_sum = numpy.sum(curr_result) #Summing the result of multiplication.
32             result[r, c] = conv_sum #Saving the summation in the convolution layer feature map.
33             ### 進一步將所有求和的值,存儲在 result 中,result是一個矩陣,尺寸即為filter 中心‘走’過的座標位置,
34                        
35     #Clipping the outliers of the result matrix.
36     ### 這一步將只保留 result 中有用的部分(即存儲 numpy.sum 值的部分),
37    
38     final_result = result[numpy.uint16(filter_size/2.0):result.shape[0]-numpy.uint16(filter_size/2.0), 
39                           numpy.uint16(filter_size/2.0):result.shape[1]-numpy.uint16(filter_size/2.0)]
40     return final_result

    - 再定義進行各層卷積方式的函數 “conv”,其中調用了“conv_” 進行了具體卷積計算 :

 1 def conv(img, conv_filter):  ### 這個函數是調整並設置卷積計算的各個要素的維度和尺寸,並通過引入 conv_ 進行計算
 2       ###-------------------------------------------------------------------------------
 3     ### 該函數相對 conv_ 而言,是對多重或多維(多通道)的 img 或 filter 制定相應的操作規則,
 4     ### 而 conv_ 是按照這個規則,進行一對一的卷積操作運算
 5     ###-------------------------------------------------------------------------------
 6     
 7     if len(img.shape) > 2 or len(conv_filter.shape) > 3: # Check if number of image channels matches the filter depth.
 8         if img.shape[-1] != conv_filter.shape[-1]:
 9             print("Error: Number of channels in both image and filter must match.")
10             sys.exit()
11     
12    
13     if conv_filter.shape[1] != conv_filter.shape[2]: # Check if filter dimensions are equal.
14         print('Error: Filter must be a square matrix. I.e. number of rows and columns must match.')
15         sys.exit()
16             
17   
18     if conv_filter.shape[1]%2==0: # Check if filter diemnsions are odd.
19         print('Error: Filter must have an odd size. I.e. number of rows and columns must be odd.')
20         sys.exit()
21 
22     # An empty feature map to hold the output of convolving the filter(s) with the image.
23     ### 但要注意,通過filter所得結果的結構形式構造,與傳統理解上做了個小變化,將filter的通道數目列在了最后(filter.shape[0]),
24     ### 這將在 numpy.zeros 的構造中建立以后兩個數為矩陣維度、第一個數為組數的三維矩陣,
25     ### 后面的操作將第三個維度作為標識,用前兩個維度構成的結構存儲了 conv_map,即卷積后的結果,
26    
27     feature_maps = numpy.zeros((img.shape[0]-conv_filter.shape[1]+1, 
28                                 img.shape[1]-conv_filter.shape[1]+1, 
29                                 conv_filter.shape[0]))
30 
31     # Convolving the image by the filter(s).
32     for filter_num in range(conv_filter.shape[0]): ### 對於每個通道上的 filter 
33         print("Filter ", filter_num + 1)
34         
35      
36         ### 定下的第一個值作為通道后,使用第二維以后的內容作為filter矩陣進行操作
37         curr_filter = conv_filter[filter_num, :] # getting a filter from the bank.
38         """ 
39         Checking if there are mutliple channels for the single filter.
40         If so, then each channel will convolve the image.
41         The result of all convolutions are summed to return a single feature map.
42         """
43         if len(curr_filter.shape) > 2: ### 如果所選擇的當前通道filter上的維度數目大於2
44             ### 則采用當前通道上,各組矩陣的第一列轉置為行后,構成的矩陣(維度是 [:,:] ),
45             ### 並使用該矩陣與相應的圖形(注意按判斷1, 維度相同)進行具體的卷積操作 “conv_”
46             conv_map = conv_(img[:, :, 0], curr_filter[:, :, 0]) # Array holding the sum of all feature maps.
47             
48                 ### 對於各個子通道的filter
49             for ch_num in range(1, curr_filter.shape[-1]): # Convolving each channel with the image and summing the results.
50               
51                 conv_map = conv_map + conv_(img[:, :, ch_num], 
52                                   curr_filter[:, :, ch_num])
53         else: # There is just a single channel in the filter.
54             ### 否則,如果只是單一個通道,直接 conv_ 操作就可以了
55             conv_map = conv_(img, curr_filter) 
56         
57         ### 這里用到了上面的 feature_maps 的情況:每個通道單獨給一個 conv_map 卷積結果數值
58        
59         feature_maps[:, :, filter_num] = conv_map # Holding feature map with the current filter.
60     ###返回 feature_maps
61     return feature_maps # Returning all feature maps.

    - 再定義最大池化函數:

 1 def pooling(feature_map, size=2, stride=2):
 2     #Preparing the output of the pooling operation.
 3     ### 最大池化,直接制定了默認池化尺寸是 2x2 ,
 4 
 5     pool_out = numpy.zeros((numpy.uint16((feature_map.shape[0]-size+1)/stride),
 6                             numpy.uint16((feature_map.shape[1]-size+1)/stride),
 7                             feature_map.shape[-1]))
 8     for map_num in range(feature_map.shape[-1]): (feature_map.shape[-1])
 9         r2 = 0 
10         for r in numpy.arange(0,feature_map.shape[0]-size-1, stride):
11             c2 = 0
12             for c in numpy.arange(0, feature_map.shape[1]-size-1, stride):
13                 ### 在通道 map_num 上,池化矩陣第(r2,c2)的位置上,做最大池化的操作,即選擇該池化尺寸對應的f_map內容中的最大值,
14 
15                 pool_out[r2, c2, map_num] = numpy.max([feature_map[r:r+size,  c:c+size]])
16                 c2 = c2 + 1
17             r2 = r2 +1
18     return pool_out

    - 再定義ReLU激勵函數:

1 def relu(feature_map):
2     #Preparing the output of the ReLU activation function.
3     ### ReLU是激勵函數中取 max(0,value) 的實現方法
4     relu_out = numpy.zeros(feature_map.shape)
5     for map_num in range(feature_map.shape[-1]):
6         for r in numpy.arange(0,feature_map.shape[0]):
7             for c in numpy.arange(0, feature_map.shape[1]):
8                 relu_out[r, c, map_num] = numpy.max([feature_map[r, c, map_num], 0])
9     return relu_out

 

  2.2 應用實現過程

    主要參考:NumPyCNN 中的 example.py 文件。

    - 導入相關庫:

 1 import skimage.data
 2 import matplotlib
 3 #import numpy
 4 #導入到notebook中,原.py文件導入不再需要:import numpycnn, 以及 import numpy
 5 
 6 """
 7 Convolutional neural network implementation using NumPy.
 8 An article describing this project is titled "Building Convolutional Neural Network using NumPy from Scratch". It is available in these links: https://www.linkedin.com/pulse/building-convolutional-neural-network-using-numpy-from-ahmed-gad/
 9 https://www.kdnuggets.com/2018/04/building-convolutional-neural-network-numpy-scratch.html
10 It is also translated into Chinese: http://m.aliyun.com/yunqi/articles/585741
11 
12 The project is tested using Python 3.5.2 installed inside Anaconda 4.2.0 (64-bit)
13 NumPy version used is 1.14.0
14 
15 For more info., contact me:
16     Ahmed Fawzy Gad
17     KDnuggets: https://www.kdnuggets.com/author/ahmed-gad
18     LinkedIn: https://www.linkedin.com/in/ahmedfgad
19     Facebook: https://www.facebook.com/ahmed.f.gadd
20     ahmed.f.gad@gmail.com
21     ahmed.fawzy@ci.menofia.edu.eg
22 """

    - 導入圖像:

1 img = skimage.data.chelsea() 
2 # Converting the image into gray.
3 ### 轉化成灰度圖
4 img = skimage.color.rgb2gray(img) 

    - 進行第一層卷積(實際是 conv/ReLU/pooling 三個操作步驟):

 1 # First conv layer
 2 #l1_filter = numpy.random.rand(2,7,7)*20 # Preparing the filters randomly.
 3 ### 定義第一層 filter 的結構形式,為2層(通道)的 3x3矩陣
 4 l1_filter = numpy.zeros((2,3,3)) 
 5 ### 量化第一個通道內容
 6 l1_filter[0, :, :] = numpy.array([[[-1, 0, 1], 
 7                                    [-1, 0, 1], 
 8                                    [-1, 0, 1]]]) 
 9 ### 量化第二個通道內容
10 l1_filter[1, :, :] = numpy.array([[[1,   1,  1], 
11                                    [0,   0,  0], 
12                                    [-1, -1, -1]]]) 
13 
14 print("\n**Working with conv layer 1**")
15 
16 ### 使用 conv 進行第一層的卷積計算
17 l1_feature_map = conv(img, l1_filter) 
18 print("\n**ReLU**")
19 #l1_feature_map_relu = numpycnn.relu(l1_feature_map)
20 ### 對第一層卷積層計算結果應用 ReLU 函數
21 l1_feature_map_relu = relu(l1_feature_map) 
22 print("\n**Pooling**")
23 #l1_feature_map_relu_pool = numpycnn.pooling(l1_feature_map_relu, 2, 2)
24 ### 對第一層卷積層計算結果進行 Pooling 處理
25 l1_feature_map_relu_pool = pooling(l1_feature_map_relu, 2, 2)
26 print("**End of conv layer 1**\n")
27 
28 ### 至此,由於filter矩陣維度是(2,3,3),即2通道3x3矩陣,因此經過卷積操作(conv)的矩陣維度是 2通道,每個通道(n-(3-1))x(m-(3-1)),即,298x449
29 ### 激勵函數 ReLU 只是對卷積層結果的每個值進行計算變換,因此維度不變,依然是(2x298x449)
30 ### 池化層采用 2x2結構,對兩個通道的每個結果進行處理,因此仍為2組,每組為 (n'/2)x(m'/2)=(148x224)

    此時,輸出各圖的維度是:

    l1-conv:(298, 449, 2), l1-relu:(298, 449, 2), l1-pooling:(148, 224, 2)

    

    - 進行第二層卷積(實際是 conv/ReLU/pooling 三個操作步驟):

 1 # Second conv layer
 2 ### 構造第二層的filter,結構上使用上一層(l1)的最終維度
 3 ### 按照 numpy.random.rand 的方法實現,這個是一個5x2矩陣,分了3大組,每組5個矩陣
 4 l2_filter = numpy.random.rand(3, 5, 5, l1_feature_map_relu_pool.shape[-1])
 5 print("\n**Working with conv layer 2**")
 6 #l2_feature_map = numpycnn.conv(l1_feature_map_relu_pool, l2_filter)
 7 ### 
 8 l2_feature_map = conv(l1_feature_map_relu_pool, l2_filter)
 9 print("\n**ReLU**")
10 #l2_feature_map_relu = numpycnn.relu(l2_feature_map)
11 l2_feature_map_relu = relu(l2_feature_map)
12 print("\n**Pooling**")
13 #l2_feature_map_relu_pool = numpycnn.pooling(l2_feature_map_relu, 2, 2)
14 l2_feature_map_relu_pool = pooling(l2_feature_map_relu, 2, 2)
15 print("**End of conv layer 2**\n")

    此時,輸出各圖的維度是:

    l2-conv:(144, 220, 3), l2-relu:(144, 220, 3), l2-pooling:(71, 109, 3)

   可以簡單算一下,與第一層卷積后得結果經過第二層filter 的卷積結果,是一致的。

 

    - 進行第三層卷積(依然是 conv/ReLU/pooling 三個操作步驟):

 1 # Third conv layer
 2 l3_filter = numpy.random.rand(1, 7, 7, l2_feature_map_relu_pool.shape[-1])
 3 print("\n**Working with conv layer 3**")
 4 #l3_feature_map = numpycnn.conv(l2_feature_map_relu_pool, l3_filter)
 5 l3_feature_map = conv(l2_feature_map_relu_pool, l3_filter)
 6 print("\n**ReLU**")
 7 #l3_feature_map_relu = numpycnn.relu(l3_feature_map)
 8 l3_feature_map_relu = relu(l3_feature_map)
 9 print("\n**Pooling**")
10 #l3_feature_map_relu_pool = numpycnn.pooling(l3_feature_map_relu, 2, 2)
11 l3_feature_map_relu_pool = pooling(l3_feature_map_relu, 2, 2)
12 print("**End of conv layer 3**\n")

    這一層的結果是該 CNN 最后一層的結果:

    l3-conv:(65, 103, 1), l3-relu:(65, 103, 1), l3-pooling:(32, 51, 1)

    與第二層卷積后得結果經過第三層filter 的卷積結果是一致的,同時由於該層僅有一個filter,因此圖形回到單一 feature_map 的情況。

    

    至此,卷積的工作已經做完,繼續進行圖像的存儲處理:

1 # Graphing results
2 ### 使用 matplotlib 進行繪圖,本cell先繪制原圖 in_img.png,具體細節見 matplotlib mannal,這里不細述
3 fig0, ax0 = matplotlib.pyplot.subplots(nrows=1, ncols=1)
4 ax0.imshow(img).set_cmap("gray")
5 ax0.set_title("Input Image")
6 ax0.get_xaxis().set_ticks([])
7 ax0.get_yaxis().set_ticks([])
8 matplotlib.pyplot.savefig("in_img.png", bbox_inches="tight")
9 matplotlib.pyplot.close(fig0)

    L1 層:

 1 # Layer 1
 2 ### 繪制第一層卷積、ReLU處理、池化后的圖,因該層 filter 是兩通道,因此每個(conv/ReLU/pooling)結果是兩個圖
 3 fig1, ax1 = matplotlib.pyplot.subplots(nrows=3, ncols=2)
 4 ax1[0, 0].imshow(l1_feature_map[:, :, 0]).set_cmap("gray")
 5 ax1[0, 0].get_xaxis().set_ticks([])
 6 ax1[0, 0].get_yaxis().set_ticks([])
 7 ax1[0, 0].set_title("L1-Map1")
 8 
 9 ax1[0, 1].imshow(l1_feature_map[:, :, 1]).set_cmap("gray")
10 ax1[0, 1].get_xaxis().set_ticks([])
11 ax1[0, 1].get_yaxis().set_ticks([])
12 ax1[0, 1].set_title("L1-Map2")
13 
14 ax1[1, 0].imshow(l1_feature_map_relu[:, :, 0]).set_cmap("gray")
15 ax1[1, 0].get_xaxis().set_ticks([])
16 ax1[1, 0].get_yaxis().set_ticks([])
17 ax1[1, 0].set_title("L1-Map1ReLU")
18 
19 ax1[1, 1].imshow(l1_feature_map_relu[:, :, 1]).set_cmap("gray")
20 ax1[1, 1].get_xaxis().set_ticks([])
21 ax1[1, 1].get_yaxis().set_ticks([])
22 ax1[1, 1].set_title("L1-Map2ReLU")
23 
24 ax1[2, 0].imshow(l1_feature_map_relu_pool[:, :, 0]).set_cmap("gray")
25 ax1[2, 0].get_xaxis().set_ticks([])
26 ax1[2, 0].get_yaxis().set_ticks([])
27 ax1[2, 0].set_title("L1-Map1ReLUPool")
28 
29 ax1[2, 1].imshow(l1_feature_map_relu_pool[:, :, 1]).set_cmap("gray")
30 ax1[2, 0].get_xaxis().set_ticks([])
31 ax1[2, 0].get_yaxis().set_ticks([])
32 ax1[2, 1].set_title("L1-Map2ReLUPool")
33 
34 matplotlib.pyplot.savefig("L1.png", bbox_inches="tight")
35 matplotlib.pyplot.close(fig1)

    L2層:

 1 # Layer 2
 2 ### 繪制第二層卷積、ReLU處理、池化后的圖,因該層 filter 是三通道,因此每個(conv/ReLU/pooling)結果是三個圖
 3 fig2, ax2 = matplotlib.pyplot.subplots(nrows=3, ncols=3)
 4 ax2[0, 0].imshow(l2_feature_map[:, :, 0]).set_cmap("gray")
 5 ax2[0, 0].get_xaxis().set_ticks([])
 6 ax2[0, 0].get_yaxis().set_ticks([])
 7 ax2[0, 0].set_title("L2-Map1")
 8 
 9 ax2[0, 1].imshow(l2_feature_map[:, :, 1]).set_cmap("gray")
10 ax2[0, 1].get_xaxis().set_ticks([])
11 ax2[0, 1].get_yaxis().set_ticks([])
12 ax2[0, 1].set_title("L2-Map2")
13 
14 ax2[0, 2].imshow(l2_feature_map[:, :, 2]).set_cmap("gray")
15 ax2[0, 2].get_xaxis().set_ticks([])
16 ax2[0, 2].get_yaxis().set_ticks([])
17 ax2[0, 2].set_title("L2-Map3")
18 
19 ax2[1, 0].imshow(l2_feature_map_relu[:, :, 0]).set_cmap("gray")
20 ax2[1, 0].get_xaxis().set_ticks([])
21 ax2[1, 0].get_yaxis().set_ticks([])
22 ax2[1, 0].set_title("L2-Map1ReLU")
23 
24 ax2[1, 1].imshow(l2_feature_map_relu[:, :, 1]).set_cmap("gray")
25 ax2[1, 1].get_xaxis().set_ticks([])
26 ax2[1, 1].get_yaxis().set_ticks([])
27 ax2[1, 1].set_title("L2-Map2ReLU")
28 
29 ax2[1, 2].imshow(l2_feature_map_relu[:, :, 2]).set_cmap("gray")
30 ax2[1, 2].get_xaxis().set_ticks([])
31 ax2[1, 2].get_yaxis().set_ticks([])
32 ax2[1, 2].set_title("L2-Map3ReLU")
33 
34 ax2[2, 0].imshow(l2_feature_map_relu_pool[:, :, 0]).set_cmap("gray")
35 ax2[2, 0].get_xaxis().set_ticks([])
36 ax2[2, 0].get_yaxis().set_ticks([])
37 ax2[2, 0].set_title("L2-Map1ReLUPool")
38 
39 ax2[2, 1].imshow(l2_feature_map_relu_pool[:, :, 1]).set_cmap("gray")
40 ax2[2, 1].get_xaxis().set_ticks([])
41 ax2[2, 1].get_yaxis().set_ticks([])
42 ax2[2, 1].set_title("L2-Map2ReLUPool")
43 
44 ax2[2, 2].imshow(l2_feature_map_relu_pool[:, :, 2]).set_cmap("gray")
45 ax2[2, 2].get_xaxis().set_ticks([])
46 ax2[2, 2].get_yaxis().set_ticks([])
47 ax2[2, 2].set_title("L2-Map3ReLUPool")
48 
49 matplotlib.pyplot.savefig("L2.png", bbox_inches="tight")
50 matplotlib.pyplot.close(fig2)

    L3層:

 1 # Layer 3
 2 ### 繪制第三層(最后一層)的卷積、ReLU處理、池化后的圖,因該層 filter 是一通道,因此每個(conv/ReLU/pooling)結果是一個圖
 3 fig3, ax3 = matplotlib.pyplot.subplots(nrows=1, ncols=3)
 4 ax3[0].imshow(l3_feature_map[:, :, 0]).set_cmap("gray")
 5 ax3[0].get_xaxis().set_ticks([])
 6 ax3[0].get_yaxis().set_ticks([])
 7 ax3[0].set_title("L3-Map1")
 8 
 9 ax3[1].imshow(l3_feature_map_relu[:, :, 0]).set_cmap("gray")
10 ax3[1].get_xaxis().set_ticks([])
11 ax3[1].get_yaxis().set_ticks([])
12 ax3[1].set_title("L3-Map1ReLU")
13 
14 ax3[2].imshow(l3_feature_map_relu_pool[:, :, 0]).set_cmap("gray")
15 ax3[2].get_xaxis().set_ticks([])
16 ax3[2].get_yaxis().set_ticks([])
17 ax3[2].set_title("L3-Map1ReLUPool")
18 
19 matplotlib.pyplot.savefig("L3.png", bbox_inches="tight")
20 matplotlib.pyplot.close(fig3)

 

    到此,所有三層輸出已全部完成。可以根據圖像的內容判斷與結果的差異、以及分類的效果。

    另一可參考到的內容為:https://coveralls.io/github/wkcn/mobula?branch=master

    代碼是全的,可深入參考。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM