实战教程 | 使用Scratch中的NumPy构建卷积神经网络，开放源码

使用Scratch中的NumPy构建卷积神经网络

1. 读取输入图像
2. 准备过滤器
3. 转换层：将每个滤镜与输入图像进行卷积
4. ReLU层：在特征图上应用ReLU激活功能（conv层的输出）
5. 最大池化层：在ReLU层的输出上应用池化操作
6. 堆叠conv，ReLU和max池化层

（卷积神经网络的其他案例在我之前的专栏中也有写过，例如：手写字识别面部表情识别等）

0. 软件环境搭建

pip install pygad

pip install pygad

1.读取输入图像

import skimage.data
img = skimage.data.chelsea()
# Converting the image into gray.
img = skimage.color.rgb2gray(img)js

图1 输入图像

2.准备滤波器

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

l1_filter[0, :, :] = numpy.array([[[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]]])
l1_filter[1, :, :] = numpy.array([[[1,   1,  1],
[0,   0,  0],
[-1, -1, -1]]])

3.卷积层（Conv Layer）

l1_feature_map = conv(img, l1_filter)

conv函数只接受两个参数，分别为输入图像、滤波器组：

def conv(img, conv_filter):
if len(img.shape) > 2 or len(conv_filter.shape) > 3: # Check if number of image channels matches the filter depth.
if img.shape[-1] != conv_filter.shape[-1]:
print("Error: Number of channels in both image and filter must match.")
sys.exit()
if conv_filter.shape[1] != conv_filter.shape[2]: # Check if filter dimensions are equal.
print('Error: Filter must be a square matrix. I.e. number of rows and columns must match.')
sys.exit()
if conv_filter.shape[1]%2==0: # Check if filter diemnsions are odd.
print('Error: Filter must have an odd size. I.e. number of rows and columns must be odd.')
sys.exit()
# An empty feature map to hold the output of convolving the filter(s) with the image.
feature_maps = numpy.zeros((img.shape[0]-conv_filter.shape[1]+1,
img.shape[1]-conv_filter.shape[1]+1,
conv_filter.shape[0]))
# Convolving the image by the filter(s).
for filter_num in range(conv_filter.shape[0]):
print("Filter ", filter_num + 1)
curr_filter = conv_filter[filter_num, :] # getting a filter from the bank.
"""
Checking if there are mutliple channels for the single filter.
If so, then each channel will convolve the image.
The result of all convolutions are summed to return a single feature map.
"""
if len(curr_filter.shape) > 2:
conv_map = conv_(img[:, :, 0], curr_filter[:, :, 0]) # Array holding the sum of all feature maps.
for ch_num in range(1, curr_filter.shape[-1]): # Convolving each channel with the image and summing the results.
conv_map = conv_map + conv_(img[:, :, ch_num],
curr_filter[:, :, ch_num])
else: # There is just a single channel in the filter.
conv_map = conv_(img, curr_filter)
feature_maps[:, :, filter_num] = conv_map # Holding feature map with the current filter.
return feature_maps # Returning all feature maps.

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

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

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

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

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

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

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

curr_region = img[r:r+filter_size, c:c+filter_size]

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

图2 卷积后图像

4.ReLU激活函数层

ReLU层将ReLU激活函数应用于conv层输出的每个特征图上，根据以下代码行调用ReLU激活函数：

l1_feature_map_relu = relu(l1_feature_map)

ReLU激活函数（ReLU）的具体实现代码如下：

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

ReLU思想很简单，只是将特征图中的每个元素与0进行比较，若大于0，则保留原始值。否则将其设置为0。ReLU层的输出如下图所示：

图3 ReLU层输出图像

5.最大池化层

ReLU层的输出作为最大池化层的输入，根据下面的代码行调用最大池化操作：

l1_feature_map_relu_pool = pooling(l1_feature_map_relu, 2, 2)

def pooling(feature_map, size=2, stride=2):
Preparing the output of the pooling operation.
pool_out = numpy.zeros((numpy.uint16((feature_map.shape[0]-size+1)/stride),
numpy.uint16((feature_map.shape[1]-size+1)/stride),
feature_map.shape[-1]))
for map_num in range(feature_map.shape[-1]):
r2 = 0
for r in numpy.arange(0,feature_map.shape[0]-size-1, stride):
c2 = 0
for c in numpy.arange(0, feature_map.shape[1]-size-1, stride):
pool_out[r2, c2, map_num] = numpy.max(feature_map[r:r+size,  c:c+size])
c2 = c2 + 1
r2 = r2 +1

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

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

图4 池化层输出图像

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")

图4 l2层处理过程可视化图像

# Third conv layer
l3_filter = numpy.random.rand(1, 7, 7, l2_feature_map_relu_pool.shape[-1])
print("\n**Working with conv layer 3**")
l3_feature_map = conv(l2_feature_map_relu_pool, l3_filter)
print("\n**ReLU**")
l3_feature_map_relu = relu(l3_feature_map)
print("\n**Pooling**")
l3_feature_map_relu_pool = pooling(l3_feature_map_relu, 2, 2)
print("**End of conv layer 3**\n"）

图5 l3层处理过程可视化图像

l2_feature_map = conv(l1_feature_map_relu_pool, l2_filter)
l3_feature_map = conv(l2_feature_map_relu_pool, l3_filter)

实践指南

|
4天前
|

CNN构建网络
【8月更文挑战第10天】CNN构建网络。
35 22
|
1天前
|
Python
NumPy 教程 之 Numpy 数组操作 19

6 2
|
3天前
|
Python
NumPy 教程 之 Numpy 数组操作 13

11 4
|
2天前
|
Python
NumPy 教程 之 Numpy 数组操作 16

10 2
|
4天前
|
Python
NumPy 教程 之 Numpy 数组操作 10

12 4
|
5天前
|
Python
NumPy 教程 之 Numpy 数组操作 7

16 5
|
1天前
|
Python
NumPy 教程 之 Numpy 数组操作 16

8 1
|
2天前
|
Python
NumPy 教程 之 Numpy 数组操作 14

7 1
|
5天前
|

DNN构建网络
【8月更文挑战第9天】DNN构建网络。
9 3
|
4天前
|

NumPy 教程 之 Numpy 数组操作 9

7 0