Тензорный поток: измените форму [N,H,W,C] на [N*C,H,W,1] для свертки на канал

#tensorflow #reshape #convolution

Вопрос:

Чего я хотел бы добиться, так это применения 2D-свертки с одним фильтром, который применяется по всем каналам. Обратите внимание, что я ищу не глубинную свертку, а действительно один фильтр. Чтобы сделать это, мой план состоял в том , чтобы изменить [N,H,W,C] [N*C,H,W,1] форму, применить свертку, а затем изменить ее обратно, чтобы мой результат был [N,H,W,C] снова.

 _, self.h, self.w, self.c = inputs.shape self.conv = tf.keras.layers.Conv2D(filters=2, kernel_size=3, strides=1, padding='same') x = tf.reshape(inputs, [-1,self.h,self.w,1]) x = self.conv(x) x = tf.math.argmax(x, axis=3) output = tf.reshape(x ,[-1,self.h,self.w,self.c])  

Однако, реализуя это, я заметил, что результат первого изменения формы содержит какой-то перемежающий переход между каналами или пакетом или чем-то еще (изображение из ImageNet): До изменения формы, После изменения формы. Моя интуиция подсказывала, что это может быть связано с тем, что пакет и каналы не соседствуют в памяти.

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

 _, self.h, self.w, self.c = inputs.shape self.conv = tf.keras.layers.Conv2D(filters=2, kernel_size=3, strides=1, padding='same', data_format="channels_first") x_t = tf.transpose(inputs, [0,3,1,2]) # convert nhwc to nchw x_t = tf.reshape(x_t, [-1,1,self.h,self.w]) x_t = self.conv(x_t) x_t = tf.math.argmax(x_t, axis=1) x_t = tf.reshape(x_t ,[-1,self.c,self.h,self.w]) output = tf.transpose(x_t, [0,2,3,1])  

Это действительно, кажется, работает так, как я ожидал, но это довольно медленный подход. У меня есть ряд вопросов:

  1. Какова точная причина чередования, которое я испытываю?
  2. Был бы способ изменить мои данные без необходимости использования транспонирования? Я знаю, что, возможно, я мог бы везде использовать NCHW формат данных, но, поскольку я пытаюсь построить реализацию на существующей платформе, я думаю, что изменение формата данных нарушит другие части кода.
  3. Есть ли, возможно, совершенно другой подход, который я мог бы применить для свертки на канал? Я думал об использовании unstack или о чем-то подобном, но для этого потребовались бы циклы, что еще более неэффективно в моей идее.

Заранее спасибо

edit: I think I at least understand why the interleaving is happening. Let me try to explain by how I understand it, and format it as good as possible. The letters n,h,w,c here should help identifying to what a number belongs.

Suppose I have 16 numbers which are contiguous in memory:

 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16  

Если они имеют форму NHWC: 2,2,2,2

 [n[h[w[c1,c2],  w[c3,c4]],   h[w[c5,c6],  w[c7,c8]]],   n[h[w[c9,c10],  w[c11,c12]],   h[w[c13,c14],  w[c15,c16]]]]  

Затем, если они будут изменены NHWC: 4,2,2,1 и, сохраняя непрерывность базовых данных, мы получим:

 [n[h[w[c1],  w[c2]],   h[w[c3],  w[c4]]],   n[h[w[c5],  w[c6]],   h[w[c7],  w[c8]]],   n[h[w[c9],  w[c10]],   h[w[c11],  w[c12]]],   n[h[w[c13],  w[c14]],   h[w[c15],  w[c16]]]]  

Таким образом, каналы путаются в пространственных измерениях изображения.

Ответ №1:

Во-первых, изменение формы не работает должным образом, если вы хотите объединить не последовательные измерения, поэтому транспонирование-хорошая идея. Однако Conv2d ожидает, что последним измерением будут входные каналы. В вашем случае это self.w так, но, скорее всего, это то, что вы хотите. Вы можете изменить линию изменения формы на: tf.reshape(x_t, [-1, self.h, self.w, 1]) как и ранее.

Комментарии:

1. Действительно, я, кажется, понимаю, что происходит с точки зрения изменения формы. Я попытался написать это в правке своего поста. Вторая версия (с транспонированием) на самом деле имеет свертку dataformat="channels_first" для обработки каналов во втором измерении.

2. о, я ничего не понял, ладно