畳み込みニューラルネットワークにおける1D、2D、および3D畳み込みの直感的な理解


回答:


414

C3Dの写真で説明したいです。

一言で言えば、たたみ込み方向出力形状が重要です!

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

↑↑↑↑↑ 1Dコンボリューション-基本 ↑↑↑↑↑

  • わずか1 -方向(時間軸)を算出CONVに
  • 入力= [W]、フィルター= [k]、出力= [W]
  • 例)入力= [1,1,1,1,1]、フィルター= [0.25,0.5,0.25]、出力= [1,1,1,1,1]
  • 出力形状は1D配列です
  • 例)グラフの平滑化

tf.nn.conv1dコードおもちゃの例

import tensorflow as tf
import numpy as np

sess = tf.Session()

ones_1d = np.ones(5)
weight_1d = np.ones(3)
strides_1d = 1

in_1d = tf.constant(ones_1d, dtype=tf.float32)
filter_1d = tf.constant(weight_1d, dtype=tf.float32)

in_width = int(in_1d.shape[0])
filter_width = int(filter_1d.shape[0])

input_1d   = tf.reshape(in_1d, [1, in_width, 1])
kernel_1d = tf.reshape(filter_1d, [filter_width, 1, 1])
output_1d = tf.squeeze(tf.nn.conv1d(input_1d, kernel_1d, strides_1d, padding='SAME'))
print sess.run(output_1d)

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

↑↑↑↑↑ 2Dコンボリューション-基本 ↑↑↑↑↑

  • コンバージョンを計算する2方向(x、y)
  • 出力形状は2D行列です
  • 入力= [W、H]、フィルター= [k、k]出力= [W、H]
  • 例)Sobel Egde Fllter

tf.nn.conv2d-おもちゃの例

ones_2d = np.ones((5,5))
weight_2d = np.ones((3,3))
strides_2d = [1, 1, 1, 1]

in_2d = tf.constant(ones_2d, dtype=tf.float32)
filter_2d = tf.constant(weight_2d, dtype=tf.float32)

in_width = int(in_2d.shape[0])
in_height = int(in_2d.shape[1])

filter_width = int(filter_2d.shape[0])
filter_height = int(filter_2d.shape[1])

input_2d   = tf.reshape(in_2d, [1, in_height, in_width, 1])
kernel_2d = tf.reshape(filter_2d, [filter_height, filter_width, 1, 1])

output_2d = tf.squeeze(tf.nn.conv2d(input_2d, kernel_2d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)

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

↑↑↑↑↑ 3Dコンボリューション-基本 ↑↑↑↑↑

  • 変換を計算する方向(x、y、z)
  • 出力形状は3Dボリュームです
  • 入力= [W、H、L ]、フィルター= [k、k、d ]出力= [W、H、M]
  • d <Lは重要です!ボリューム出力を行うため
  • 例)C3D

tf.nn.conv3d-おもちゃの例

ones_3d = np.ones((5,5,5))
weight_3d = np.ones((3,3,3))
strides_3d = [1, 1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
in_depth = int(in_3d.shape[2])

filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])
filter_depth = int(filter_3d.shape[2])

input_3d   = tf.reshape(in_3d, [1, in_depth, in_height, in_width, 1])
kernel_3d = tf.reshape(filter_3d, [filter_depth, filter_height, filter_width, 1, 1])

output_3d = tf.squeeze(tf.nn.conv3d(input_3d, kernel_3d, strides=strides_3d, padding='SAME'))
print sess.run(output_3d)

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

↑↑↑↑↑ 2Dコンボリューションと3D入力 -LeNet、VGG、...、↑↑↑↑↑

  • イベントハフ入力は3Dですex)224x224x3、112x112x32
  • 出力形状は3Dボリュームではなく、2Dマトリックスです
  • フィルターの深さ= Lは入力チャネル= Lと一致する必要があるため
  • 2-方向(x、y)でコンバージョンを計算します!3Dではない
  • 入力= [W、H、L ]、フィルター= [k、k、L ]出力= [W、H]
  • 出力形状は2D行列です
  • N個のフィルターをトレーニングしたい場合(Nはフィルターの数)
  • 次に、出力形状は(スタック2D)3D = 2D x Nマトリックスです。

conv2d-LeNet、VGG、... 1フィルター

in_channels = 32 # 3 for RGB, 32, 64, 128, ... 
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae with in_channels
weight_3d = np.ones((3,3,in_channels)) 
strides_2d = [1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])

filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])

input_3d   = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_3d = tf.reshape(filter_3d, [filter_height, filter_width, in_channels, 1])

output_2d = tf.squeeze(tf.nn.conv2d(input_3d, kernel_3d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)

conv2d-NフィルターのLeNet、VGG、...

in_channels = 32 # 3 for RGB, 32, 64, 128, ... 
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])

filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])

input_3d   = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])

#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)

ここに画像の説明を入力してください ↑↑↑↑↑ CNNのボーナス1x1コンバージョン -GoogLeNet、...、↑↑↑↑↑

  • 1x1 convは、これをsobelのような2D画像フィルターと考えると混乱します
  • CNNの1x1コンバージョンの場合、入力は上の図のように3D形状です。
  • 深さ方向のフィルタリングを計算します
  • 入力= [W、H、L]、フィルター= [1,1、L]出力= [W、H]
  • 出力スタック形状は3D = 2D x N行列です。

tf.nn.conv2d-特別なケース1x1コンバージョン

in_channels = 32 # 3 for RGB, 32, 64, 128, ... 
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((1,1,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])

filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])

input_3d   = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])

#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)

アニメーション(3D入力の2D変換)

ここに画像の説明を入力してください -オリジナルリンク:LINK
-著者:マーティンゴルナー
-ツイッター:@martin_gorner
-グーグル+:plus.google.com/+MartinGorne

ボーナス1Dコンボリューションと2D入力

ここに画像の説明を入力してください ↑↑↑↑↑ 1D入力による1Dコンボリューション ↑↑↑↑↑

ここに画像の説明を入力してください ↑↑↑↑↑ 1D Convolutions with 2D input ↑↑↑↑↑

  • イベントハフ入力は2Dですex)20x14
  • 出力形状は2Dではなく1D行列です
  • フィルターの高さ= Lは入力の高さ= Lと一致する必要があるため
  • 1方向(x)でコンバージョンを計算します!2Dではない
  • 入力= [W、L ]、フィルター= [k、L ]出力= [W]
  • 出力形状は1D行列です
  • N個のフィルターをトレーニングしたい場合(Nはフィルターの数)
  • 次に、出力形状は(スタック1D)2D = 1D x N行列です。

ボーナスC3D

in_channels = 32 # 3, 32, 64, 128, ... 
out_channels = 64 # 3, 32, 64, 128, ... 
ones_4d = np.ones((5,5,5,in_channels))
weight_5d = np.ones((3,3,3,in_channels,out_channels))
strides_3d = [1, 1, 1, 1, 1]

in_4d = tf.constant(ones_4d, dtype=tf.float32)
filter_5d = tf.constant(weight_5d, dtype=tf.float32)

in_width = int(in_4d.shape[0])
in_height = int(in_4d.shape[1])
in_depth = int(in_4d.shape[2])

filter_width = int(filter_5d.shape[0])
filter_height = int(filter_5d.shape[1])
filter_depth = int(filter_5d.shape[2])

input_4d   = tf.reshape(in_4d, [1, in_depth, in_height, in_width, in_channels])
kernel_5d = tf.reshape(filter_5d, [filter_depth, filter_height, filter_width, in_channels, out_channels])

output_4d = tf.nn.conv3d(input_4d, kernel_5d, strides=strides_3d, padding='SAME')
print sess.run(output_4d)

sess.close()

Tensorflowでの入力と出力

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

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

概要

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


18
説明におけるあなたの労力と明快さを考慮すると、8の賛成票は少なすぎます。
user3282777 2017

3
3D入力の2Dコンバージョンは良い感じです。1dコンバージョンと2d入力(マルチチャネルアレイなど)を含め、その違いを2d入力の2dコンバージョンと比較するように編集することをお勧めします。
SumNeuron 2017年

2
素晴らしい答え!
ベン

なぜ2d inの変換方向です。私は方向が行の1場合、行の場合であると主張する出典を見てきました1+stride。畳み込み自体はシフト不変なので、畳み込みの方向が重要なのはなぜですか?
Minh Triet

ご質問ありがとうございます。はい!畳み込み自体はシフト不変です。したがって、変換方向の計算は重要ではありません(2つの大きな行列の乗算で2d変換を計算できます。caffeフレームワークはすでに実行されています)が、理解するには、変換方向で説明する方が良いでしょう。2Dコンバージョンと3D入力は、方向がないと混乱するためです。^^
runhani

6

@runhaniからの回答に続いて、説明をもう少し明確にするためにいくつかの詳細を追加し、これをもう少し説明しようとします(もちろん、TF1とTF2の例を使用します)。

私が含む主な追加ビットの1つは、

  • アプリケーションの強調
  • の使用法 tf.Variable
  • 入力/カーネル/出力のより明確な説明1D / 2D / 3Dたたみ込み
  • ストライド/パディングの影響

1Dたたみ込み

以下は、TF 1とTF 2を使用して1D畳み込みを行う方法です。

具体的に言うと、私のデータには次のような形があります。

  • 1Dベクトル- [batch size, width, in channels](例1, 5, 1
  • カーネル- [width, in channels, out channels](例5, 1, 4
  • 出力- [batch size, width, out_channels](例1, 5, 4

TF1の例

import tensorflow as tf
import numpy as np

inp = tf.placeholder(shape=[None, 5, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')

with tf.Session() as sess:
  tf.global_variables_initializer().run()
  print(sess.run(out, feed_dict={inp: np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]])}))

TF2の例

import tensorflow as tf
import numpy as np

inp = np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]]).astype(np.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')
print(out)

TF2は必要ないので、TF2での作業ははるかに少なくなります。 Sessionvariable_initializerたとえば、ます。

これは実際にはどのように見えるでしょうか?

信号平滑化の例を使用して、これが何をしているのかを理解しましょう。左側にはオリジナルがあり、右側には3つの出力チャネルを持つConvolution 1Dの出力があります。

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

複数のチャネルとはどういう意味ですか?

複数のチャネルは、基本的に入力の複数の特徴表現です。この例では、3つの異なるフィルターによって取得された3つの表現があります。最初のチャネルは、均等に重み付けされた平滑化フィルターです。2番目は、境界よりもフィルターの中央に重みを付けるフィルターです。最後のフィルターは2番目のフィルターの反対を行います。これらの異なるフィルターがどのように異なる効果をもたらすかを見ることができます。

1D畳み込みのディープラーニングアプリケーション

1D畳み込みは、文の分類タスクに使用されています。

2D畳み込み

2Dたたみ込みにオフ。あなたが深層学習者であれば、2D畳み込みに遭遇していない可能性は…ほぼゼロです。これは、画像分類、オブジェクト検出などのCNNや、画像に関連するNLP問題(画像キャプションの生成など)で使用されます。

例を試してみましょう。ここに次のフィルターを備えたたたみ込みカーネルがあります。

  • エッジ検出カーネル(3x3ウィンドウ)
  • ぼかしカーネル(3x3ウィンドウ)
  • シャープカーネル(3x3ウィンドウ)

具体的に言うと、私のデータには次のような形があります。

  • 画像(白黒)- [batch_size, height, width, 1](例1, 340, 371, 1
  • カーネル(別名フィルター)- [height, width, in channels, out channels](例3, 3, 1, 3
  • 出力(機能マップとも呼ばれます)- [batch_size, height, width, out_channels](例1, 340, 371, 3

TF1の例、

import tensorflow as tf
import numpy as np
from PIL import Image

im = np.array(Image.open(<some image>).convert('L'))#/255.0

kernel_init = np.array(
    [
     [[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
     [[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
     [[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
     ])

inp = tf.placeholder(shape=[None, image_height, image_width, 1], dtype=tf.float32)
kernel = tf.Variable(kernel_init, dtype=tf.float32)
out = tf.nn.conv2d(inp, kernel, strides=[1,1,1,1], padding='SAME')

with tf.Session() as sess:
  tf.global_variables_initializer().run()
  res = sess.run(out, feed_dict={inp: np.expand_dims(np.expand_dims(im,0),-1)})

TF2の例

import tensorflow as tf
import numpy as np
from PIL import Image

im = np.array(Image.open(<some image>).convert('L'))#/255.0
x = np.expand_dims(np.expand_dims(im,0),-1)

kernel_init = np.array(
    [
     [[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
     [[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
     [[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
     ])

kernel = tf.Variable(kernel_init, dtype=tf.float32)

out = tf.nn.conv2d(x, kernel, strides=[1,1,1,1], padding='SAME')

これは実際の生活の中でどのように見えるでしょうか?

ここでは、上記のコードによって生成された出力を確認できます。最初の画像はオリジナルで、時計回りに進むと、1番目のフィルター、2番目のフィルター、3番目のフィルターの出力があります。 ここに画像の説明を入力してください

複数のチャネルとはどういう意味ですか?

2D畳み込みの場合、これらの複数のチャネルの意味を理解するのははるかに簡単です。あなたが顔認識をしているとしましょう。あなたは考えることができます(これは非常に非現実的な単純化ですが、ポイントを取得します)各フィルターは目、口、鼻などを表します。そのため、各機能マップは、その機能が指定した画像に存在するかどうかのバイナリ表現になります。顔認識モデルの場合、これらは非常に貴重な機能であることを強調する必要はないと思います。この記事の詳細情報。

これは、私が表現しようとしていることのイラストです。

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

2D畳み込みのディープラーニングアプリケーション

2D畳み込みは、ディープラーニングの分野で非常に普及しています。

CNN(たたみ込みニューラルネットワーク)は、ほとんどすべてのコンピュータービジョンタスク(画像分類、オブジェクト検出、ビデオ分類など)に2Dたたみ込み演算を使用します。

3D畳み込み

現在は、次元数が増えるにつれて、何が起こっているのかを説明することがますます難しくなっています。しかし、1Dおよび2Dのたたみ込みがどのように機能するかを十分に理解していれば、その理解を3Dたたみ込みに一般化するのは非常に簡単です。だからここに行く。

具体的に言うと、私のデータには次のような形があります。

  • 3Dデータ(LIDAR)- [batch size, height, width, depth, in channels](例1, 200, 200, 200, 1
  • カーネル-[height, width, depth, in channels, out channels](例5, 5, 5, 1, 3
  • 出力-[batch size, width, height, width, depth, out_channels](例1, 200, 200, 2000, 3

TF1の例

import tensorflow as tf
import numpy as np

tf.reset_default_graph()

inp = tf.placeholder(shape=[None, 200, 200, 200, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(inp, kernel, strides=[1,1,1,1,1], padding='SAME')

with tf.Session() as sess:
  tf.global_variables_initializer().run()
  res = sess.run(out, feed_dict={inp: np.random.normal(size=(1,200,200,200,1))})

TF2の例

import tensorflow as tf
import numpy as np

x = np.random.normal(size=(1,200,200,200,1))
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(x, kernel, strides=[1,1,1,1,1], padding='SAME') 

3D畳み込みのディープラーニングアプリケーション

3D畳み込みは、本質的に3次元であるLIDAR(Light Detection and Ranging)データを含む機械学習アプリケーションを開発するときに使用されてきました。

何...もっと専門用語?:ストライドとパディング

もうすぐ終わりです。お待ちください。ストライドとパディングとは何かを見てみましょう。それらについて考えれば、彼らは非常に直感的です。

廊下をまたぐと、少ないステップでより早く到着できます。しかし、それはまた、部屋を横切って歩いた場合よりも周囲の観察が少ないことを意味します。きれいな絵で理解を深めましょう!2Dたたみ込みによってこれらを理解しましょう。

ストライドを理解する

畳み込みストライド

tf.nn.conv2dたとえばを使用する場合は、4要素のベクトルとして設定する必要があります。これに脅かされる理由はありません。ストライドは次の順序で含まれています。

  • 2Dコンボリューション- [batch stride, height stride, width stride, channel stride]。ここでは、バッチストライドとチャネルストライドを1に設定しました(私は5年間ディープラーニングモデルを実装しており、1以外に設定する必要はありませんでした)。つまり、設定するストライドは2つだけです。

  • 3Dコンボリューション- [batch stride, height stride, width stride, depth stride, channel stride]。ここでは、高さ/幅/深さのストライドのみを心配しています。

パディングについて

これで、ストライドがどれほど小さくても(つまり1)、たたみ込み中に避けられない次元の減少が発生していることがわかります(たとえば、4ユニット幅の画像をたたみ込んだ後の幅は3です)。これは、特に深い畳み込みニューラルネットワークを構築する場合は望ましくありません。ここでパディングが役に立ちます。最も一般的に使用されるパディングタイプは2つあります。

  • SAME そして VALID

以下に違いを示します。

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

最後の言葉:あなたが非常に興味があるなら、あなたは疑問に思うかもしれません。自動寸法縮小全体に爆弾を投下し、さまざまなストライドがあることについて話しています。しかし、ストライドの最も良い点は、いつどこでどのように寸法を小さくするかを制御できることです。


2
  1. CNN 1D、2D、または3Dは、入力またはフィルターの次元ではなく、畳み込み方向を指します。

  2. 1チャネル入力の場合、CNN2DはCNN1Dに等しく、カーネル長=入力長です。(1コンバージョン方向)


2

要約すると、1D CNNでは、カーネルは1方向に移動します。1D CNNの入出力データは2次元です。主に時系列データで使用されます。

2D CNNでは、カーネルは2方向に移動します。2D CNNの入出力データは3次元です。主に画像データで使用されます。

3D CNNでは、カーネルは3方向に移動します。3D CNNの入出力データは4次元です。主に3D画像データ(MRI、CTスキャン)で使用されます。

詳細については、https//medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6をご覧ください。

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