Auto Byte

Science AI

# 如何使用纯NumPy代码从头实现简单的卷积神经网络

1. 读取输入图像。

2. 准备滤波器。

3. 卷积层：使用滤波器对输入图像执行卷积操作。

4. ReLU 层：将 ReLU 激活函数应用于特征图（卷积层的输出）。

5. 最大池化层：在 ReLU 层的输出上应用池化操作。

6. 堆叠卷积层、ReLU 层和最大池化层。

1. 读取输入图像

``````1.  import skimage.data
3.  img = skimage.data.chelsea()
4.  # Converting the image into gray.
5.  img = skimage.color.rgb2gray(img)``````

2. 准备滤波器

``````1.  l1_filter = numpy.zeros((2,3,3))
``````

``````1.  l1_filter[0, :, :] = numpy.array([[[-1, 0, 1],
2.                                     [-1, 0, 1],
3.                                     [-1, 0, 1]]])
4.  l1_filter[1, :, :] = numpy.array([[[1,   1,  1],
5.                                     [0,   0,  0],
6.                                     [-1, -1, -1]]]) ``````

3. 卷积层

``````1.  l1_feature_map = conv(img, l1_filter)
``````

``````1.  def conv(img, conv_filter):
2.      if len(img.shape) > 2 or len(conv_filter.shape) > 3: # Check if number of image channels matches the filter depth.
3.          if img.shape[-1] != conv_filter.shape[-1]:
4.              print("Error: Number of channels in both image and filter must match.")
5.              sys.exit()
6.      if conv_filter.shape[1] != conv_filter.shape[2]: # Check if filter dimensions are equal.
7.          print('Error: Filter must be a square matrix. I.e. number of rows and columns must match.')
8.          sys.exit()
9.      if conv_filter.shape[1]%2==0: # Check if filter diemnsions are odd.
10.         print('Error: Filter must have an odd size. I.e. number of rows and columns must be odd.')
11.         sys.exit()
12.
13.     # An empty feature map to hold the output of convolving the filter(s) with the image.
14.     feature_maps = numpy.zeros((img.shape[0]-conv_filter.shape[1]+1,
15.                                 img.shape[1]-conv_filter.shape[1]+1,
16.                                 conv_filter.shape[0]))
17.
18.     # Convolving the image by the filter(s).
19.     for filter_num in range(conv_filter.shape[0]):
20.         print("Filter ", filter_num + 1)
21.         curr_filter = conv_filter[filter_num, :] # getting a filter from the bank.
22.         """
23.         Checking if there are mutliple channels for the single filter.
24.         If so, then each channel will convolve the image.
25.         The result of all convolutions are summed to return a single feature map.
26.         """
27.         if len(curr_filter.shape) > 2:
28.             conv_map = conv_(img[:, :, 0], curr_filter[:, :, 0]) # Array holding the sum of all feature maps.
29.             for ch_num in range(1, curr_filter.shape[-1]): # Convolving each channel with the image and summing the results.
30.                 conv_map = conv_map + conv_(img[:, :, ch_num],
31.                                   curr_filter[:, :, ch_num])
32.         else: # There is just a single channel in the filter.
33.             conv_map = conv_(img, curr_filter)
34.         feature_maps[:, :, filter_num] = conv_map # Holding feature map with the current filter.
35.      return feature_maps # Returning all feature maps.
```
```

``````1.  if len(img.shape) > 2 or len(conv_filter.shape) > 3: # Check if number of image channels matches the filter depth.
2.          if img.shape[-1] != conv_filter.shape[-1]:
3.              print("Error: Number of channels in both image and filter must match.")  ``````

``````1.  if conv_filter.shape[1] != conv_filter.shape[2]: # Check if filter dimensions are equal.
2.      print('Error: Filter must be a square matrix. I.e. number of rows and columns must match.')
3.      sys.exit()
4.  if conv_filter.shape[1]%2==0: # Check if filter diemnsions are odd.
5.      print('Error: Filter must have an odd size. I.e. number of rows and columns must be odd.')
6.      sys.exit()  ``````

``````1.  # An empty feature map to hold the output of convolving the filter(s) with the image.
2.  feature_maps = numpy.zeros((img.shape[0]-conv_filter.shape[1]+1,
3.                              img.shape[1]-conv_filter.shape[1]+1,
4.                              conv_filter.shape[0])) ``````

``````1.   # Convolving the image by the filter(s).
2.      for filter_num in range(conv_filter.shape[0]):
3.          print("Filter ", filter_num + 1)
4.          curr_filter = conv_filter[filter_num, :] # getting a filter from the bank.
5.          """
6.          Checking if there are mutliple channels for the single filter.
7.          If so, then each channel will convolve the image.
8.          The result of all convolutions are summed to return a single feature map.
9.          """
10.         if len(curr_filter.shape) > 2:
11.             conv_map = conv_(img[:, :, 0], curr_filter[:, :, 0]) # Array holding the sum of all feature maps.
12.             for ch_num in range(1, curr_filter.shape[-1]): # Convolving each channel with the image and summing the results.
13.                 conv_map = conv_map + conv_(img[:, :, ch_num],
14.                                   curr_filter[:, :, ch_num])
15.         else: # There is just a single channel in the filter.
16.             conv_map = conv_(img, curr_filter)
17.         feature_maps[:, :, filter_num] = conv_map # Holding feature map with the current filter.  ``````

``1.  curr_filter = conv_filter[filter_num, :] # getting a filter from the bank.    ``

``````1.  if len(curr_filter.shape) > 2:
2.       conv_map = conv_(img[:, :, 0], curr_filter[:, :, 0]) # Array holding the sum of all feature map
3.       for ch_num in range(1, curr_filter.shape[-1]): # Convolving each channel with the image and summing the results.
4.          conv_map = conv_map + conv_(img[:, :, ch_num],
5.                                    curr_filter[:, :, ch_num])
6.  else: # There is just a single channel in the filter.
7.      conv_map = conv_(img, curr_filter)     ``````

``````1.  def conv_(img, conv_filter):
2.      filter_size = conv_filter.shape[0]
3.      result = numpy.zeros((img.shape))
4.      #Looping through the image to apply the convolution operation.
5.      for r in numpy.uint16(numpy.arange(filter_size/2,
6.                            img.shape[0]-filter_size/2-2)):
7.          for c in numpy.uint16(numpy.arange(filter_size/2, img.shape[1]-filter_size/2-2)):
8.              #Getting the current region to get multiplied with the filter.
9.              curr_region = img[r:r+filter_size, c:c+filter_size]
10.             #Element-wise multipliplication between the current region and the filter.
11.             curr_result = curr_region * conv_filter
12.             conv_sum = numpy.sum(curr_result) #Summing the result of multiplication.
13.             result[r, c] = conv_sum #Saving the summation in the convolution layer feature map.
14.
15.     #Clipping the outliers of the result matrix.
16.     final_result = result[numpy.uint16(filter_size/2):result.shape[0]-numpy.uint16(filter_size/2),
17.                           numpy.uint16(filter_size/2):result.shape[1]-numpy.uint16(filter_size/2)]
18.     return final_result      ``````

``1.  curr_region = img[r:r+filter_size, c:c+filter_size]  ``

``````1.  #Element-wise multipliplication between the current region and the filter.
2.  curr_result = curr_region * conv_filter
3.  conv_sum = numpy.sum(curr_result) #Summing the result of multiplication.
4.  result[r, c] = conv_sum #Saving the summation in the convolution layer feature map.   ``````

4. ReLU 层

ReLU 层对卷积层返回的每个特征图应用 ReLU 激活函数。根据以下代码使用 relu 函数使用它：

``l1_feature_map_relu = relu(l1_feature_map)``

relu 函数的实现方式如下：

``````1.  def relu(feature_map):
2.      #Preparing the output of the ReLU activation function.
3.      relu_out = numpy.zeros(feature_map.shape)
4.      for map_num in range(feature_map.shape[-1]):
5.          for r in numpy.arange(0,feature_map.shape[0]):
6.              for c in numpy.arange(0, feature_map.shape[1]):
7.                  relu_out[r, c, map_num] = numpy.max(feature_map[r, c, map_num], 0) ``````

ReLU 层的输出将馈送到最大池化层。

5. 最大池化

``1.  l1_feature_map_relu_pool = pooling(l1_feature_map_relu, 2, 2) ``

``````1.  def pooling(feature_map, size=2, stride=2):
2.      #Preparing the output of the pooling operation.
3.      pool_out = numpy.zeros((numpy.uint16((feature_map.shape[0]-size+1)/stride),
4.                              numpy.uint16((feature_map.shape[1]-size+1)/stride),
5.                              feature_map.shape[-1]))
6.      for map_num in range(feature_map.shape[-1]):
7.          r2 = 0
8.          for r in numpy.arange(0,feature_map.shape[0]-size-1, stride):
9.              c2 = 0
10.             for c in numpy.arange(0, feature_map.shape[1]-size-1, stride):
11.                 pool_out[r2, c2, map_num] = numpy.max(feature_map[r:r+size,  c:c+size])
12.                 c2 = c2 + 1
13.             r2 = r2 +1  ``````

``````1.  pool_out = numpy.zeros((numpy.uint16((feature_map.shape[0]-size+1)/stride),
2.                          numpy.uint16((feature_map.shape[1]-size+1)/stride),
3.                          feature_map.shape[-1]))  ``````

``pool_out[r2, c2, map_num] = numpy.max(feature_map[r:r+size,  c:c+size])``

6. 层级的堆叠

``````1.  # Second conv layer
2.  l2_filter = numpy.random.rand(3, 5, 5, l1_feature_map_relu_pool.shape[-1])
3.  print("\n**Working with conv layer 2**")
4.  l2_feature_map = conv(l1_feature_map_relu_pool, l2_filter)
5.  print("\n**ReLU**")
6.  l2_feature_map_relu = relu(l2_feature_map)
7.  print("\n**Pooling**")
8.  l2_feature_map_relu_pool = pooling(l2_feature_map_relu, 2, 2)
9.  print("**End of conv layer 2**\n")  ``````

``````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 = conv(l2_feature_map_relu_pool, l3_filter)
5.  print("\n**ReLU**")
6.  l3_feature_map_relu = relu(l3_feature_map)
7.  print("\n**Pooling**")
8.  l3_feature_map_relu_pool = pooling(l3_feature_map_relu, 2, 2)
9.  print("**End of conv layer 3**\n")  ``````

``````1.  l2_feature_map = conv(l1_feature_map_relu_pool, l2_filter)
2.  l3_feature_map = conv(l2_feature_map_relu_pool, l3_filter)``````

7. 完整代码