Как поэкспериментировать с кастомными ядрами 2d-свертки в Keras? - PullRequest
0 голосов
/ 08 января 2019

В слое Conv2D по умолчанию с kernel_size=3 веса среза одного из фильтров можно назвать так:

A B C
D E F
G H I

С kernel_size=5 вот так:

A B C D E
F G H I J
K L M N O
P Q R S T
U V W X Y

Теперь я хотел бы построить (и обучить / протестировать) модель, основанную на сложных слоях с ядрами, подобными этому:

A A B C C
A A B C C
D D E F F
G G H I I
G G H I I

Как может выглядеть реализация такого пользовательского слоя ?

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Может, вот так?

class CustomConv2D(Layer):
    def __init__(self, filters, **kwargs):
        self.filters = filters
        self.kernel_size = (3, 3)
        super(CustomConv2D, self).__init__(**kwargs)

    def build(self, input_shape):
        # only have a 3x3 kernel
        shape = self.kernel_size + (input_shape[-1], self.filters)
        self.kernel = self.add_weight(name='kernel', shape=shape,
                                      initializer='glorot_uniform')
        super(CustomConv2D, self).build(input_shape)

    def call(self, x):
        # duplicate rows 0 and 2
        dup_rows = K.stack([self.kernel[0]]*2 + [self.kernel[1]] + [self.kernel[2]]*2, axis=0)
        # duplicate cols 0 and 2
        dup_cols = K.stack([dup_rows[:,0]]*2 + [dup_rows[:,1]] + [dup_rows[:,2]]*2, axis=1)
        # having a 5x5 kernel now
        return K.conv2d(x, dup_cols)

    def compute_output_shape(self, input_shape):
        return input_shape[:-1] + (self.filters,)

Хитрость заключается в том, чтобы просто хранить только 9 весов на фильтр в ядре 3x3 (жестко запрограммировать, вы можете обобщить его) и дублировать первые и последние строки и столбцы, чтобы сделать это ядро ​​5x5 так, как вы этого хотите. , Затем это ядро ​​передается в K.conv2d(), как в оригинальной реализации Conv2d .

Я проверил это, и, кажется, работает Вы можете добавить другие параметры, такие как отступ, смещение и т. Д.

0 голосов
/ 08 января 2019

Я думаю, что это базовая версия того, что вы намереваетесь:

from keras import backend as K

class Conv2DTiledKernel(Layer):
    def __init__(self, filters, kernel_size, multiplies, **kwargs):
        self.filters = filters
        self.kernel_size = kernel_size
        self.multiplies = multiplies
        super(Conv2DTiledKernel, self).__init__(**kwargs)
    def build(self, input_shape):
        shape = list(self.kernel_size) + [input_shape[-1], self.filters]
        self.kernel = self.add_weight(name='kernel', shape=shape,
                                      initializer='glorot_uniform')
        super(Conv2DTiledKernel, self).build(input_shape)
    def call(self, x):
        mult = list(self.multiplies) + [1, 1]
        kernel_tiled = K.tile(self.kernel, mult)
        return K.conv2d(x, kernel_tiled)
    def compute_output_shape(self, input_shape):
        return input_shape[:-1] + (self.filters,)

fitlers - это число выходных каналов, kernel_size размер каждого канала ядра и multiplies коэффициенты листов. Вы бы использовали что-то вроде этого:

from keras.models import Model
from keras.layers import Input, Layer

img = Input(shape=(64, 64, 3))
output = Conv2DTiledKernel(10, [1, 5], [5, 1])(img)
model = Model(inputs=img, outputs=output)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...