Визуализации выходного слоя свертки в 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
на этом, вы должны получить сетку маленьких изображений.
ниже изображение того, что человек получает после выполнения всех шагов здесь.
в случае, если кто-то хотел бы "перейти" к 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, который является целым числом, иначе он не может "заполнить" полученное изображение правильно. Возможным решением может быть заполнение глубины входного тензора до глубины, которая принимается методом,но я не пробовал этого.