CNNでは、各新しいフィルターは各入力チャンネルに対して異なる重みを持っていますか、または各フィルターの同じ重みが入力チャンネル全体で使用されていますか?


28

私の理解では、畳み込みニューラルネットワークの畳み込み層には、input_channels、filter_height、filter_width、number_of_filtersの4つの次元があります。さらに、各新しいフィルターは、すべてのinput_channels(または前のレイヤーの機能/アクティベーションマップ)で複雑になるだけであると理解しています。

ただし、CS231の次の図は、チャネル全体で使用されている同じフィルターではなく、単一フィルターに適用されている各フィルター(赤)を示しています。これは、各チャンネルに個別のフィルターがあることを示しているようです(この場合、入力画像の3つのカラーチャンネルであると仮定していますが、すべての入力チャンネルに同じことが当てはまります)。

これは紛らわしいです-入力チャンネルごとに異なるユニークなフィルターがありますか?

ここに画像の説明を入力してください

ソース:http : //cs231n.github.io/convolutional-networks/

上記の画像は、O'reillyの"Fundamentals of Deep Learning"からの抜粋と矛盾しているようです。

「...フィルタは、単一の機能マップで動作するだけではありません。特定のレイヤーで生成された機能マップのボリューム全体で動作します...その結果、機能マップはボリューム上で動作できなければなりません。エリアだけでなく」

...また、これらの画像は以下を示しているというのが私の理解ですSAMEだけ(CS231グラフィック上記に示しているものと矛盾)すべての3つの入力チャネルを介して畳み込まれるフィルタは:

ここに画像の説明を入力してください

ここに画像の説明を入力してください


回答:


13

畳み込みニューラルネットワークでは、各入力チャネルに一意のフィルターがありますか、それともすべての入力チャネルで同じ新しいフィルターが使用されていますか?

前者。実際、各入力チャネル/出力チャネルの組み合わせに対して定義された個別のカーネルがあります。

通常、CNNアーキテクチャの場合、number_of_filtersパラメーターで記述されている単一のフィルターには、入力チャネルごとに1つの2Dカーネルがあります。input_channels * number_of_filters重みのセットがあり、それぞれが畳み込みカーネルを表します。したがって、各フィルターの入力チャネルごとに1セットの重みを示す図は正しいです。最初の図は、これらのカーネルを適用した結果が、それらを合計し、各出力チャネルにバイアスを追加することによって結合されることも明確に示しています。

これは、出力チャンネルごとに3Dコンボリューションを使用していると見なすことできます。たまたま入力と同じ深さを持っています。これは、2番目の図が示しているものであり、多くのライブラリが内部で行うことです。数学的には、これは同じ結果です(深さが正確に一致する場合)。ただし、レイヤータイプには通常「Conv2D」または類似のラベルが付いています。同様に、入力タイプがボクセルやビデオなどの本質的に3Dの場合、「Conv3D」レイヤーを使用できますが、内部的には4Dコンボリューションとして実装できます。


この説明をありがとう。各フィルターには、実際に異なる重みを持つ複数のinput_channelsバージョンがあるようです。この理解を確認する「公式」ソースはありますか?
ライアンチェイス

@RyanChase:はい、それは正しいです。:カラー画像が処理される方法とここに始まる-私はちょうどCNNsのアンドリュー・ウのコースであなたを指すことになり coursera.org/learn/convolutional-neural-networks/lecture/ctQZz/...
ニール・スレーター

そのソース(cs231n.github.io/convolutional-networks)で、フィルター(重みまたはkernesl)はボリューム(つまり3次元)であり、同じ3次元が入力の1つを持つことに注意してください。ボリューム。さらに、(少なくとも)現在そのソースで述べられているように、入力ボリュームへのフィルターの適用をよりよく視覚化するために、ボリュームは3次元にわたってスライスされています。一般に、「入力チャネルと出力チャネルの組み合わせごとに個別のカーネルが定義されている」とは思いません。正しい。
nbro

フィルター(またはカーネル)は学習する必要がある重みであることに注意してください(つまり、固定されていませんが、実際にはCNNのパラメーターです)。それらは最後に(つまり、フィルターのスライス)、3次元にわたって同じである可能性があります。
nbro

@nbro:はい、複数の2Dスライスにまたがる2Dコンボリューションを、チャネル数と同じカーネル深度を持つ単一の3Dコンボリューションとして実装できます。数学的には、これは私の説明と同じです。また、共有ウェイト(その多くはゼロ)を持つ切り捨てられた完全接続フィードフォワードネットワークとして表示することもできます。OPは2Dフィルターの配置について尋ねているため、この答えは2Dフィルターのビューに焦点を当てています。実際には、より大きな3Dカーネルに配置することもできますが、3D畳み込みが同等である「トリック」を使用して2Dカーネルとして適用されます。
ニール・スレーター

12

質問で使用した次の図は、何が起こっているかを非常に正確に説明しています。3Dフィルターの各要素(灰色の立方体)は、異なる値(3x3x3=27値)で構成されていることに注意してください。したがって、3つの異なるサイズの2Dフィルター3x3連結して、この1つのサイズの3Dフィルターを形成できます3x3x3

convnet2D

3x3x3画像からのRGBチャンクは、3Dフィルター(灰色で表示)によって要素ごとに乗算されます。この場合、フィルターには重みがあります。これらの重みが要素ごとに乗算されてから合計されると、1つの値が得られます。 3x3x3=27


では、各入力チャンネルに個別のフィルターがありますか?

はい、画像内の入力チャンネルの数と同じ数の2Dフィルターがあります。ただし、複数のチャネルを持つ入力行列の場合、3Dフィルターは1つしかない(上の画像に示すように)と考えると役立ちます。


では、なぜこれが2Dコンボリューションと呼ばれるのですか(フィルターが3Dで入力行列が3Dの場合)?

フィルタのストライドのみ(高さと幅の寸法に沿っているので、これは2次元畳み込みであるNOT深さ)、したがって、この畳み込みによって生成される出力は、また、2次元マトリクスです。フィルターの移動方向の数により、畳み込みの次元が決まります。

注: 複数の2Dフィルター(レイヤーごとに1つ)ではなく、単一の3Dフィルターを視覚化して理解を深めると、Resnet、InceptionV3などの高度なCNNアーキテクチャを簡単に理解できます。


これは良い説明ですが、より具体的には、私が理解しようとしている質問は、各入力チャンネルで動作するフィルターが同じ重みのコピーであるか、まったく異なる重みであるかです。これは実際には画像に表示されておらず、実際、画像の種類は、各チャンネルに同じ重みが適用されていることを示唆しています(同じ色なので)... @ニール・スレーターの答えによると、それぞれのように聞こえますフィルターには、実際に異なる重みをinput_channels持つバージョンが多数あります。これがあなたの理解でもある場合、これを確認する「公式」ソースはありますか?
ライアンチェイス

はい、確かに、それは私の理解でもあります。私にとっては、その灰色の立方体が27の異なる重量値で構成されていると考えようとしたときに明らかになりました。つまり、3つの異なる2Dフィルターがあり、各入力レイヤーに同じ2Dフィルターが適用されます。
モーシンブハリ

これを確認するための公式ソースは見つかりませんでした。ただし、この同じ概念に頭を悩まそうとしたときに、Tensorflowでダミーの入力と重みフィルターを作成し、出力を観察しました。私はそれに満足していました。公式の説明があれば。上記の回答を編集します。
モーシンブハリ

Tensorflowパスをたどる場合。ダミーCNNレイヤーに入力サンプルを表示した後、ウェイトフィルターを印刷できます。
モーシンブハリ

@Moshsin Bukhari私は間違いなくTensorFlow内のフィルターを探索しようとします。フィルターに含まれるものを調べる方法について、コードを共有してもらえますか?たとえば、ネットワークの各ステップでフィルターの値を印刷できますか?
ライアンチェイス

3

入力チャネルと出力チャネルおよび重みに関してそれぞれ畳み込みがどのように機能するかをさらに明確にすることを期待して、具体的な例を使用して上記の答えをフォローアップしています:

例は次のようになります(1畳み込み層まで):

  • 入力テンソルは9x9x5、つまり5つの入力チャネルです。 input_channels=5
  • フィルター/カーネルサイズは4x4で、ストライドは1です
  • 出力テンソルは6x6x56、つまり56個の出力チャネルです。 output_channels=56
  • パディングタイプは「有効」です(つまりパディングなし)

次のことに注意してください。

  • 入力には5つのチャネルがあるため、フィルターの次元は4x4x5になります。つまり、サイズ4x4の5つの個別の一意の2Dフィルターがあります(つまり、それぞれ16の重みがあります)。サイズ9x9x5の入力で畳み込むために、フィルターは3Dになり、サイズ4x4x5でなければなりません
  • したがって、各入力チャネルには、それぞれ16の異なる重みを持つ個別の2Dフィルターが存在します。つまり、2Dフィルターの数は入力チャンネルの数と一致します
  • 56の出力チャネルがあるため、サイズ4x4x5の56の3次元フィルターW0、W1、...、W55が必要です(CS231グラフィックでは、2つの出力を考慮して2つの3次元フィルターW0、W1があります)チャンネル)、サイズ5の3番目の次元は5つの入力チャンネルへのリンクを表します(CS231グラフィックの各3DフィルターW0、W1には3つの入力チャンネルに一致する3番目の次元3があります)
  • したがって、3Dフィルターの数は出力チャンネルの数と等しくなります

したがって、その畳み込み層には以下が含まれます。

56個の出力チャネルに対応するサイズ4x4x5(= 80個の異なる重み)の56個の3次元フィルタ。それぞれが5個の入力チャネルに一致する5番目の3次次元の値を持ちます。合計で

number_of_filters=input_channel*output_channels=5*56=280

サイズ4x4の2Dフィルター(つまり、合計280x16の異なる重み)。


0

2Dには制限のみがあります。どうして?

完全に接続されたレイヤーを想像してください。

それは非常に巨大で、各ニューロンはおそらく1000x1000x3の入力ニューロンに接続されます。しかし、近くのピクセルを処理することは理にかなっていることを知っているため、小さな2D近傍に制限するため、各ニューロンは2Dの3x3近傍ニューロンにのみ接続されます。私たちはチャンネルについてそのようなことを知らないので、すべてのチャンネルに接続します。

それでも、重みが多すぎます。ただし、変換が不変であるため、ある領域で適切に機能するフィルターは、別の領域で最も役立つ可能性があります。したがって、2D全体で同じウェイトのセットを使用します。繰り返しますが、チャンネル間にそのような変換不変性はないため、そのような制限はありません。


0

http://cs231n.github.io/convolutional-networks/の「ローカル接続」セクションとスライド7-18を参照してください

深さは前のレイヤーの深さによって固定されるため、フィルターの「受容フィールド」ハイパーパラメーターは高さと幅のみで定義されます。

「深度軸に沿った接続性の範囲は、常に入力ボリュームの深度に等しい」ことに注意してください-または-アクティベーションマップの深度(後のレイヤーの場合)。

直感的には、これは、画像チャネルデータが平面ではなくインターリーブされているという事実によるものでなければなりません。このように、フィルターの適用は、列ベクトルの乗算によって簡単に実現できます。

畳み込みネットワークはすべてのフィルターパラメーター(深さ次元を含む)を学習し、それらは合計で「h w input_layer_depth + 1(バイアス)」であることに注意してください。


0

修士論文の2.2.1章をお勧めします回答としてします。残りの回答に追加するには:

ケラスは何が起こるかを理解するあなたの友達です:

from keras.models import Sequential
from keras.layers import Conv2D

model = Sequential()
model.add(Conv2D(32, input_shape=(28, 28, 3),
          kernel_size=(5, 5),
          padding='same',
          use_bias=False))
model.add(Conv2D(17, (3, 3), padding='same', use_bias=False))
model.add(Conv2D(13, (3, 3), padding='same', use_bias=False))
model.add(Conv2D(7, (3, 3), padding='same', use_bias=False))
model.compile(loss='categorical_crossentropy', optimizer='adam')

print(model.summary())

与える

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 32)        2400      
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 28, 28, 17)        4896      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 28, 28, 13)        1989      
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 28, 28, 7)         819       
=================================================================
Total params: 10,104

オプションを定式化してください。何か他のことが当てはまる場合、それはパラメーターにとって何を意味しますか?

ヒント: 2400=32355

このアプローチは、畳み込みレイヤーだけでなく、他のレイヤータイプでも役立ちます。

また、他の数のパラメーターを持つ可能性のあるさまざまなソリューションを自由に実装できることにも注意してください。


0

次の2つの詳細を明確にするためです。

あなたが持っていると言う N 2D入力チャンネル N2D出力チャンネル。2Dの総数3×3 フィルターの重みは実際には N2。しかし、3D畳み込みはどのように影響を受けます。つまり、すべての入力チャンネルがすべての出力チャンネルに1つの2Dレイヤーを提供する場合、各出力チャンネルは最初にN 2Dレイヤー、それらはどのように組み合わされますか?

これは、私が見たほとんどすべての出版物で見直される傾向がありますが、重要な概念は N2 2D出力チャンネルは互いにインターリーブされて、 Nシャッフルカードデッキのような出力チャンネルは、合計される前に。これは、畳み込みのチャネルの次元(図には示されていません)に沿って、実際に完全に接続されたレイヤーがあることを認識したときに、すべて論理的です!すべての入力2Dチャンネルに、一意の3×3フィルター、単一の出力チャンネルへの2D出力レイヤーの寄与を生成します。結合されると、すべての出力層はすべての入力層の組み合わせになります×ユニークなフィルター。それはすべての貢献です。

これを自分に納得させる最も簡単な方法は、他のシナリオで何が起こるかを想像し、計算が退化することを確認することです-つまり、結果をインターリーブして再結合しなければ、異なる出力は実際には何もしません-彼らは'重みを組み合わせた単一の出力と同じ効果があります。


0

畳み込みがどのように計算されるかを理解しようとしている人のために、Pytorchの便利なコードスニペットを以下に示します。

batch_size = 1
height = 3 
width = 3
conv1_in_channels = 2
conv1_out_channels = 2
conv2_out_channels = 2
kernel_size = 2
# (N, C_in, H, W) is shape of all tensors. (batch_size, channels, height, width)
input = torch.Tensor(np.arange(0, batch_size*height*width*in_channels).reshape(batch_size, in_channels, height, width))
conv1 = nn.Conv2d(in_channels, conv1_out_channels, kernel_size, bias=False) # no bias to make calculations easier
# set the weights of the convolutions to make the convolutions easier to follow
nn.init.constant_(conv1.weight[0][0], 0.25)
nn.init.constant_(conv1.weight[0][1], 0.5)
nn.init.constant_(conv1.weight[1][0], 1) 
nn.init.constant_(conv1.weight[1][1], 2) 
out1 = conv1(input) # compute the convolution

conv2 = nn.Conv2d(conv1_out_channels, conv2_out_channels, kernel_size, bias=False)
nn.init.constant_(conv2.weight[0][0], 0.25)
nn.init.constant_(conv2.weight[0][1], 0.5)
nn.init.constant_(conv2.weight[1][0], 1) 
nn.init.constant_(conv2.weight[1][1], 2) 
out2 = conv2(out1) # compute the convolution

for tensor, name in zip([input, conv1.weight, out1, conv2.weight, out2], ['input', 'conv1', 'out1', 'conv2', 'out2']):
    print('{}: {}'.format(name, tensor))
    print('{} shape: {}'.format(name, tensor.shape))

これを実行すると、次の出力が得られます。

input: tensor([[[[ 0.,  1.,  2.],
          [ 3.,  4.,  5.],
          [ 6.,  7.,  8.]],

         [[ 9., 10., 11.],
          [12., 13., 14.],
          [15., 16., 17.]]]])
input shape: torch.Size([1, 2, 3, 3])
conv1: Parameter containing:
tensor([[[[0.2500, 0.2500],
          [0.2500, 0.2500]],

         [[0.5000, 0.5000],
          [0.5000, 0.5000]]],


        [[[1.0000, 1.0000],
          [1.0000, 1.0000]],

         [[2.0000, 2.0000],
          [2.0000, 2.0000]]]], requires_grad=True)
conv1 shape: torch.Size([2, 2, 2, 2])
out1: tensor([[[[ 24.,  27.],
          [ 33.,  36.]],

         [[ 96., 108.],
          [132., 144.]]]], grad_fn=<MkldnnConvolutionBackward>)
out1 shape: torch.Size([1, 2, 2, 2])
conv2: Parameter containing:
tensor([[[[0.2500, 0.2500],
          [0.2500, 0.2500]],

         [[0.5000, 0.5000],
          [0.5000, 0.5000]]],


        [[[1.0000, 1.0000],
          [1.0000, 1.0000]],

         [[2.0000, 2.0000],
          [2.0000, 2.0000]]]], requires_grad=True)
conv2 shape: torch.Size([2, 2, 2, 2])
out2: tensor([[[[ 270.]],

         [[1080.]]]], grad_fn=<MkldnnConvolutionBackward>)
out2 shape: torch.Size([1, 2, 1, 1])

畳み込みの各チャネルが以前のすべてのチャネル出力を合計する方法に注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.