web-dev-qa-db-ja.com

入力に関するCaffe変換フィルターの勾配を見つける

フィルターを視覚化する の方法として、畳み込みニューラルネットワーク(CNN)の単一の畳み込みフィルターの入力レイヤーに関する勾配を見つける必要があります。
Python Caffe のインターフェース この例 のインターフェースなど)で訓練されたネットワークがある場合、どうすれば入力レイヤーのデータに関する変換フィルターの勾配を見つけますか?

編集:

answer by cesans に基づいて、以下のコードを追加しました。入力レイヤーのサイズは_[8, 8, 7, 96]_です。私の最初の変換レイヤーである_conv1_には、サイズが_1x5_の11個のフィルターがあり、その結果、次元は_[8, 11, 7, 92]_になります。

_net = solver.net
diffs = net.backward(diffs=['data', 'conv1'])
print diffs.keys() # >> ['conv1', 'data']
print diffs['data'].shape # >> (8, 8, 7, 96)
print diffs['conv1'].shape # >> (8, 11, 7, 92)
_

出力からわかるように、net.backward()によって返される配列の次元は、Caffeのレイヤーの次元と同じです。いくつかのテストを行った結果、この出力はdataレイヤーと_conv1_レイヤーのそれぞれに関する損失の勾配であることがわかりました。

ただし、私の質問は、入力レイヤーのデータに関する単一のconvフィルターの勾配をどのように見つけるかでした。これは別のことです。どうすればこれを達成できますか?

36
pir

Caffe netは、2つの「ストリーム」の数を操作します。
1つ目はデータ「ストリーム」です。つまり、画像とラベルがネットを介してプッシュされます。これらの入力がネットを介して進行すると、それらは高レベルの表現に変換され、最終的に(分類タスクで)クラス確率ベクトルに変換されます。
2番目の「ストリーム」は、さまざまなレイヤーのパラメーター、畳み込みの重み、バイアスなどを保持します。これらの数/重みは、ネットのトレーニングフェーズ中に変更および学習されます。

これら2つの「ストリーム」が果たす根本的に異なる役割にもかかわらず、カフェはそれらを格納および管理するために同じデータ構造blobを使用します。
ただし、各レイヤーには2つのdifferent blobsベクトルがあり、各ストリームに1つあります。

これが私が明確にしたいと思う例です:

import caffe
solver = caffe.SGDSolver( PATH_TO_SOLVER_PROTOTXT )
net = solver.net

今見れば

net.blobs

ネットの各レイヤーの「カフェブロブ」オブジェクトを格納する辞書が表示されます。各blobには、データと勾配の両方を格納するスペースがあります

net.blobs['data'].data.shape    # >> (32, 3, 224, 224)
net.blobs['data'].diff.shape    # >> (32, 3, 224, 224)

そして、畳み込み層の場合:

net.blobs['conv1/7x7_s2'].data.shape    # >> (32, 64, 112, 112)
net.blobs['conv1/7x7_s2'].diff.shape    # >> (32, 64, 112, 112)

net.blobsは最初のデータストリームを保持します。その形状は、結果のクラス確率ベクトルまでの入力画像の形状と一致します。

一方、netの別のメンバーを見ることができます

net.layers

これは、さまざまなレイヤーのパラメーターを格納するカフェベクトルです。
最初のレイヤー('data'レイヤー)を見る:

len(net.layers[0].blobs)    # >> 0

入力レイヤーに保存するパラメーターはありません。
一方、最初の畳み込み層

len(net.layers[1].blobs)    # >> 2

ネットは、フィルターの重み用に1つのblobを保存し、一定のバイアス用に別のblobを保存します。はい、どうぞ

net.layers[1].blobs[0].data.shape  # >> (64, 3, 7, 7)
net.layers[1].blobs[1].data.shape  # >> (64,)

ご覧のとおり、このレイヤーは3チャネルの入力画像に対して7x7の畳み込みを実行し、64個のフィルターがあります。

では、グラデーションを取得する方法は?まあ、あなたが指摘したように

diffs = net.backward(diffs=['data','conv1/7x7_s2'])

dataストリームの勾配を返します。これは

np.all( diffs['data'] == net.blobs['data'].diff )  # >> True
np.all( diffs['conv1/7x7_s2'] == net.blobs['conv1/7x7_s2'].diff )  # >> True

TL; DR)パラメータの勾配が必要な場合、これらはパラメータとともにnet.layersに格納されます。

net.layers[1].blobs[0].diff.shape # >> (64, 3, 7, 7)
net.layers[1].blobs[1].diff.shape # >> (64,)

レイヤーの名前とインデックスをnet.layersベクトルにマッピングするのに役立つように、net._layer_namesを使用できます。


更新フィルター応答を視覚化するための勾配の使用に関して:
通常、勾配はスカラー関数に対して定義されます。損失は​​スカラーであるため、スカラー損失に対するピクセル/フィルターの重みの勾配について説明できます。この勾配は、ピクセル/フィルターの重みごとに1つの数値です。
特定の内部の隠しノードの最大の活性化で生じる入力を取得したい場合、損失は特定の活性化の測定値である「補助」ネットが必要です視覚化する非表示ノード。この補助ネットを取得したら、任意の入力から開始して、入力層への補助損失の勾配に基づいてこの入力を変更できます。

update = prev_in + lr * net.blobs['data'].diff
29
Shai

backward()パスを実行すると、任意のレイヤーでグラデーションを取得できます。関数を呼び出すときにレイヤーのリストを指定するだけです。データレイヤーでグラデーションを表示するには:

net.forward()
diffs = net.backward(diffs=['data', 'conv1'])`
data_point = 16
plt.imshow(diffs['data'][data_point].squeeze())

すべてのレイヤーを強制的に逆方向に実行したい場合があります。force_backwardモデルのパラメータ。

https://github.com/BVLC/caffe/blob/master/src/caffe/proto/caffe.proto

10
cesans