www.machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home
Занимательная история, выдающиеся люди, малоизвестные факты, находки, открытия, фальсификации. Присоединяйся!

Построение сверточной нейронной сети с использованием NumPy с нуля

Дата публикации Jun 27, 2018

Использование уже существующих моделей в библиотеках ML / DL может быть полезным в некоторых случаях. Но чтобы лучше контролировать и понимать, вы должны попытаться реализовать их самостоятельно. В этой статье показано, как CNN реализован только с помощью NumPy.


Введение

Сверточная нейронная сеть (CNN) - это современная методика анализа многомерных сигналов, таких как изображения. Существуют разные библиотеки, которые уже реализуют CNN, такие как TensorFlow и Keras. Такие библиотеки изолируют разработчика от некоторых деталей и просто предоставляют абстрактный API, чтобы упростить жизнь и избежать сложности в реализации. Но на практике такие детали могут иметь значение. Иногда специалисту по данным приходится проходить через такие детали, чтобы повысить производительность. Решение в такой ситуации состоит в том, чтобы создать каждую часть такой модели самостоятельно. Это дает максимально возможный уровень контроля над сетью. Также рекомендуется применять такие модели, чтобы лучше их понимать.

В этой статье CNN создается с использованием только библиотеки NumPy. Создается всего три слоя: свертка (для краткости свёртка), ReLU и максимальный пул. Основными этапами являются следующие:

1. Чтение входного изображения.

2. Подготовка фильтров.

3. Conv layer: свертка каждого фильтра с входным изображением.

4. Слой ReLU: применение функции активации ReLU на картах объектов (вывод конвоя).

5. Максимальный уровень пула: применение операции пула на выходе уровня ReLU.

6. Укладка конв, ReLU и максимальный пул слоев.


1. Чтение входного изображения

Следующий код читает уже существующее изображение из библиотеки Python skimage и преобразует его в серый.

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

Чтение изображения является первым шагом, потому что следующие шаги зависят от размера ввода. Изображение после преобразования в серый показано ниже.

Рисунок 1. Оригинальное серое изображение. Это изображение Skimage с именем Chelsea, доступ к которому осуществляется через skimage.data.chelsea ()

2. Подготовка фильтров

Следующий код подготавливает банк фильтров для первого слоя сравнения (l1короче):

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

Нулевой массив создается в соответствии с количеством фильтров и размером каждого фильтра.2фильтры размера3x3создаются, поэтому нулевой массив имеет размер (2= num_filters,3= Num_rows_filter,3= Num_columns_filter). Размер фильтра выбирается как двумерный массив без глубины, потому что входное изображение имеет серый цвет и не имеет глубины (то есть 2D). Если изображение RGB с 3 каналами, размер фильтра должен быть (3, 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)

Такая функция принимает только два аргумента, которые являются изображением и банком фильтров, который реализован, как показано ниже.

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

Поскольку нет ни шагов, ни отступов, размер карты объектов будет равен (img_rows-filter_rows + 1, image_columns-filter_columns + 1, num_filters), как указано выше в коде. Обратите внимание, что для каждого фильтра в банке есть карта выходных объектов. Именно поэтому количество фильтров в банке фильтров (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.

Внешний цикл перебирает каждый фильтр в банке фильтров и возвращает его для дальнейших шагов в соответствии с этой строкой:

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

Вы можете заметить, что свертка применяется функцией под названиемconv_который отличается отконвфункция. Функцияконвпросто принимает входное изображение и банк фильтров, но не применяет свертку самостоятельно. Он просто передает каждый набор пар входного фильтра для свертки вconv_функция. Это просто для упрощения изучения кода. Вот реализацияconv_функция:

def conv_(img, conv_filter):
filter_size = conv_filter.shape[1]
result = numpy.zeros((img.shape))
#Looping through the image to apply the convolution operation.
for r in numpy.uint16(numpy.arange(filter_size/2.0,
img.shape[0]-filter_size/2.0+1)):
for c in numpy.uint16(numpy.arange(filter_size/2.0,
img.shape[1]-filter_size/2.0+1)):
"""
Getting the current region to get multiplied with the filter.
How to loop through the image and get the region based on
the image and filer sizes is the most tricky part of convolution.
"""
curr_region = img[r-numpy.uint16(numpy.floor(filter_size/2.0)):r+numpy.uint16(numpy.ceil(filter_size/2.0)),
c-numpy.uint16(numpy.floor(filter_size/2.0)):c+numpy.uint16(numpy.ceil(filter_size/2.0))]
#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.0):result.shape[0]-numpy.uint16(filter_size/2.0),
numpy.uint16(filter_size/2.0):result.shape[1]-numpy.uint16(filter_size/2.0)]
return final_result

Он перебирает изображение и выделяет области равного размера для фильтра в соответствии с этой строкой:

curr_region = img[r-numpy.uint16(numpy.floor(filter_size/2.0)):r+numpy.uint16(numpy.ceil(filter_size/2.0)), 
c-numpy.uint16(numpy.floor(filter_size/2.0)):c+numpy.uint16(numpy.ceil(filter_size/2.0))]

Затем он применяет поэлементное умножение между регионом и фильтром и суммирует их, чтобы получить одно значение в качестве результата в соответствии с этими строками:

#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 показаны карты объектов, возвращаемые таким конвекс-слоем.

Рисунок 2. Выходные карты характеристик первого конвон-слоя.

Вывод такого слоя будет применен к слою ReLU.

4. ReLU Layer

Уровень ReLU применяет функцию активации ReLU к каждой карте объектов, возвращаемой уровнем конв. Это называется с помощьюReluфункционировать в соответствии со следующей строкой кода:

l1_feature_map_relu = relu(l1_feature_map)

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])
return relu_out

Это очень просто. Просто зациклите каждый элемент в карте объектов и верните исходное значение в карте объектов, если оно больше 0. В противном случае верните 0. Выходные данные уровня ReLU показаны на рисунке 3.

Рисунок 3. Вывод уровня ReLU, примененный к выводу первого слоя уровня

Выходные данные уровня 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, map_num]])
c2 = c2 + 1
r2 = r2 +1
return pool_out

Функция принимает три входа, которые являются выходом уровня ReLU, размера маски пула и шага. Он просто создает пустой массив, как и предыдущий, который содержит выходные данные такого слоя. Размер такого массива указывается в соответствии с аргументами размера и шага, как в следующей строке:

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

Затем он проходит через вход, канал за каналом в соответствии с внешним циклом, который использует переменную циклаmap_num, Для каждого канала на входе применяется операция максимального пула. В соответствии с используемым шагом и размером область обрезается, и ее максимум возвращается в выходной массив в соответствии с этой строкой:

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

Выходные данные такого пула показаны на следующем рисунке. Обратите внимание, что размер выходного слоя пула меньше его входного значения, даже если они кажутся идентичными на своих графиках.

Рисунок 4. Вывод уровня пула, примененный к выводу первого уровня ReLU

6. Укладка слоев

К этому моменту архитектура CNN с уровнями conv, ReLU и max pooling завершена. Там может быть несколько других слоев, которые будут сложены в дополнение к предыдущим, как показано ниже.

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

Предыдущий конв слой использует3фильтры с их значениями, сгенерированными случайным образом. Вот почему там будет3карты характеристик, полученные в результате такого конвоя. Это также относится и к последовательным уровням ReLU и пула. Выходы таких слоев показаны на рисунке 5.

Рисунок 5. Вывод второго слоя conv-ReLU-Pooling
# 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 = numpycnn.conv(l2_feature_map_relu_pool, l3_filter)
print("\n**ReLU**")
l3_feature_map_relu = numpycnn.relu(l3_feature_map)
print("\n**Pooling**")
l3_feature_map_relu_pool = numpycnn.pooling(l3_feature_map_relu, 2, 2)
print("**End of conv layer 3**\n")

На рисунке 6 показаны результаты предыдущих слоев. Предыдущий слой конвоя принимает только один фильтр. Вот почему в качестве выходных данных используется только одна карта объектов.

Рисунок 6. Выходы третьего уровня conv-ReLU-Pooling

Но помните, что выход каждого предыдущего слоя является входом для следующего слоя. Например, такие строки принимают предыдущие выходы в качестве своих входов.

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

7. Полный код

Полный код доступен вGitHub(https://github.com/ahmedfgad/NumPyCNN). Код содержит визуализацию результатов каждого слоя с использованиемMatplotlibбиблиотека.

import skimage.data
import numpy
import matplotlib
import sysdef conv_(img, conv_filter):
filter_size = conv_filter.shape[1]
result = numpy.zeros((img.shape))
#Looping through the image to apply the convolution operation.
for r in numpy.uint16(numpy.arange(filter_size/2.0,
img.shape[0]-filter_size/2.0+1)):
for c in numpy.uint16(numpy.arange(filter_size/2.0,
img.shape[1]-filter_size/2.0+1)):
"""
Getting the current region to get multiplied with the filter.
How to loop through the image and get the region based on
the image and filer sizes is the most tricky part of convolution.
"""
curr_region = img[r-numpy.uint16(numpy.floor(filter_size/2.0)):r+numpy.uint16(numpy.ceil(filter_size/2.0)),
c-numpy.uint16(numpy.floor(filter_size/2.0)):c+numpy.uint16(numpy.ceil(filter_size/2.0))]
#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.0):result.shape[0]-numpy.uint16(filter_size/2.0),
numpy.uint16(filter_size/2.0):result.shape[1]-numpy.uint16(filter_size/2.0)]
return final_result
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.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, map_num]])
c2 = c2 + 1
r2 = r2 +1
return pool_outdef 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])
return relu_out# Reading the image
#img = skimage.io.imread("fruits2.png")
img = skimage.data.chelsea()
# Converting the image into gray.
img = skimage.color.rgb2gray(img)# First conv layer
#l1_filter = numpy.random.rand(2,7,7)*20 # Preparing the filters randomly.
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]]])print("\n**Working with conv layer 1**")
l1_feature_map = conv(img, l1_filter)
print("\n**ReLU**")
l1_feature_map_relu = relu(l1_feature_map)
print("\n**Pooling**")
l1_feature_map_relu_pool = pooling(l1_feature_map_relu, 2, 2)
print("**End of conv layer 1**\n")# Second conv layer
l2_filter = numpy.random.rand(3, 5, 5, l1_feature_map_relu_pool.shape[-1])
print("\n**Working with conv layer 2**")
l2_feature_map = conv(l1_feature_map_relu_pool, l2_filter)
print("\n**ReLU**")
l2_feature_map_relu = relu(l2_feature_map)
print("\n**Pooling**")
l2_feature_map_relu_pool = pooling(l2_feature_map_relu, 2, 2)
print("**End of conv layer 2**\n")# 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")# Graphing results
fig0, ax0 = matplotlib.pyplot.subplots(nrows=1, ncols=1)
ax0.imshow(img).set_cmap("gray")
ax0.set_title("Input Image")
ax0.get_xaxis().set_ticks([])
ax0.get_yaxis().set_ticks([])
matplotlib.pyplot.savefig("in_img.png", bbox_inches="tight")
matplotlib.pyplot.close(fig0)# Layer 1
fig1, ax1 = matplotlib.pyplot.subplots(nrows=3, ncols=2)
ax1[0, 0].imshow(l1_feature_map[:, :, 0]).set_cmap("gray")
ax1[0, 0].get_xaxis().set_ticks([])
ax1[0, 0].get_yaxis().set_ticks([])
ax1[0, 0].set_title("L1-Map1")ax1[0, 1].imshow(l1_feature_map[:, :, 1]).set_cmap("gray")
ax1[0, 1].get_xaxis().set_ticks([])
ax1[0, 1].get_yaxis().set_ticks([])
ax1[0, 1].set_title("L1-Map2")ax1[1, 0].imshow(l1_feature_map_relu[:, :, 0]).set_cmap("gray")
ax1[1, 0].get_xaxis().set_ticks([])
ax1[1, 0].get_yaxis().set_ticks([])
ax1[1, 0].set_title("L1-Map1ReLU")ax1[1, 1].imshow(l1_feature_map_relu[:, :, 1]).set_cmap("gray")
ax1[1, 1].get_xaxis().set_ticks([])
ax1[1, 1].get_yaxis().set_ticks([])
ax1[1, 1].set_title("L1-Map2ReLU")ax1[2, 0].imshow(l1_feature_map_relu_pool[:, :, 0]).set_cmap("gray")
ax1[2, 0].get_xaxis().set_ticks([])
ax1[2, 0].get_yaxis().set_ticks([])
ax1[2, 0].set_title("L1-Map1ReLUPool")ax1[2, 1].imshow(l1_feature_map_relu_pool[:, :, 1]).set_cmap("gray")
ax1[2, 0].get_xaxis().set_ticks([])
ax1[2, 0].get_yaxis().set_ticks([])
ax1[2, 1].set_title("L1-Map2ReLUPool")matplotlib.pyplot.savefig("L1.png", bbox_inches="tight")
matplotlib.pyplot.close(fig1)# Layer 2
fig2, ax2 = matplotlib.pyplot.subplots(nrows=3, ncols=3)
ax2[0, 0].imshow(l2_feature_map[:, :, 0]).set_cmap("gray")
ax2[0, 0].get_xaxis().set_ticks([])
ax2[0, 0].get_yaxis().set_ticks([])
ax2[0, 0].set_title("L2-Map1")ax2[0, 1].imshow(l2_feature_map[:, :, 1]).set_cmap("gray")
ax2[0, 1].get_xaxis().set_ticks([])
ax2[0, 1].get_yaxis().set_ticks([])
ax2[0, 1].set_title("L2-Map2")ax2[0, 2].imshow(l2_feature_map[:, :, 2]).set_cmap("gray")
ax2[0, 2].get_xaxis().set_ticks([])
ax2[0, 2].get_yaxis().set_ticks([])
ax2[0, 2].set_title("L2-Map3")ax2[1, 0].imshow(l2_feature_map_relu[:, :, 0]).set_cmap("gray")
ax2[1, 0].get_xaxis().set_ticks([])
ax2[1, 0].get_yaxis().set_ticks([])
ax2[1, 0].set_title("L2-Map1ReLU")ax2[1, 1].imshow(l2_feature_map_relu[:, :, 1]).set_cmap("gray")
ax2[1, 1].get_xaxis().set_ticks([])
ax2[1, 1].get_yaxis().set_ticks([])
ax2[1, 1].set_title("L2-Map2ReLU")ax2[1, 2].imshow(l2_feature_map_relu[:, :, 2]).set_cmap("gray")
ax2[1, 2].get_xaxis().set_ticks([])
ax2[1, 2].get_yaxis().set_ticks([])
ax2[1, 2].set_title("L2-Map3ReLU")ax2[2, 0].imshow(l2_feature_map_relu_pool[:, :, 0]).set_cmap("gray")
ax2[2, 0].get_xaxis().set_ticks([])
ax2[2, 0].get_yaxis().set_ticks([])
ax2[2, 0].set_title("L2-Map1ReLUPool")ax2[2, 1].imshow(l2_feature_map_relu_pool[:, :, 1]).set_cmap("gray")
ax2[2, 1].get_xaxis().set_ticks([])
ax2[2, 1].get_yaxis().set_ticks([])
ax2[2, 1].set_title("L2-Map2ReLUPool")ax2[2, 2].imshow(l2_feature_map_relu_pool[:, :, 2]).set_cmap("gray")
ax2[2, 2].get_xaxis().set_ticks([])
ax2[2, 2].get_yaxis().set_ticks([])
ax2[2, 2].set_title("L2-Map3ReLUPool")matplotlib.pyplot.savefig("L2.png", bbox_inches="tight")
matplotlib.pyplot.close(fig2)# Layer 3
fig3, ax3 = matplotlib.pyplot.subplots(nrows=1, ncols=3)
ax3[0].imshow(l3_feature_map[:, :, 0]).set_cmap("gray")
ax3[0].get_xaxis().set_ticks([])
ax3[0].get_yaxis().set_ticks([])
ax3[0].set_title("L3-Map1")ax3[1].imshow(l3_feature_map_relu[:, :, 0]).set_cmap("gray")
ax3[1].get_xaxis().set_ticks([])
ax3[1].get_yaxis().set_ticks([])
ax3[1].set_title("L3-Map1ReLU")ax3[2].imshow(l3_feature_map_relu_pool[:, :, 0]).set_cmap("gray")
ax3[2].get_xaxis().set_ticks([])
ax3[2].get_yaxis().set_ticks([])
ax3[2].set_title("L3-Map1ReLUPool")matplotlib.pyplot.savefig("L3.png", bbox_inches="tight")
matplotlib.pyplot.close(fig3)

Оригинальная статья доступна в LinkedIn по этой ссылке:

Построение сверточной нейронной сети с использованием NumPy с нуля

Использование уже существующих моделей в библиотеках ML / DL может быть полезным в некоторых случаях. Но чтобы иметь лучший контроль и ...

www.linkedin.com


Для связи с автором

Ахмед Фавзи Гад

LinkedIn:

Ахмед Гад - участник - AI Tribune | LinkedIn

Просмотрите профиль Ахмеда Гада на LinkedIn, крупнейшем в мире профессиональном сообществе. Ахмед имеет 11 рабочих мест, перечисленных на их ...

linkedin.com

Эл. адрес:

[email protected]

Оригинальная статья

Footer decor

© www.machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map