CNN: пересылка пакета изображений - застрял при использовании нескольких ядер - PullRequest
2 голосов
/ 17 мая 2019

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

Предположим, у меня есть 4 изображения с 3 каналами размером 4x4: 4x3x4x4 И у меня есть ядра с 3 каналами размера 3x3: K * 3x3x3

Я пытаюсь вычислитьсвертки на всех 4 изображениях, но я всегда теряюсь в размерности.Вот что я попробовал:

import numpy as np


w = np.array(
    [
        # Img: 1, 4x4 image with 3 channels
        [
            [
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1]
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],
            [
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2]
            ]
        ],
        # Img: 2, 4x4 image with 3 channels
        [
            [
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],
            [
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1]
            ],
            [
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2]
            ]
        ],
        # Img: 3, 4x4 image with 3 channels
        [
            [
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2]
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],
            [
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1]
            ]
        ],
        # Img: 4, 4x4 image with 3 channels
        [
            [
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2],
                [2, 2, 2, 2]
            ],
            [
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [1, 1, 1, 1]
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ]
        ]
    ]
)

f = np.array(
    [
        # Filter: 1, 3x3 filter for 3 channels -> All 1s
        [
            [
                [1, 1, 1],
                [1, 1, 1],
                [1, 1, 1]
            ],
            [
                [1, 1, 1],
                [1, 1, 1],
                [1, 1, 1]
            ],
            [
                [1, 1, 1],
                [1, 1, 1],
                [1, 1, 1]
            ]
        ],
        # Filter: 2, 3x3 filter for 3 channels -> All 2s
        [
            [
                [2, 2, 2],
                [2, 2, 2],
                [2, 2, 2]
            ],
            [
                [2, 2, 2],
                [2, 2, 2],
                [2, 2, 2]
            ],
            [
                [2, 2, 2],
                [2, 2, 2],
                [2, 2, 2]
            ]
        ]
    ]
)

hori_dimension = (w.shape[3] - f.shape[3]) // 1 + 1
vert_dimension = (w.shape[2] - f.shape[2]) // 1 + 1
r = np.zeros(shape=(w.shape[0], f.shape[0], vert_dimension, hori_dimension))
for i in range(vert_dimension):
    for j in range(hori_dimension):
        r[:, :, i, j] += np.sum(w[:, :, i:i+3, j:j+3] * f, axis=(1, 2, 3))
print(r)

Это не работает, и у меня есть проблемы с этой частью, когда у меня есть число ядер K для числа N изображений.

Если яоднако у меня есть одно ядро, и я могу определить свою операцию следующим образом: (Это работает безупречно):

hori_dimension = (w.shape[3] - f.shape[3]) // 1 + 1
vert_dimension = (w.shape[2] - f.shape[2]) // 1 + 1
r = np.zeros(shape=(w.shape[0], vert_dimension, hori_dimension))
for i in range(vert_dimension):
    for j in range(hori_dimension):
        r[:, i, j] += np.sum(w[:, :, i:i+3, j:j+3] * f, axis=(1, 2, 3))
print(r)

, что приводит к возможности 2x2 для каждого изображения:

[[[27. 27.]
  [27. 27.]]

 [[27. 27.]
  [27. 27.]]

 [[27. 27.]
  [27. 27.]]

 [[27. 27.]
  [27. 27.]]]

Это кажется правильным,У меня есть 1 ядро, поэтому у меня будет 1 карта объектов для каждого изображения, что дает 4 карты объектов в 2 измерениях.В вышесказанном я бы ожидал, другое измерение.Имея 4 карты характеристик для каждого ядра, но я не могу понять это.

@ Обновление:

Это похоже на выполнение работы:

p = 0
s = 1
number_of_input_images, number_of_image_channels, height_of_image, width_of_image = w.shape
number_of_kernels, number_of_kernel_channels, height_of_kernel, width_of_kernel = f.shape
assert(number_of_image_channels == number_of_kernel_channels)

width_of_features = (width_of_image - width_of_kernel + 2*p) // s + 1
height_of_features = (height_of_image - height_of_kernel + 2*p) // s + 1
feature_maps = np.zeros(shape=(number_of_input_images, number_of_kernels, height_of_features, width_of_features))

for k in range(f.shape[0]):
    for i in range(height_of_features):
        for j in range(width_of_features):
            feature_maps[:, k, i, j] += np.sum(w[:, :, i:i+3, j:j+3] * f[k], axis=(1, 2, 3))

print(feature_maps)

Этоприводит к следующим картам функций:

[
    # pic1
    [
        # kernel1
        [
            [27. 27.]
            [27. 27.]
        ]
        # kernel2
        [
            [54. 54.]
            [54. 54.]
        ]
    ]
    # pic2
    [
        #kernel1
        [
            [27. 27.]
            [27. 27.]
        ]
        #kernel2
        [
            [54. 54.]
            [54. 54.]
        ]
    ]
    #pic3
    [
        #kernel1
        [
            [27. 27.]
            [27. 27.]
        ]
        #kernel2
        [
            [54. 54.]
            [54. 54.]
        ]
    ]
    #pic4
    [
        #kernel1
        [
            [27. 27.]
            [27. 27.]
        ]
        #kerbel2
        [
            [54. 54.]
            [54. 54.]
        ]
    ]
]

Есть ли лучший способ сделать это?Это даже правильно?Мне кажется, это нормально.Имея изображение и несколько ядер, результатом свертки будет карта характеристик каждого ядра, поставленная «после» другого права?Таким образом, при количестве ядер K, в котором карты объектов имеют N N измерений, вывод сверточного слоя становится K N * N.Таким образом, вышесказанное кажется правильным, я думаю?Как я уже сказал, я действительно испортил эти N измерений ...

@ Обновление:

Я получил следующий код для действительных (прямая) / полная (обратная) сверток:

def convolve(sources: np.ndarray,
             kernels: np.ndarray,
             mode: str = 'valid',
             padding: typing.Tuple[int] = (0, 0),
             stride: int = 1):
    number_of_input_images, number_of_image_channels, height_of_image, width_of_image = sources.shape
    number_of_kernels, number_of_kernel_channels, height_of_kernel, width_of_kernel = kernels.shape
    assert(number_of_image_channels == number_of_kernel_channels)

    if mode == 'full':
        padding = (height_of_kernel, width_of_kernel)

    if padding:
        sources = np.pad(sources,
                         ((0, 0), (0, 0), (padding[0], padding[0]), (padding[1], padding[1])),
                         mode='constant', constant_values=0)

    kernels = np.rot90(kernels, k=2, axes=(2, 3))

    width_of_features = (width_of_image - width_of_kernel + 2*padding[1]) // stride + 1
    height_of_features = (height_of_image - height_of_kernel + 2*padding[0]) // stride + 1
    feature_maps = np.zeros(shape=(number_of_input_images, number_of_kernels, height_of_features, width_of_features))

    for k in range(f.shape[0]):
        for i in range(height_of_features):
            for j in range(width_of_features):
                feature_maps[:, k, i, j] = np.einsum('ncij,cij', sources[:, :, i:i+3, j:j+3],  kernels[k])

    return feature_maps

Любые отзывы будут оценены.Я читал, что ядра должны быть повернуты при выполнении свертки, поэтому я поворачиваюсь дважды на 90 градусов, также есть возможность использовать пользовательское заполнение, и для полной сверточности я дополняю до размера ядра -1, чтобы всеокружающие элементы равны нулю, и я не получаю ошибок индекса.

Ответы [ 2 ]

1 голос
/ 17 мая 2019

Давайте посмотрим на одно изображение и одно ядро ​​за раз. Если ваше изображение имеет размер wxh, а ваше ядро ​​имеет размер f*f, и если вы шагаете (е) по одному пикселю за раз, и если вы увеличиваете яркость с помощью p пикселей, то после свертки 1 изображения с 1 Ядро приведет к изображению размером (w-f+2*p)/s + 1, (h-f+2*p)/s +1). В вашем случае w=h=4, f=3, s=1 и p=0.

  1. Сначала вы берете патч f*f с изображения. Поскольку у вас есть 3 канала, каждый патч будет состоять из 3 каналов
  2. Ядра каждого канала умножаются на соответствующий канал в патче (поэлементное умножение)
  3. Наконец, все числа во всех каналах суммируются для получения одного единственного числа.

Графическое представление

enter image description here

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

Это делается для каждого изображения, и в результате вы получаете меньшее свернутое изображение для каждого изображения.

Пример кода

images = np.ones((2,3,4,4))
kernal = np.ones((3,3,3))
w = 4
f = 3
p = 0
s = 1
r = np.ones((2, 
        int((w-f+2*p)/s +1), int((w-f+2*p)/s +1)))
for i, image in enumerate(images):
    for h in range((4//3)+1):
        for w in range((4//3)+1):            
            x = np.sum(image[:, w:w+3,h:h+3]*kernal)
            r[i,w,h] = x
print (r)

Выход:

[[[27. 27.]
  [27. 27.]]

 [[27. 27.]
  [27. 27.]]]

2 изображения размером 4x4, свернутые с ядром размера 3x3, дадут вам 2 изображения размером 2x2 (проверьте (4-3+0/1 +1, 4-3+0/1 +1))

Необходимо прочитать ресурс: CV231n

0 голосов
/ 26 мая 2019

Завершено следующим фрагментом кода для обработки свертки пакета:

def convolve(input_images, kernels, padding=0, stride=1):
    number_of_images, image_height, image_width, image_channels = input_images.shape
    number_of_kernels, kernel_height, kernel_width, kernel_depth = kernels.shape
    assert (image_channels == kernel_depth)

    input_images = np.pad(input_images, ((0, 0), (padding, padding), (padding, padding), (0, 0)),
                          mode='constant', constant_values=(0,))
    kernels = np.rot90(kernel, k=2, axes=(1, 2))

    fm_height = (image_height - kernel_height + 2*padding) // stride + 1
    fm_width = (image_width - kernel_width + 2*padding) // stride + 1
    feature_maps = np.zeros(shape=(number_of_images, fm_height, fm_width, number_of_kernels))
    for n in range(number_of_images):
        for i in range(fm_height):
            for j in range(fm_width):
                x = input_images[n, i * stride:i * stride + kernel_height, j * stride:j * stride + kernel_width, :]
                feature_maps[n, i, j, :] = np.einsum('ijk,mijk', x, kernels)
    return feature_maps
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...