Визуализации выходного слоя свертки в tensorflow

Я пытаюсь визуализировать вывод сверточного слоя в tensorflow с помощью функции tf.image_summary. Я уже успешно использую его в других случаях (e. г. визуализация входного изображения), но есть некоторые трудности с корректной перестройкой вывода. У меня есть следующий уровень conv:

img_size = 256
x_image = tf.reshape(x, [-1,img_size, img_size,1], "sketch_image")

W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

таким образом, выход h_conv1 имеет форму [-1, img_size, img_size, 32]. Просто используя tf.image_summary("first_conv", tf.reshape(h_conv1, [-1, img_size, img_size, 1])) не учитывает 32 разных ядра, поэтому я в основном разрезаю разные здесь представлены карты.

как я могу изменить их правильно? Или есть другая вспомогательная функция, которую я мог бы использовать для включения этого вывода в сводку?

4 ответов


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

Итак, если у вас есть тензор, это images x ix x iy x channels

>>> V = tf.Variable()
>>> print V.get_shape()

TensorShape([Dimension(-1), Dimension(256), Dimension(256), Dimension(32)])

Итак, в этом примере ix = 256, iy=256, channels=32

сначала срежьте 1 изображение и удалите image измерение

V = tf.slice(V,(0,0,0,0),(1,-1,-1,-1)) #V[0,...]
V = tf.reshape(V,(iy,ix,channels))

Далее добавьте пару пикселей с нулевым заполнением вокруг изображение

ix += 4
iy += 4
V = tf.image.resize_image_with_crop_or_pad(image, iy, ix)

затем измените форму так, чтобы вместо 32 каналов у вас было 4x8 каналов, давайте назовем их cy=4 и cx=8.

V = tf.reshape(V,(iy,ix,cy,cx)) 

теперь хитрая часть. tf кажется, возвращает результаты в C-порядке, по умолчанию numpy.

текущий порядок, если он сглажен, перечислит все каналы для первого пикселя (итерация по cx и cy), перед перечислением каналов второго пикселя (incrementing ix). Идя по рядам пиксели (ix) перед увеличением до следующей строки (iy).

мы хотим порядок, который будет выкладывать изображения в сетке. Таким образом, вы идете через ряд изображения (ix), прежде чем шагнуть вдоль ряда каналов (cx), когда вы нажмете конец строки каналов, вы перейдете к следующей строке изображения (iy) и когда вы бежите вне или строки в изображении вы инкремент к следующей строке каналов (cy). Итак:

V = tf.transpose(V,(2,0,3,1)) #cy,iy,cx,ix

лично я предпочитаю np.einsum для фантазии транспонирует, для удобочитаемости, но это не в tf пока.

newtensor = np.einsum('yxYX->YyXx',oldtensor)

во всяком случае, теперь, когда пиксели находятся в правильном порядке, мы можем безопасно сгладить его в 2d-тензор:

# image_summary needs 4d input
V = tf.reshape(V,(1,cy*iy,cx*ix,1))

попробовать tf.image_summary на этом, вы должны получить сетку маленьких изображений.

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

enter image description here


в случае, если кто-то хотел бы "перейти" к numpy и визуализировать "там" вот пример, как отобразить оба Weights и processing result. Все преобразования основаны на prev answer by mdaoust.

# to visualize 1st conv layer Weights
vv1 = sess.run(W_conv1)

# to visualize 1st conv layer output
vv2 = sess.run(h_conv1,feed_dict = {img_ph:x, keep_prob: 1.0})
vv2 = vv2[0,:,:,:]   # in case of bunch out - slice first img


def vis_conv(v,ix,iy,ch,cy,cx, p = 0) :
    v = np.reshape(v,(iy,ix,ch))
    ix += 2
    iy += 2
    npad = ((1,1), (1,1), (0,0))
    v = np.pad(v, pad_width=npad, mode='constant', constant_values=p)
    v = np.reshape(v,(iy,ix,cy,cx)) 
    v = np.transpose(v,(2,0,3,1)) #cy,iy,cx,ix
    v = np.reshape(v,(cy*iy,cx*ix))
    return v

# W_conv1 - weights
ix = 5  # data size
iy = 5
ch = 32   
cy = 4   # grid from channels:  32 = 4x8
cx = 8
v  = vis_conv(vv1,ix,iy,ch,cy,cx)
plt.figure(figsize = (8,8))
plt.imshow(v,cmap="Greys_r",interpolation='nearest')

#  h_conv1 - processed image
ix = 30  # data size
iy = 30
v  = vis_conv(vv2,ix,iy,ch,cy,cx)
plt.figure(figsize = (8,8))
plt.imshow(v,cmap="Greys_r",interpolation='nearest')

вы можете попытаться получить изображение активации сверточного слоя таким образом:

    h_conv1_features = tf.unpack(h_conv1, axis=3)
    h_conv1_imgs = tf.expand_dims(tf.concat(1, h_conv1_features_padded), -1)

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

Если вы хотите, чтобы они были проложены (в моем случае активации relu для прокладки с белой линией):

    h_conv1_features = tf.unpack(h_conv1, axis=3)
    h_conv1_max = tf.reduce_max(h_conv1)
    h_conv1_features_padded = map(lambda t: tf.pad(t-h_conv1_max, [[0,0],[0,1],[0,0]])+h_conv1_max, h_conv1_features)
    h_conv1_imgs = tf.expand_dims(tf.concat(1, h_conv1_features_padded), -1)

Я лично пытаюсь выложить каждый 2d-фильтр в одном изображении.

для этого -если я не очень ошибаюсь, так как я совершенно новичок в DL - я узнал, что может быть полезно использовать depth_to_space функция, так как она принимает тензор 4d

[batch, height, width, depth]

и производит выход формы

[batch, height*block_size, width*block_size, depth/(block_size*block_size)]

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