对卷积神经网络中一维、二维和三维卷积的直观理解

有没有人能用例子来清楚地解释一维、二维和三维卷积在卷积神经网络(深度学习)中的区别?

126899 次浏览

我想用 C3D的图片来解释。

简而言之,卷积方向卷积方向输出形状输出形状很重要!

enter image description here

↑↑↑↑↑ 一维卷积-基本↑↑↑↑↑

  • 只需 1方向(时间轴)来计算
  • Input = [ W ] ,filter = [ k ] ,output = [ W ]
  • Ex) input = [1,1,1,1,1] ,filter = [0.25,0.5,0.25] ,output = [1,1,1,1,1]
  • 输出形状为一维阵列
  • 例子)图的平滑

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)

enter image description here

↑↑↑↑↑ 二维卷积-基本↑↑↑↑↑

  • 2 -方向(x,y)计算 conv
  • 输出形状为 2D矩阵
  • Input = [ W,H ] ,filter = [ k,k ] output = [ W,H ]
  • 例子) Sobel Egde Flter

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)

enter image description here

↑↑↑↑↑ 三维卷积-基础 ↑↑↑↑↑

  • 3 -方向(x,y,z)计算 conv
  • 输出形状为 3D
  • Input = [ W,H,L] ,filter = [ k,k,D] output = [ W,H,M ]
  • D < L 很重要
  • 例子) C3D

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)

enter image description here

-LeNet,VGG,..

  • 即使输入是3D x)224x224x3,112x112x32
  • 输出形状不是 3D体积,而是 2D矩阵
  • 因为滤波深度 = L必须与输入通道 = L匹配
  • 2 -方向(x,y)来计算 conv! 而不是3D
  • Input = [ W,H,L] ,filter = [ k,k,L] output = [ W,H ]
  • 输出形状为 2D矩阵
  • 如果我们想训练 N 个过滤器(N 是过滤器的个数)
  • 然后输出形状为(叠加2D) 3D = 2D x N矩阵。

Conv2d-LeNet,VGG,... for 1 filter

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)

用于 N 过滤器的 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)

enter image description here -GoogLeNet,..

  • 当你认为这是像 Sobel 一样的2D 图像过滤器时,1x1 conv 是令人困惑的
  • 对于 CNN 中的1x1视频,输入的是如上图所示的3D 形状。
  • 计算深度滤波
  • Input = [ W,H,L ] ,filter = [1,1,L ] output = [ W,H ]
  • 输出叠加形状为 3D = 2D x N矩阵。

特殊情况1x1 conv

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)

动画(2D Conv 与3D 输入)

enter image description here

  • 原始链接: 链接
  • 作者: Martin Görner
  • 推特:@martin _ gorner
  • Google + : plus.Google.com/+martingorne

额外的1D 卷积与2D 输入

enter image description here ↑↑↑↑↑ 具有一维输入的一维卷积↑↑↑↑↑

enter image description here ↑↑↑↑↑ 具有二维输入的一维卷积↑↑↑↑↑

  • 即使输入是2D x)20x14
  • 输出形状不是 2D,而是 1D矩阵
  • 因为滤波器高度 = L必须与输入高度 = L匹配
  • 1 -方向(x)计算 conv! 不是2D
  • Input = [ W,L] ,filter = [ k,L] output = [ 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 的投入与产出

enter image description here

enter image description here

摘要

enter image description here

  1. 细胞神经网络1D,2 D,或3D 指卷积方向,而不是输入或滤波维数。

  2. 对于1通道输入,CNN2D 等于 CNN1D 是内核长度 = 输入长度

在@runhani 的回答之后,我将添加一些更多的细节,以使解释更加清晰一些,并将尝试更多地解释这一点(当然还有来自 TF1和 TF2的例子)。

其中一个主要的附加部分是,

  • 重点是应用
  • tf.Variable的用法
  • 更清晰地解释输入/内核/输出1D/2D/3D 卷积
  • 跨步/填充的效果

一维卷积

下面是如何使用 TF 1和 TF 2进行一维卷积。

具体来说,我的数据有如下形状,

  • 一维向量 -[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

这在现实生活中会是什么样子呢?

让我们用一个信号平滑的例子来理解它的作用。左边是原版,右边是卷积1D 的输出,它有3个输出通道。

enter image description here

多频道是什么意思?

多通道基本上是一个输入的多个特征表示。在这个示例中,您有三个由三个不同的过滤器获得的表示。第一个通道是等权平滑滤波器。第二个是一个过滤器,它对过滤器中部的权重大于边界。最后一个过滤器的作用与第二个相反。所以你可以看到这些不同的过滤器如何产生不同的效果。

一维卷积的深度学习应用

一维卷积已成功地应用于 句子分类任务。

2D 卷积

开始2D 卷积。如果你是一个有深度学习能力的人,你没有遇到2D 卷积的几率是... ... 几乎为零。它被用于有线电视新闻网络的图像分类、目标检测等,以及涉及图像的自然语言处理问题(例如图像标题生成)。

举个例子,我得到了一个卷积内核它有以下过滤器,

  • 边缘检测内核(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')

这在现实生活中会是什么样子呢?

在这里,您可以看到由上面的代码产生的输出。第一个图像是原始的和去时钟方向你有输出的第一个滤波器,第二个滤波器和3个滤波器。 enter image description here

多频道是什么意思?

在二维卷积的背景下,更容易理解这些多通道的含义。假设你正在进行面部识别。你可以想到(这是一个非常不切实际的简化,但得到的观点)每个过滤器代表一个眼睛,嘴,鼻子等。因此,每个特征映射将是一个二进制表示,这个特征是否存在于您提供的图像中。我不认为我需要强调,对于一个人脸识别模型,这些都是非常有价值的特征。更多信息在这个 文章

这就是我想表达的意思。

enter image description here

二维卷积的深度学习应用

二维卷积在深度学习领域非常普遍。

Cnn (卷积神经网络)对几乎所有的计算机视觉任务(例如图像分类,目标检测,视频分类)都使用二维卷积操作。

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')

三维卷积的深度学习应用

三维卷积已被用于开发机器学习应用程序涉及 LIDAR (光探测和测距)数据的三维性质。

什么... 更多的术语? : 跨步和填充

好了,你快到了。等等。让我们看看什么是跨步和填充。如果你仔细想想,你会发现它们非常直观。

如果你大步走过一条走廊,你会用更少的步子更快地到达那里。但这也意味着你观察到的周围环境比你走过房间时要少。现在让我们用一张漂亮的图片来加强我们的理解!让我们通过二维卷积来理解这些。

理解跨步

Convolution stride

例如,当您使用 tf.nn.conv2d时,您需要将其设置为4个元素的向量。没必要被这个吓到。它只是按照以下顺序包含步长。

  • 二维卷积 -[batch stride, height stride, width stride, channel stride]。在这里,批量跨步和通道跨步你只设置为一个(我已经实施了5年的深度学习模型,从来没有设置他们以外的任何东西)。所以你只剩下两步了。

  • 3D 卷积 -[batch stride, height stride, width stride, depth stride, channel stride]。在这里你只需要考虑高度/宽度/深度的跨步。

理解填充物

现在,你注意到无论你的步幅有多小(例如1) ,在卷积过程中都会不可避免地出现维度减化(例如,在卷积4单位宽的图像后,宽度是3)。这是不可取的,特别是当建立深卷积神经网络。这就是填充物救援的地方。有两种最常用的填充类型。

  • SAMEVALID

下面你可以看到区别。

enter image description here

最后一个词 : 如果你非常好奇,你可能会想知道。我们刚刚在自动维度减化上扔了颗炸弹现在又在讨论不同的步伐。但是关于跨步最好的事情是你可以控制何时何地以及如何缩小尺寸。

总之,在1D CNN 中,内核朝着一个方向移动。一维 CNN 的输入输出数据是二维的。主要用于时间序列数据。

在2D CNN 中,内核向两个方向移动。二维 CNN 的输入输出数据是三维的。主要用于图像数据。

在3D CNN 中,内核向3个方向移动。三维 CNN 的输入输出数据是四维的。主要用于三维图像数据(MRI,CT 扫描)。

你可在此找到更多资料: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6