web-dev-qa-db-ja.com

テンソルフローでの畳み込み層の出力の視覚化

関数_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の異なるカーネルを考慮しないため、ここでは基本的に異なる機能マップをスライスしています。

どうやって正しく形を変えることができますか?または、この出力を要約に含めるために使用できる別のヘルパー関数はありますか?

34
panmari

ヘルパー関数は知りませんが、すべてのフィルターを表示する場合は、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 = 256iy=256channels=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は、numpyのデフォルトであるCオーダーで結果を返すようです。

現在の順序は、フラット化されている場合、2番目のピクセルのチャネルをリストする前に(cxをインクリメントする)最初のピクセルのすべてのチャネルをリストします(cyおよびixを繰り返します)。次の行(ix)にインクリメントする前に、ピクセルの行(iy)を移動します。

画像をグリッドに配置する順序が必要です。そのため、チャンネルの行(ix)に沿って進む前に、画像の行(cx)を横切って、チャンネルの行の最後に到達すると、次の行に進みます。 image(iy)そして、イメージ内でなくなるか行になったら、チャネルの次の行に増分します(cy)。そう:

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

個人的にはnp.einsum読みやすくするための派手な転置用ですが、tfyet にはありません。

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

34
mdaoust

誰かがnumpyに「ジャンプ」して「そこ」を視覚化する場合は、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')
2
rgr

この方法で畳み込み層活性化画像を取得しようとする場合があります。

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

これにより、すべての画像が垂直に連結された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)
1
Anton Suslov

私は個人的に単一の画像ですべての2Dフィルターを並べようとしています。

これを行うために -DLが初めてなので、ひどく間違えていなければ- depth_to_space 関数を利用すると役立つ場合があることがわかりました。

_[batch, height, width, depth]_

そして、形状の出力を生成します

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

Block_sizeは、出力イメージ内の「タイル」の数です。これに対する唯一の制限は、深さがblock_sizeの2乗である必要があることです。これは整数です。そうでない場合、結果のイメージを正しく「埋めること」ができません。考えられる解決策は、入力テンソルの深さをメソッドで受け入れられる深さまでパディングすることですが、これはまだ試していません。

1
EdoardoG

私が非常に簡単だと思う別の方法は、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')

以下の例は、モデルの2番目のconvレイヤーの入力(色付きの猫)と出力です。 below

この質問は古く、Kerasには簡単な方法がありますが、他の人(私のような)の古いモデルを使用している人にとっては、これは役に立つかもしれません。

0
smttsp