Keras / Tensorflow - реализация точечного умножения Фурье для conv2d, работающего в 4 раза медленнее, чем пространственная свертка - PullRequest
0 голосов
/ 01 апреля 2019

Согласно теореме о свертке, свертка меняется на точечное умножение в области Фурье, и было показано, что накладные расходы на преобразование Фурье затеняются усилением из-за преобразования операции свертки в операцию точечного умножения во многих предыдущих работах. как следующее - https://arxiv.org/abs/1312.5851.

Чтобы повторить это, я пытался заменить слой keras.layers.Conv2D () пользовательским слоем, который принимает количество входных данных (я взял количество данных, прежде чем вводить их в модель, чтобы сократить время обучения). , инициализирует число ядер no_of_kernels того же размера, что и изображение, берет его rfft, умножает ввод и ядро ​​поточечно и возвращает продукт (да, не беря irfft, так как я хочу дополнительно обучить сеть в самой области Фурье) -

На уровне функция вызова реализована следующим образом: Обратите внимание - в моем наборе данных, то есть высота изображения MNIST = ширина, поэтому транспонирование работает нормально

def call(self, x):
        fft_x = x #(batch_size, height, width, in_channels)
        fft_kernel = tf.spectral.rfft2d(self.kernel) #(in_channels, height, width, out_channels)
        fft_kernel = tf.transpose(fft_kernel, perm=[2, 1, 0, 3]) #(width, height, in_channels, out_channels)
        output  = tf.einsum('ijkl,jklo->ijko', fft_x, fft_kernel)
        return output 

Этот код сохраняет точность, заданную слоем Keras Conv2D, но он работает примерно в 4 раза медленнее, чем Conv2D, поэтому цель преобразования в область Фурье не выполняется. Может ли кто-нибудь объяснить, почему это происходит и как я могу воспроизвести результаты быстрых сверток в области Фурье?

(Примечание. Для тех, кто может чувствовать, что tf.spectral.rfft2d (self.kernel) может быть излишним, это не тот случай, как я убедился.

Кроме того, я думаю, что функция Conv2D может сглаживать входные тензоры и ядра 4D, чтобы уменьшить их до умножения матриц, как объяснено здесь - введите описание ссылки здесь . Я не мог придумать какого-либо интеллектуального метода сглаживания и т. Д. Для точечного умножения, кроме как рассматривать его как точечное произведение, как я делал с tf.einsum. Есть ли какой-нибудь разумный метод для точечного умножения? ) Спасибо.

Редактировать - Полная реализация слоя для справки -

class Fourier_Conv2D(Layer):
    def __init__(self, no_of_kernels, **kwargs):
        self.no_of_kernels = no_of_kernels
        super(Fourier_Conv2D, self).__init__(**kwargs)

    def build(self, input_shape):
        self.kernel_shape = (int(input_shape[3]), int(input_shape[1]), int(input_shape[2]), self.no_of_kernels)
        self.kernel = self.add_weight(name = 'kernel', 
                                      shape = self.kernel_shape, 
                                      initializer = 'uniform', trainable = True)
        super(Fourier_Conv2D, self).build(input_shape)

    def call(self, x):
        fft_x = x
        fft_kernel = tf.spectral.rfft2d(self.kernel)
        fft_kernel = tf.transpose(fft_kernel, perm=[2, 1, 0, 3])
        output  = tf.einsum('ijkl,jklo->ijko', fft_x, fft_kernel)
        return output       

    def compute_output_shape(self, input_shape):
        return (input_shape[0], input_shape[1], input_shape[2], int(self.no_of_kernels/2)+1)

1 Ответ

0 голосов
/ 01 апреля 2019

Я не думаю, что ваш результат вообще удивителен: реализация Conv2D в Keras оставлена ​​бэкэнду, и большинство бэкэндов (таких как TensorFlow) имеют очень оптимизированные версии операций свертки, особенно если вы используете CuDNN.Таким образом, ваша собственная версия, которая должна быть быстрее наивной реализации, медленнее, чем высокооптимизированная.

Возможно, что для проведения значимого сравнения вам потребуется реализовать базовый Conv2D, который выполняет сверткунаивно, без каких-либо оптимизаций.

...