上一节介绍了卷积的另一特点——筛选数据特征。并通过神经网络的反向传播过程,我们能够对卷积核内的参数进行更新,得到从数据中提取到更优质特征的卷积核。
本节对卷积操作进行示例,并继续介绍池化操作。
在之前的卷积神经网络反向传播示例中,我们仅仅使用单通道的灰度图片作为输入,并使用 1 1 1个卷积核执行卷积运算。
但在 3 3 3通道的彩色图片中,每个通道均使用对应的卷积核执行卷积操作。也就是说:对于彩色图片,如果依然使用 3 × 3 3 \times 3 3×3大小的卷积核执行卷积运算,对于该图片的卷积核格式 ( Shape ) (\text{Shape}) (Shape)是: 3 × 3 × 3 3 \times 3 \times 3 3×3×3。
需要注意的是,虽然对每个通道分别进行卷积操作,但是
3 × 3 × 3 3 \times 3 \times 3 3×3×3卷积核矩阵是‘一个卷积核’。并且各分量之间仅起到一个‘并行’作用。仅仅是用矩阵合在一起。
在卷积神经网络的卷积层中,针对一个图像数据,不一定仅使用 1 1 1个卷积核来执行卷积操作,而是通过构建若干个卷积核针对同一个数据进行卷积操作。这意味着:针对同一数据,可以得到不同卷积结果,其结果数量与卷积核数量相同。
这里使用 PyTorch \text{PyTorch} PyTorch中的二维卷积 Conv2d \text{Conv2d} Conv2d的执行过程示例。
为了简化运算,使用元素值均为 1 1 1的矩阵作为数据。其数据格式表示如下:
import torch
from torch import nn as nn
SampleInput = torch.ones(1,3,3,3)
这明显是一个 4 4 4维张量。各维度的物理意义表示为:
其卷积层设置如下:
Conv1 = nn.Conv2d(
in_channels=3,
out_channels=1,
kernel_size=(2,2)
)
其中:
in_channels=3
表示该卷积层对于样本数据的输入通道数为 3 3 3;out_channels=1
表示输出通道数,也就是针对每个输入数据,使用的卷积核数量;kernel_size=(2,2)
表示针对每个通道的卷积核的大小。Stride,Padding
等参数均使用默认值。我们观察卷积核的权重信息以及它的返回结果格式:
print(Conv1.state_dict()["weight"].shape)
print(Conv1(SampleInput).shape)
返回结果如下:
torch.Size([1, 3, 2, 2])
torch.Size([1, 1, 2, 2])
其中权重 ( Weight ) (\text{Weight}) (Weight)的格式可看作是: 1 1 1个 3 × 2 × 2 3 \times 2 \times 2 3×2×2大小的卷积核。而对应的输出结果格式表示为:针对 1 1 1个数据, 1 1 1个卷积核产生的大小为 2 × 2 2 \times 2 2×2的卷积结果。
同理,如果将卷积层中的out_channels
参数进行修改,可得到不同格式的权重和输出结果:
Conv1 = nn.Conv2d(
in_channels=3,
out_channels=10,
kernel_size=(2,2)
)
torch.Size([10, 3, 2, 2])
torch.Size([1, 10, 2, 2])
观察它的计算过程:
print(SampleInput)
print("------" * 10)
print(Conv1.state_dict()["weight"].shape)
print(Conv1.state_dict()["weight"])
print(Conv1.state_dict()["bias"])
print(Conv1(SampleInput).shape)
print(Conv1(SampleInput))
返回结果如下:
tensor([[[[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]],
[[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]],
[[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]]]])
------------------------------------------------------------
torch.Size([1, 3, 2, 2])
tensor([[[[-0.2332, 0.0908],
[ 0.0909, 0.1832]],
[[ 0.2570, 0.1007],
[-0.1385, -0.2769]],
[[-0.1610, -0.2174],
[ 0.1630, 0.2141]]]])
tensor([0.0693])
torch.Size([1, 1, 2, 2])
tensor([[[[0.1420, 0.1420],
[0.1420, 0.1420]]]], grad_fn=<ThnnConv2DBackward0>)
各项均相同,用
∗ ∗ ** ∗∗简写,注意要加偏置项。
最终和计算机的计算结果相同。
如果说卷积 ( Convolution ) (\text{Convolution}) (Convolution)操作是提取数据中信息特征的分布,那么池化就是对这个描述信息特征的分布进行降采样 ( DownSampling ) (\text{DownSampling}) (DownSampling)。而降采样的目的是:数据特征基本不变的条件下,消减数据特征的大小,使其在卷积神经网络的计算过程中(前馈计算/反向传播过程)减少参数的计算量。
这个计算量主要是指卷积核中参数更新的次数减少,但更新的特征却基本没有减少。因而池化层降低了模型的复杂度,从而实现防止过拟合 ( Overfitting ) (\text{Overfitting}) (Overfitting)的效果。
这里说的‘参数的更新次数’是指卷积神经网络反向传播过程中,卷积核内权重对各输出特征梯度的累加数量。关于卷积神经网络反向传播见
传送门可以将池化理解成数据特征的'收缩'。
具体的池化层主要包含两种——平均池化 ( Average Pooling ) (\text{Average Pooling}) (Average Pooling)和最大池化 ( Max Pooling ) (\text{Max Pooling}) (Max Pooling)。
这里说的‘特征图像’可能是一个输入的灰度图像,也可能是某卷积层的输出结果,不影响。
就是对窗口内涵盖的特征结果取平均值。
观察池化的过程,如果将该过程看做采样的话,那么这个采样的目的是:用一个值来描述当前窗口信息的综合情况。而池化后的结果就是特征图像描述信息的缩影。
上例中‘特征图像’的左半部分与右半部分相比,左半部分的数值结果存在较大的数值;
并且左半部分数值‘比较活跃’,数值之间相差的更大;相反,右半数值相比之下更加稳定。
而这些特征在最大池化结果中,都有体现:
从代码角度观察池化操作,这里对某一个卷积结果进行池化操作:
没有涉及到激活函数。仅重点观察池化的具体效果。
其中池化操作中的
m , n m,n m,n表示池化窗口的宽、高。为了简化起见,其步长就是池化窗口的宽度。
import numpy as np
import cv2
import torch
from torch import nn as nn
Conv1 = nn.Conv2d(
in_channels=3,
out_channels=1,
kernel_size=(2,2))
def Pooling(data, m, n,mode):
assert mode in ["Max","Avg"]
a, b = data.shape
img_new = []
for i in range(0, a, m):
line = []
for j in range(0, b, n):
x = data[i:i + m, j:j + n]
if mode == "Max":
line.append(np.max(x))
else:
line.append(np.sum(x) / (n * m))
img_new.append(line)
return np.array(img_new)
这里选择
2 × 2 2 \times 2 2×2大小的池化窗口,对应步长为
2 2 2。PicPath = "C:/Users/Administrator/Desktop/PicSample.jpg"
img = cv2.imread(PicPath)
cv2.imshow("img",img)
imgInput = torch.transpose(torch.FloatTensor(img),0,2).unsqueeze(0)
ConvOut = Conv1(imgInput)
ConvOut = torch.transpose(ConvOut.squeeze(0),0,2).detach().numpy()
cv2.imshow("ConvOut",ConvOut)
PoolInput = np.squeeze(ConvOut)
# MaxPooling
img_MaxPool = Pooling(PoolInput, 2, 2,mode="Max")
# AvgPooling
img_AvgPool = Pooling(PoolInput, 2, 2,mode="Avg")
cv2.imshow("img_MaxPool",img_MaxPool)
cv2.imshow("img_AvgPool",img_AvgPool)
cv2.waitKey(0)
对应原始数据,卷积结果,最大/平均池化结果分别表示如下:
这里卷积过程中,卷积核是随机产生的,如果效果不好,请多试几次~
从图像的角度观察,卷积结果的特征信息基本没变化,但是图像大小(像素点数量)缩减为原来的
1 4 \begin{aligned}\frac{1}{4}\end{aligned} 41.平移不变性的基本描述是:当输入做少量平移时,池化能够帮助输入的表示近似不变。
引自《深度学习》(花书) P207 9.3 池化
首先,卷积神经网络对于位置是非常敏感的。如果样本输入发生一些平移,也就是说,如果说同一个事物,基于它产生的两个图像之间仅仅差若干个像素,对于神经网络的学习结果都会产生巨大的变化。就像这样:
从人的角度观察,虽然存在少许偏差,但它依然是同一个事物;但在卷积神经网络的角度,可能会对图片中事物的判别发生错误。
关于平移不变性的示例如下:
这意味着,第一个位置由新的元素替代,该结果的最后一个元素被‘踢掉’。
这意味着:该输出结果随着左移产生新元素的加入,所有元素的位置均发生了变化。如果仅看局部位置,针对局部位置执行池化窗口为 3 3 3,步长为 1 1 1的最大池化操作,两个输出结果对应的池化结果分别是:
{ ( 0.1 , 1.0 , 0.2 , 0.1 , ⋯ ) ⟹ Pool ( 1.0 , 1.0 , ⋯ ) ( 0.3 , 0.1 , 1.0 , 0.2 , 0.1 ⋯ ) ⟹ Pool ( 1.0 , 1.0 , 1.0 , ⋯ ) \begin{aligned} \begin{cases} (0.1,1.0,0.2,0.1,\cdots) \overset{\text{Pool}}{\Longrightarrow} (1.0,1.0,\cdots) \\ (0.3,0.1,1.0,0.2,0.1\cdots) \overset{\text{Pool}}{\Longrightarrow} (1.0,1.0,1.0,\cdots) \end{cases} \end{aligned} ⎩ ⎨ ⎧(0.1,1.0,0.2,0.1,⋯)⟹Pool(1.0,1.0,⋯)(0.3,0.1,1.0,0.2,0.1⋯)⟹Pool(1.0,1.0,1.0,⋯)
可以发现,局部位置池化后的结果相比之前的池化结果,相当于第一位增加了一个 1.0 1.0 1.0,最后一位可能消掉了一个结果,中间过程的所有结果均未发生变化。而这个增加的 1.0 1.0 1.0和其相邻的结果相同,都是这个局部区域的最大值 1.0 1.0 1.0。相当于被模糊掉了。
之所以会出现这种现象,是因为平移产生的像素 0.3 0.3 0.3没有影响这个局部区域最大值 1.0 1.0 1.0的地位,执行最大池化时,大概率会被同化掉。也就是说,最大池化单元仅仅对周围的最大值比较敏感。
如果将卷积层、激活函数、池化层看作是一个复杂层,我们可以将这个复杂层看作是基于权重有一个无限强先验的全连接神经网络模型。为什么要这么说 ? ? ?
为了区别于前面,我们将‘卷积层’描述为‘由卷积级、探测级(激活函数)、池化级’构成的复杂层。详见《机器学习》(花书) P208 9.3 池化
基于卷积函数的基本性质,在每次迭代过程中,卷积级的参数部分只有卷积核大小的数量。这也是它和全连接神经网络的主要区别。
卷积级的输出结果中,每一个元素(像素点)均只由卷积核中的参数以及被卷积核覆盖的输入(像素点)决定。也就是说,每一次卷积级的计算,它的权重空间仅由卷积核大小数量的权重参数约束着。这意味着权重空间的约束性很强。
相反,全连接神经网络的权重空间相比之下更加广阔,因为每一个输入都存在一个权重与其相对应,全连接层每个神经元的输出均由所有输入对应的权重共同决定。也就是《深度学习》(花书) P211 9.3 池化中提到的‘局部连接关系’。
如果仅仅是卷积级内的局部连接关系,还不至于是无限强的先验,关键在池化级。因为池化级(依然以最大池化级为例)让探测级输出的特征结果强行地归属于其局部区域内的最大值。
在极大似然估计与最大后验概率估计中介绍了投掷硬币的例子。在投掷质地均匀硬币 10 10 10次, 7 7 7次正面朝上, 3 3 3次反面朝上。如果没有先验概率的干预,仅通过极大似然估计来判别正面向上的概率,其概率结果为:
ln P ( X ∣ θ ) = ln [ P 7 × ( 1 − P ) 3 ] = 7 ln P + 3 ln ( 1 − P ) ∂ ln P ( X ∣ θ ) ∂ P ≜ 0 ⇒ 7 P + 3 1 − P = 0 ⇒ P = 0.7 \begin{aligned} \ln \mathcal P(\mathcal X \mid \theta) & = \ln \left[\mathcal P^7 \times (1 - \mathcal P)^3\right] \\ & = 7 \ln \mathcal P + 3 \ln(1 - \mathcal P) \\ \frac{\partial \ln \mathcal P(\mathcal X \mid \theta)}{\partial \mathcal P} \triangleq 0 & \Rightarrow \frac{7}{\mathcal P} + \frac{3}{1 - \mathcal P} = 0 \\ & \Rightarrow \mathcal P = 0.7 \end{aligned} lnP(X∣θ)∂P∂lnP(X∣θ)≜0=ln[P7×(1−P)3]=7lnP+3ln(1−P)⇒P7+1−P3=0⇒P=0.7
但这个结果并不符合我们的认知:投掷质地均匀硬币,正面朝上的概率是 0.5 0.5 0.5,反面朝上的概率是 0.5 0.5 0.5。因而针对参数 θ \theta θ,追加了一个先验概率 P ( θ ) \mathcal P(\theta) P(θ):均值 μ = 0.5 \mu=0.5 μ=0.5,方差 σ = 0.1 \sigma=0.1 σ=0.1的高斯分布,使用最大后验概率估计求解概率 P \mathcal P P:
arg max θ P ( θ ∣ X ) ∝ arg max θ P ( X ∣ θ ) ⋅ P ( θ ) ⇒ ln [ P ( X ∣ θ ) ⋅ P ( θ ) ] = ln [ P 7 × ( 1 − P ) 3 ] + ln [ 1 0.1 × 2 π exp { − ( p − 0.5 ) 2 2 × 0.01 } ] ⇒ ∂ ln [ P ( X ∣ θ ) ⋅ P ( θ ) ] ∂ P ≜ 0 ⇒ 3 1 − P + 7 P − 100 × ( P − 1 2 ) = 0 \begin{aligned} \mathop{\arg\max}\limits_{\theta} \mathcal P(\theta \mid \mathcal X) & \propto \mathop{\arg\max}\limits_{\theta} \mathcal P(\mathcal X \mid \theta) \cdot \mathcal P(\theta) \\ \Rightarrow \ln [\mathcal P(\mathcal X \mid \theta) \cdot \mathcal P(\theta)] & = \ln \left[\mathcal P^7 \times (1- \mathcal P)^3\right] + \ln \left[\frac{1}{0.1 \times \sqrt{2 \pi}} \exp \left\{-\frac{(p - 0.5)^2}{2 \times 0.01}\right\}\right] \\ \Rightarrow \frac{\partial \ln \left[\mathcal P(\mathcal X \mid \theta) \cdot \mathcal P(\theta)\right]}{\partial \mathcal P} \triangleq 0 & \Rightarrow \frac{3}{1 - \mathcal P} + \frac{7}{\mathcal P} - 100 \times \left(\mathcal P - \frac{1}{2}\right) = 0 \end{aligned} θargmaxP(θ∣X)⇒ln[P(X∣θ)⋅P(θ)]⇒∂P∂ln[P(X∣θ)⋅P(θ)]≜0∝θargmaxP(X∣θ)⋅P(θ)=ln[P7×(1−P)3]+ln[0.1×2π1exp{−2×0.01(p−0.5)2}]⇒1−P3+P7−100×(P−21)=0
最终求解 P ≈ 0.558 \mathcal P \approx 0.558 P≈0.558。相比于未含先验的 0.7 0.7 0.7,它已经将该结果拉向了我们的认知范围内。这里关于最大池的思路同理,并且它比上述的先验更强——强行等于某个局部最大值,而不是一个概率分布范围。也就是说:该值以 1 1 1的概率等于其局部区域内的最大值。这就相当于使用一种无限强的先验,强行引导输出值等于某个具体结果。
由于池化层仅是一个收缩的操作,因而它无法从卷积神经网络中学习到特征信息。但它同样需要通过反向传播将信息传递给上层的探测级:
仅仅起到一个传递梯度的操作。
而这个梯度传递操作的核心是:池化前后梯度之和保持不变。
相关参考:
CNN卷积层的多输入\输出通道你搞清了吗?
池化层详细介绍
《深度学习》(花书) 9.3 池化