卷积层

CNN的最重要的构建层就是卷积层:第一卷积层的神经元不会连接到输入图像中的每个像素,而只于接受视野内的像素相连。反过来,第二卷积层的每个神经元仅连接到位于第一层中的一个小矩阵内的神经元。这种架构允许网络集中在第一个隐藏层的低阶特征中,然后在下一个隐藏层中将它们组装成比较高阶的特征。

之前的神经网络都具有由一长列神经元组成的层,必须将输入图像展平为一维,然后将其输入到神经网络中。在CNN中,每一层都以2D表示,这使得将神经元与其相应的输入进行匹配变得更加容易

位于给定层的第$i$行第$i$列的神经元连接到位于第i到$i+f_h-1$行、第$j$到$j+f_w-1$列的前一层中的神经元的输出,其中$f_h$和$f_w$是接受视野的高度和宽度。为了使层的高度和宽度与上一层相同,通常在输入周围添加零。这成为零填充。

可以通过隔出接受视野的方式来将较大的输入层连接到较小的层。这大大降低了模型的计算复杂度。从一个接受视野移动到另一个接受视野的距离称为步幅。位于上层第i行、第j列的神经元与位于第$i\times s_h$到$i\times s_h+f_h-1$行、第$j\times s_w$到$j\times s_w+f_w-1$列的上一层神经元输出连接,其中$s_h$和$s_w$是垂直步幅和水平步幅。

滤波器

神经元的权重可以表示为一副小图像,其大小相当于接受视野的大小。滤波器可以设置权重,来选择图像中的内容。例如,一个$7\times7$的滤波器,全为0,除了中间列(全为1))。使用这些权重的神经元将忽略接受视野中除中心垂直线以外的所有内容(因为所有输入都将乘以0,位于中心垂直线的输入除外)

堆叠多个特征图

到目前为止,每个卷积层的输出表示为2D层,但实际上卷积层具有多个滤波器并为每个滤波器输出一个特征图,因此可以更精确的以3D模式显示。它在每个特征图中每个像素有一个神经元,给定特征图中的所有神经元共享相同的参数(即相同的权重和偏置项)。不同的特征图中的神经元使用不同的参数。神经元的接受视野与之前描述的相同,但是它拓展到了所有先前层的特征。简而言之,卷积层将多个可训练滤波器同时应用于其输入,从而使其能够检测出输入中任何位置的多个特征。

特征图中所有神经元共享相同的参数,大大减少了模型中的参数数量。CNN一旦学会了在一个位置识别模式,就可以在其他任何位置识别模式。相反,常规DNN学会了在一个位置识别模式,它就只能在那个特定位置识别它。

输入图像也由多个子层组成:每个颜色一个子层。通常有三种:红色、绿色和蓝色(RGB)。灰度图像只有一个通道,但是某些图像可能具有更多通道,例如有额外光频率(例如红外)的卫星图像。

计算卷积层中神经元的输出

$$ z_{i,j,k}=b_k+\sum_{u=0}^{f_h-1}\sum_{v=0}^{f_w-1}\sum^{f_{n'}-1}_{k'=0}x_{i',j',k'}\cdot w_{u,v,k',k} $$

其中:

$$ i'=i\times s_h+u $$

$$ j'=j\times s_w+v $$

  • $Z_{i,j,k}$是位于卷积层(第$l$层)的特征图$k$中第$i$行$j$列中的神经元的输出
  • $s_h$和$s_w$是垂直步幅和水平步幅,$f_h$和$f_w$是接受视野的高度和宽度,$f_{n'}$是上一层(层$l-1$)中特征图的数量
  • $x_{i',j',k'}$是位于第$l-1$层、第$i'$行、第$j'$列,特征图$k'$(或通道$k'$,如果前一层是输入层)的神经元的输出
  • $b_k$是特征图$k$(在$l$层中)的偏置项。可以将其视为用于调整特征图$k$整体亮度的旋钮
  • $w_{u,v,k',k}$是层$l$的特征图$k$中的任何神经元与其位于$u$行、$v$列(相对于神经元的接受视野)和特征图$k$的输入之间的连接权重

TensorFlow实现

在TensorFlow中,每个输入图像通常表示为形状为[height, width, channels]的3D张量。小批量表示为形状为[min batch size, heigh, width, channels]的4D张量。卷积层的权重表示为形状为$[f_h,f_w,f_{n'},f_n]$的4D张量。卷积层的偏置项简单表示为形状$[f_n]$的一维张量。

下面使用Sickit-Learn的load_sample_image()加载两个样本图像(加载两个彩色图像:一个是中国寺庙,另一个是花朵),然后创建两个滤波器并将其应用于两个图像,最后显示其中一个结果特征图

from sklearn.datasets import load_sample_image
import tensorflow as tf

china = load_sample_image('china.jpg')/255
flower = load_sample_image('flower.jpg')/255
images = np.array([china, flower])
batch_size, height, width, channels = images.shape

filters = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)
filters[:, 3, :, 0] = 1
filters[3, :, :, 1] = 1

outputs = tf.nn.conv2d(images, filters, strides=1, padding='SAME')

plt.imshow(outputs[0, :, :, 1], cmap='gray')
plt.show
  • 每个颜色通道的像素强度表示从0到255的字节,因此我们除以255即可缩放这些特征,得到从0到1的浮点数
  • 创建两个$7\times7$的滤波器(一个在中间带有垂直白线,另一个在中间带有水平白线)
  • 使用tf.nn.conv2d()函数应用于两个图像,这是TensorFlow的底层深度学习API的一部分。使用零填充(padding='SAME')和步幅为1
  • 最后,绘制一个结果特征图

tf.nn.conv2d():

  • images是输入小批量(4D张量)
  • filter是要应用的一组滤波器(4D张量)
  • strides等于1,但也可以是抱哈四个元素的一维度组,其中两个中间元素是垂直步幅和水平步幅($s_h$和$s_w$)。第一个元素和最后一个元素必须等于1。它们可能用于指定批处理步幅(跳过某些实例)和通道步幅(跳过某些上一层的特征图或通道)
  • padding必须设置为“SAME”或“VALID”

    • 如果设置为“SAME”,则卷积层在必要时使用零填充。将输出大小设置为输入神经元数量除以步幅(向上取整)所得1的值。然后根据需要在输入周围尽可能均匀地添加零。当strides=1时,层的输入将具有与其输入相同的空间尺寸(宽度和高度),因此命名为“same”
    • 如果设置为“VALID”,则卷积层将不使用零填充,并且可能会忽略输入图像底部和右侧的某些行和列,具体取决于步幅。这意味着每个神经元的接受视野都严格位于输入内部的有效位之内(不会超出范围),因此命名为“valid”

在此示例中,手动定义了滤波器,但是在实际的CNN中,通向将滤波器定义为可训练变量,以便神经网络可以了解哪种滤波器效果最好。不用手动创建变量,使用keras.layers.Conv2D层:

from tensorflow import keras
conv=keras.layers.Conv2D(filters=32,kernel_size=3,strides=1,padding='same',activation='relu')

这段代码使用步幅为1(水平和垂直)和“same”的填充,创建了一个包含32个滤波器(每个$3\times3$)的Conv2D层,并将ReLU激活函数应用于其输出

内存需求

CNN的另一个问题是卷积层需要大量的RAM。在训练期间尤其如此,因此反向传播需要在正向传播过程中计算出的所有中间值。

例如,考虑一个带$5\times5$滤波器的卷积层,输出200个大小为$150\times100$的特征图,步幅为1,填充为“same”。如果输出是$150\times100$RGB图像(三个通道),则参数的数量为$(5\times5\times3+1)\times200=15200$(+1对应偏置项),与全连接层相比它很小。但是200个特征图中的每个特征图都包含$150\times100$个神经元,并且每个神经元都需要计算其$5\times5\times3=75$个输入的加权和:总共有2.25亿($150\times100\times75\times200=2.25e8$)个浮点乘法。

如果使用32位浮点数来表示特征图,则卷积层的输出将占用$200\times150\times100\times32=96$百万位(12MB)的RAM。

在推理期间(即对新实例进行预测时),只要计算了下一层,就可以释放由前一层占用的RAM,因此只需要两个连续层所需的RAM。但是在训练过程中,需要保留正向传播过程中的所有内容以供反向传播,因此所需的RAM量至少是所有层所需的RAM总量

如果由于内存不足而导致训练失败,则可以尝试减少批量的大小。另外可以尝试使用步幅来减少维度或删除几层。也可以尝试使用16位浮点数而不是32位浮点数。或者在多个设备上分配CNN。

标签: none

添加新评论