学习参考:
- 动手学深度学习2.0
- Deep-Learning-with-TensorFlow-book
- pytorchlightning
①如有冒犯、请联系侵删。
②已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。
③非常推荐上面(学习参考)的前两个教程,在网上是开源免费的,写的很棒,不管是开始学还是复习巩固都很不错的。
深度学习回顾,专栏内容来源多个书籍笔记、在线笔记、以及自己的感想、想法,佛系更新。争取内容全面而不失重点。完结时间到了也会一直更新下去,已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。所有文章涉及的教程都会写在开头、一起学习一起进步。
1.填充 (padding)和 步幅(步长)(stride)
填充(Padding):
- 填充是指在输入特征图周围添加额外的像素值(通常是0),以扩大特征图的尺寸。填充可以用来控制卷积层输出特征图的空间尺寸,避免信息丢失,同时有助于处理边界信息。
- 填充可以是"valid"填充(即无填充)或"same"填充(使得输出特征图尺寸与输入特征图尺寸相同)。"valid"填充表示不使用填充,输出尺寸会减小;"same"填充表示使用足够的填充使得输出尺寸与输入尺寸相同。
步幅(Stride):
- 步幅是指卷积核在对输入特征图进行卷积操作时的移动步长。步幅控制了卷积核每次滑动的距离,影响了输出特征图的尺寸。
- 大步幅会导致输出特征图尺寸减小,而小步幅会导致输出特征图尺寸增大。
- 通常情况下,步幅为1是比较常见的选择,可以保留更多空间信息。但在某些情况下,可以使用大步幅来减小特征图尺寸,提高计算效率。
填充和步幅的组合方式会影响卷积层的输出尺寸。通过调整填充和步幅的数值,可以控制卷积神经网络在不同层面提取特征的方式,从而更好地适应特定的任务和数据。填充和步幅的选择也与输入图像尺寸、卷积核大小等因素有关,需要根据具体情况进行调整。
假设以下情景: 有时,在应用了连续的卷积之后,最终得到的输出远小于输入大小。这是由于卷积核的宽度和高度通常大于 1 所导致的。比如,一个 240×240 像素的图像,经过 10 层 5×5 的卷积后,将减少到 200×200 像素。如此一来,原始图像的边界丢失了许多有用信息。而填充是解决此问题最有效的方法;
有时,可能希望大幅降低图像的宽度和高度。例如,如果发现原始的输入分辨率十分冗余。步幅则可以在这类情况下提供帮助。
1.1 填充
在应用多层卷积时,常常丢失边缘像素。 由于通常使用小卷积核,因此对于任何单个卷积,可能只会丢失几个像素。 但随着应用许多连续卷积层,累积丢失的像素数就多了。 解决这个问题的简单方法即为填充(padding):在输入图像的边界填充元素(通常填充元素是 0 )。
将 3×3输入填充到 5×5 ,那么它的输出就增加为 4×4 。阴影部分是第一个输出元素以及用于输出计算的输入和核张量元素: 0×0+0×1+0×2+0×3=0 。
卷积神经网络中卷积核的高度和宽度通常为奇数,例如1、3、5或7。 选择奇数的好处是,保持空间维度的同时,可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。
此外,使用奇数的核大小和填充大小也提供了书写上的便利。对于任何二维张量X,当满足:
- 卷积核的大小是奇数;
- 所有边的填充行数和列数相同;
- 输出与输入具有相同高度和宽度
则可以得出:输出Y[i, j]是通过以输入X[i, j]为中心,与卷积核进行互相关计算得到的。
例子:创建一个高度和宽度为3的二维卷积层,并(在所有侧边填充1个像素)。给定高度和宽度为8的输入,则输出的高度和宽度也是8。
import tensorflow as tf
# 为了方便起见,我们定义了一个计算卷积层的函数。
# 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数
def comp_conv2d(conv2d, X):
# 这里的(1,1)表示批量大小和通道数都是1
X = tf.reshape(X, (1, ) + X.shape + (1, ))
Y = conv2d(X)
# 省略前两个维度:批量大小和通道
return tf.reshape(Y, Y.shape[1:3])
# 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列
conv2d = tf.keras.layers.Conv2D(1, kernel_size=3, padding='same')
X = tf.random.uniform(shape=(8, 8))
comp_conv2d(conv2d, X).shape
TensorShape([8, 8])
例子:当卷积核的高度和宽度不同时,可以填充不同的高度和宽度,使输出和输入具有相同的高度和宽度。在如下示例中,使用高度为5,宽度为3的卷积核,高度和宽度两边的填充自动分别为2和1。
conv2d = tf.keras.layers.Conv2D(1, kernel_size=(5, 3), padding='same')
comp_conv2d(conv2d, X).shape
填充可以增加输出的高度和宽度。这常用来使输出与输入具有相同的高和宽。
1.2步幅
卷积窗口从输入张量的左上角开始,向下、向右滑动。 在上面的例子中,默认每次滑动一个元素。 但是,有时候为了高效计算或是缩减采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素。将每次滑动元素的数量称为步幅(stride)。
下图是垂直步幅为 3 ,水平步幅为 2 的二维互相关运算。 着色部分是输出元素以及用于输出计算的输入和内核张量元素: 0×0+0×1+1×2+2×3=8、0×0+6×1+0×2+0×3=6 。
解释:为了计算输出中第一列的第二个元素和第一行的第二个元素,卷积窗口分别向下滑动三行和向右滑动两列。但是,当卷积窗口继续向右滑动两列时,没有输出,因为输入元素无法填充窗口(除非添加另一列填充)。
通常,当垂直步幅为 𝑠ℎ 、水平步幅为 𝑠𝑤 时,输出形状为:
更进一步,如果输入的高度和宽度可以被垂直和水平步幅整除,则输出形状将为
将高度和宽度的步幅设置为2,从而将输入的高度和宽度减半:
- 1:代表该卷积层输出的特征图的数量为1,即只有一个输出通道。
- kernel_size=3:指定卷积核的大小为3x3,即卷积核的高和宽都是3。
- padding=‘same’:表示使用“same”填充方式,即在输入特征图的周围填充足够的0,使得卷积操作后输出特征图的尺寸与输入特征图的尺寸相同。
- strides=2:表示卷积核在水平和垂直方向上移动的步幅为2,即每次移动2个像素。
conv2d = tf.keras.layers.Conv2D(1, kernel_size=3, padding='same', strides=2)
comp_conv2d(conv2d, X).shape
复杂例子:
- 1:表示卷积核的数量,即输出的特征图的通道数量为1。
- kernel_size=(3, 5):表示卷积核的大小为3x5,即卷积核的高为3,宽为5。
- padding=‘valid’:表示使用“valid”填充方式,即不使用填充。
- strides=(3, 4):表示卷积操作时卷积核在输入特征图上沿高和宽两个方向分别移动3和4个像素。
conv2d = tf.keras.layers.Conv2D(1, kernel_size=(3,5), padding='valid',strides=(3, 4))
comp_conv2d(conv2d, X).shape
步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的 1/𝑛 ( 𝑛 是一个大于 1 的整数)。
2.通道
2.1单通道输入和单卷积核
灰度图片只有灰度值一个通道,单个卷积核𝑐{out}= 1的情况。以输入X为5 ×5 的矩阵,卷积核为3 × 3的矩阵为例,如下图 10.12 所示。与卷积核同大小的感受野(输入X上方的绿色方框)首先移动至输入X最左上方,选中输入X上3 × 3的感受野元素,与卷积核(图片中间3 × 3方框)对应元素相乘:
完成第一个感受野区域的特征提取后,感受野窗口向右移动一个步长单位(Strides,记为𝑠,默认为 1),选中图 10.13 中绿色方框中的 9 个感受野元素,按照同样的计算方法,与卷积核对应元素相乘累加,得到输出 10,写入第一行、第二列位置.
按照上述方法,每次感受野向右移动𝑠 = 个步长单位,若超出输入边界,则向下移动𝑠 = 1个步长单位,并回到行首,直到感受野移动至最右边、最下方位置。
总结基本如下:
2.2多通道输入和单卷积核
多通道输入的卷积层更为常见,比如彩色的图片包含了 R/G/B 三个通道,每个通道上面的像素值表示 R/G/B 色彩的强度。以 3 通道输入、单个卷积核为例,将单通道输入的卷积运算方法推广到多通道的情况。
在多通道输入的情况下,卷积核的通道数需要和输入X的通道数量相匹配,卷积核的第i个通道和X的第i个通道运算,得到第i个中间矩阵,此时可以视为单通道输入与单卷积核的情况,所有通道的中间矩阵对应元素再次相加,作为最终输出。
如下所示:每个通道上面的感受野窗口同步落在对应通道上面的最左边、最上方位置,每个通道上感受野区域元素与卷积核对应通道上面的矩阵相乘累加,分别得到三个通道上面的输出 7、-11、-1 的中间变量,这些中间变量相加得到输出-5,写入对应位置。
输入的每个通道处的感受野均与卷积核的对应 通道相乘累加,得到与通道数量相等的中间变量,这些中间变量全部相加即得到当前位置 的输出值。输入通道的通道数量决定了卷积核的通道数。一个卷积核只能得到一个输出矩 阵,无论输入X的通道数量。
一个卷积核只能完成某种逻辑的特征提取,当需要同时提取多种逻辑特征 时,可以通过增加多个卷积核来得到多种特征,提高神经网络的表达能力,这就是多通道 输入、多卷积核的情况。
2.3多通道输入和多卷积核
基本如下:
3.池化层(汇聚层)
池化层与卷积层一样,汇聚层也可以改变输出形状。和以前一样,可以通过填充和步幅以获得所需的输出形状。
通常当处理图像时,希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。
汇聚(pooling)层(池化层),它具有双重目的:降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性。
实现池化层的前向传播:
import tensorflow as tf
def pool2d(X, pool_size, mode='max'):
p_h, p_w = pool_size
Y = tf.Variable(tf.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w +1)))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
if mode == 'max':
Y[i, j].assign(tf.reduce_max(X[i: i + p_h, j: j + p_w]))
elif mode =='avg':
Y[i, j].assign(tf.reduce_mean(X[i: i + p_h, j: j + p_w]))
return Y
3.1最大池化和平均池化
与卷积层类似,汇聚层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口(有时称为汇聚窗口)遍历的每个位置计算一个输出。 然而,不同于卷积层中的输入与卷积核之间的互相关计算,汇聚层不包含参数。 相反,池运算是确定性的,我们通常计算汇聚窗口中所有元素的最大值或平均值。这些操作分别称为最大汇聚层(maximum pooling)和平均汇聚层(average pooling)。
在这两种情况下,与互相关运算符一样,汇聚窗口从输入张量的左上角开始,从左往右、从上往下的在输入张量内滑动。在汇聚窗口到达的每个位置,它计算该窗口中输入子张量的最大值或平均值。计算最大值或平均值是取决于使用了最大汇聚层还是平均汇聚层。
例如:输出张量的高度为 2 ,宽度为 2 。这四个元素为每个汇聚窗口中的最大值:
最大池化:
X = tf.constant([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
pool2d(X, (2, 2))
平均池化:
pool2d(X, (2, 2), 'avg')
3.2池化层的填充和步幅
池化层与卷积层一样,汇聚层也可以改变输出形状。和以前一样,可以通过填充和步幅以获得所需的输出形状。
X = tf.reshape(tf.range(16, dtype=tf.float32), (1, 4, 4, 1))
X
(1)默认情况下,(深度学习框架中的步幅与汇聚窗口的大小相同)。 因此,如果我们使用形状为(3, 3)的汇聚窗口,那么默认情况下,我们得到的步幅形状为(3, 3)。
pool2d = tf.keras.layers.MaxPool2D(pool_size=[3, 3])
pool2d(X)
(2)填充和步幅可以手动同时一次性设定:
paddings = tf.constant([[0, 0], [1,0], [1,0], [0,0]])
X_padded = tf.pad(X, paddings, "CONSTANT")
pool2d = tf.keras.layers.MaxPool2D(pool_size=[3, 3], padding='valid',strides=2)
pool2d(X_padded)
(3)可以设定一个任意大小的矩形汇聚窗口,并分别设定填充和步幅的高度和宽度。
paddings = tf.constant([[0, 0], [0, 0], [1, 1], [0, 0]])
X_padded = tf.pad(X, paddings, "CONSTANT")
pool2d = tf.keras.layers.MaxPool2D(pool_size=[2, 3], padding='valid',strides=(2, 3))
pool2d(X_padded)
3.3 池化层处理多通道
在处理多通道输入数据时,汇聚层(池化层)在每个输入通道上单独运算,而不是像卷积层一样在通道上对输入进行汇总。 这意味着汇聚层的输出通道数与输入通道数相同,注意通道数是必定相同的,但是输入shape和输出的shape一般不相同。
如下所示,汇聚后输出通道的数量仍然是2,与输入通道一致。
X = tf.concat([X, X + 1], 3)
paddings = tf.constant([[0, 0], [1,0], [1,0], [0,0]])
X_padded = tf.pad(X, paddings, "CONSTANT")
pool2d = tf.keras.layers.MaxPool2D(pool_size=[3, 3], padding='valid',
strides=2)
pool2d(X_padded)