Согласно теореме о свертке, свертка меняется на точечное умножение в области Фурье, и было показано, что накладные расходы на преобразование Фурье затеняются усилением из-за преобразования операции свертки в операцию точечного умножения во многих предыдущих работах. как следующее - 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)