可视化张量流中卷积层的输出

Visualizing output of convolutional layer in tensorflow

我正在尝试使用函数 tf.image_summary 可视化张量流中卷积层的输出。我已经在其他情况下成功使用它(例如可视化输入图像),但在正确重塑输出时遇到一些困难。我有以下转换层:

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 个不同的内核,所以我基本上在这里切入不同的特征图。

如何正确地重塑它们?或者我可以使用另一个辅助函数将此输出包含在摘要中吗?

我不知道辅助函数,但如果您想查看所有滤镜,您可以将它们打包到一张图像中,并使用 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)

然后重塑,使您拥有 4x8 通道,而不是 32 个通道,我们称它们为 cy=4cx=8.

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

现在是棘手的部分。 tf 似乎 return 结果是 C 顺序,numpy 的默认值。

如果展平,当前顺序将列出第一个像素的所有通道(遍历 cxcy),然后列出第二个像素的通道(递增 ix).在递增到下一行 (iy) 之前遍历像素行 (ix)。

我们想要在网格中布置图像的顺序。 因此,在沿着通道行 (cx) 步进之前,您浏览了一行图像 (ix),当您到达通道行的末尾时,您将步进到通道中的下一行图像 (iy),当你 运行 输出或图像中的行时,你会递增到下一行通道 (cy)。所以:

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

就我个人而言,我更喜欢 np.einsum 用于精美的转置,以提高可读性,但它不在 tf yet.

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

无论如何,现在像素的顺序正确了,我们可以安全地将其展平为二维张量:

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

试试 tf.image_summary,你应该得到一个小图像网格。

下图是完成此处所有步骤后得到的图像。

万一有人想 "jump" numpy 和可视化 "there" 这里有一个如何同时显示 Weightsprocessing result 的例子。所有转换均基于 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是输出图像中"tiles"的个数。唯一的限制是深度应该是 block_size 的平方,它是一个整数,否则无法 "fill" 正确生成图像。 一种可能的解决方案是将输入张量的深度填充到该方法可接受的深度,但我还没有尝试过。

另一种我认为很简单的方法是使用 get_operation_by_name 函数。我很难用其他方法可视化图层,但这对我有帮助。

#first, find out the operations, many of those are micro-operations such as add etc.
graph = tf.get_default_graph()
graph.get_operations()

#choose relevant operations
op_name = '...' 
op = graph.get_operation_by_name(op_name)
out = sess.run([op.outputs[0]], feed_dict={x: img_batch, is_training: False}) 
#img_batch is a single image whose dimensions are (1,n,n,1). 

# out is the output of the layer, do whatever you want with the output
#in my case, I wanted to see the output of a convolution layer
out2 = np.array(out)
print(out2.shape)

# determine, row, col, and fig size etc.
for each_depth in range(out2.shape[4]):
    fig.add_subplot(rows, cols, each_depth+1)
    plt.imshow(out2[0,0,:,:,each_depth], cmap='gray')

例如下面是我模型中第二个转换层的输入(彩色猫)和输出。

请注意,我知道这个问题很老,Keras 有更简单的方法,但对于使用其他人(例如我)的旧模型的人来说,这可能有用。