文章目录
十九、卷积层基础知识
1、全连接层
( f ∗ g ) ( t ) = ∫ − ∞ ∞ f ( τ ) g ( t − τ ) d τ (f \ast g)(t) = \int_{-\infty}^{\infty} f(\tau) g(t - \tau) \, d\tau (f∗g)(t)=∫−∞∞f(τ)g(t−τ)dτ
二维是w,b,四维是【Wi,Wj,Wk,Wl】
之前输入输出都是一维,权重就是二维
现在输入输出都是二维,权重就是四维
全连接层的权重W是个矩阵,Wij表示第i行第j列的值(标量),对他推广, Wij表示一个二维矩阵(引入宽高kl)就变成了4D
原则一:平移不变性

原则二 :局部性

全连接层(Fully Connected Layers)在神经网络中通常用于连接前一层的所有神经元到当前层的每一个神经元。然而,当处理图像数据时,全连接层可能会遇到两个问题:参数数量庞大和缺乏平移不变性(Translation Invariance)与局部性(Locality)。
平移不变性意味着无论图像中的目标对象移动到哪里,神经网络都应该能够识别它。局部性则表明神经网络在初始阶段应该只关注图像的局部区域,而不是整个图像。
-
平移不变性:
- 在全连接层中,每个神经元都与前一层的所有神经元相连,这导致网络对图像中的位置变化非常敏感。
- 卷积层通过使用共享的卷积核(也称为滤波器或特征检测器)在整个图像上滑动来解决这个问题。由于卷积核是共享的,并且以相同的方式应用于图像的不同部分,因此网络对图像中的位置变化具有不变性。
-
局部性:
- 在全连接层中,每个神经元都考虑图像中的所有像素,这导致大量的参数和计算复杂性。
- 卷积层通过限制每个神经元只查看图像的一个局部区域(即卷积核的大小)来实现局部性。这使得网络能够在初始阶段关注图像的局部特征,然后在后续层中将这些特征组合起来以形成更高级别的表示。

2、卷积层
下标 h w 分布代表高和宽

-
一维卷积:
一维卷积通常用于处理时间序列数据或一维信号。假设有两个一维向量f和h,其长度分别为n和k,则一维卷积的结果g是一个长度为n-k+1的向量。一维卷积的LaTeX表达式为:g [ i ] = ∑ j = 0 k − 1 f [ i − j ] ⋅ h [ j ] g[i] = \sum_{j=0}^{k-1} f[i-j] \cdot h[j] g[i]=j=0∑k−1f[i−j]⋅h[j]
注意:这里假设
i的范围从k-1到n-1。 -
二维卷积:
二维卷积在计算机视觉和图像处理中非常常见。假设有两个二维矩阵F和H(通常称为输入图像和卷积核),则二维卷积的结果G是一个新的二维矩阵。G [ i ] [ j ] = ∑ m = 0 M − 1 ∑ n = 0 N − 1 F [ i − m ] [ j − n ] ⋅ H [ m ] [ n ] G[i][j] = \sum_{m=0}^{M-1} \sum_{n=0}^{N-1} F[i-m][j-n] \cdot H[m][n] G[i][j]=m=0∑M−1n=0∑N−1F[i−m][j−n]⋅H[m][n]
其中,
M和N分别是卷积核H的高度和宽度。注意:这里假设i和j的范围考虑了边界条件(如填充或步长)。

-
三维卷积:
三维卷积在处理立体数据如医学图像中的CT扫描。假设有两个三维数组F和H,则三维卷积的结果G是一个新的三维数组。三维卷积的LaTeX表达式为:G [ i ] [ j ] [ k ] = ∑ m = 0 M − 1 ∑ n = 0 N − 1 ∑ p = 0 P − 1 F [ i − m ] [ j − n ] [ k − p ] ⋅ H [ m ] [ n ] [ p ] G[i][j][k] = \sum_{m=0}^{M-1} \sum_{n=0}^{N-1} \sum_{p=0}^{P-1} F[i-m][j-n][k-p] \cdot H[m][n][p] G[i][j][k]=m=0∑M−1n=0∑N−1p=0∑P−1F[i−m][j−n][k−p]⋅H[m][n][p]
其中,
M、N和P分别是卷积核H在三个维度上的大小。同样,这里假设i、j和k的范围考虑了边界条件。

3、代码实现
def corr2d(X, K): #@save
"""计算二维互相关运算"""
h, w = K.shape
Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
return Y
- 接受两个参数:
X和K,其中X通常是一个二维输入数据(例如图像),而K是一个较小的二维核(kernel)。函数的目标是计算X和K之间的二维互相关,并将结果存储在Y中。
- 获取核的大小:通过
K.shape获取核的高度h和宽度w。 - 初始化输出:使用
torch.zeros初始化一个全零的二维张量Y,其大小基于X和K的大小来确定。因为核K会在X上滑动,所以Y的高度和宽度会分别是X的高度和宽度减去核的高度和宽度再加1。 - 双重循环遍历:使用两个嵌套的
for循环遍历Y的所有位置。对于每个位置(i, j),从X中提取一个与K大小相同的子块,并计算这个子块与K的元素乘积之和。这个和就是Y在位置(i, j)的值。 - 返回结果:返回计算得到的二维互相关结果
Y。
Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
-
X[i:i + h, j:j + w]:这部分代码从X 中选取一个子区域。这个子区域的左上角坐标是(i, j),高度是h,宽度是w。因此,这个子区域包含了从(i, j)开始,高h宽w的所有像素或特征值。 -
X[i:i + h, j:j + w] * K:这里,X的子区域与K进行了逐元素相乘。这意味着X中每个位置(m, n)(其中m在i到i+h-1之间,n在j到j+w-1之间)的值与K中对应位置(m-i, n-j)的值相乘。 -
sum():最后,这个逐元素相乘的结果数组的所有元素被加起来,产生一个单一的输出值。这个值被存储在输出特征图Y的(i, j)位置。
卷积实现
class Conv2D(nn.Module):
def __init__(self, kernel_size):
super().__init__()
self.weight = nn.Parameter(torch.rand(kernel_size))
self.bias = nn.Parameter(torch.zeros(1))
def forward(self, x):
return corr2d(x, self.weight) + self.bias
学习卷积核实现
# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)
# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
# 其中批量大小和通道数都为1
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
lr = 3e-2 # 学习率
for i in range(10):
Y_hat = conv2d(X)
l = (Y_hat - Y) ** 2
conv2d.zero_grad()
l.sum().backward()
# 迭代卷积核
conv2d.weight.data[:] -= lr * conv2d.weight.grad
if (i + 1) % 2 == 0:
print(f'epoch {
i+1}, loss {
l.sum():.3f}')

实现简单的二维卷积层训练过程,通过迭代来更新卷积核的参数以最小化预测输出
Y_hat和真实输出Y之间的平方误差。
- 定义二维卷积层
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)
使用PyTorch的nn.Conv2d类来定义一个二维卷积层。该层具有1个输入通道和1个输出通道,卷积核的大小是(1, 2),且没有偏置(bias=False)。
- 准备输入和输出数据
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
X和Y是之前定义好的数据,它们被重新塑形为四维张量,以匹配PyTorch中卷积层的输入和输出格式。四维张量的形状是(批量大小, 通道数, 高度, 宽度)。这里批量大小和通道数都为1,而X的高度和宽度分别是6和8,Y的高度和宽度是6和7(注意通常卷积后的大小会减小,但由于这里卷积核的特殊情况,输出大小可能不变或仅在一个维度上减小)。

- 设置学习率
lr = 3e-2 # 学习率
定义学习率lr为0.03,它决定了在梯度下降过程中参数更新的步长。
- 训练循环
for i in range(10):
# ...
这是一个简单的训练循环,只迭代10次。在实际应用中,通常会迭代更多次,并使用验证集来监控过拟合和欠拟合。
- 计算损失
l = (Y_hat - Y) ** 2
计算预测输出Y_hat和真实输出Y之间的平方误差,得到损失张量l。
- 清除梯度
conv2d.zero_grad()
在每次迭代开始时,清除卷积层参数的梯度。这是必要的,因为PyTorch会累积梯度,而我们在每次迭代时只想基于当前迭代计算的梯度来更新参数。
- 反向传播
l.sum().backward()
对损失张量l的所有元素求和,然后调用.backward()方法来进行反向传播,计算卷积层参数的梯度。
- 更新参数
conv2d.weight.data[:] -= lr * conv2d.weight.grad
使用梯度下降来更新卷积层的权重参数。这里假设只有权重需要更新(因为设置了bias=False)。注意这里直接对.data进行操作来更新权重,而在一些更复杂的训练过程中,可能会使用优化器(如torch.optim.SGD)来更新参数。
- 打印损失
if (i + 1) % 2 == 0:
print(f'epoch {
i+1}, loss {
l.sum():.3f}')
每两个迭代打印一次当前迭代的损失值。注意这里打印的是损失张量l所有元素的和,即整个批量上的总损失。
f-string: 以 f 或 F 开头的字符串字面量允许你在字符串中嵌入表达式,这些表达式在运行时会被其值替换。
最后输出所学卷积核的权重张量

二十、卷积层里的填充和步幅

这里直接给出以下结论,官网推导链接
O u p u t = ( I n p u t + 2 ∗ p a d d i n g − k e r n e l ) / s t r i d e + 1 Ouput=(Input+2∗padding−kernel)/stride+1 Ouput=(Input+2∗padding−kernel)/stride+1
结果向下取整
1、卷积填充
- 填充(padding)是指在输入高和宽的两侧填充元素(通常是0元素)。下图在原输入高和宽的两侧分别添加了值为0的元素,使得输入高和宽从3变成了5,并导致输出高和宽由2增加到4。

一般来说,如果在高的两侧一共填充 p h p_h ph行,在宽的两侧一共填充 p w p_w pw列,那么输出形状将会是
( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) , (n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1), (nh−kh+ph+1)×(nw−kw+pw+1),
也就是说,输出的高和宽会分别增加 p h p_h ph和 p w p_w pw。
-
在很多情况下,我们会设置 p h = k h − 1 p_h=k_h-1 ph=kh−1和 p w = k w − 1 p_w=k_w-1 pw=kw−1来使输入和输出具有相同的高和宽。这样会方便在构造网络时推测每个层的输出形状。假设这里 k h k_h kh是奇数,我们会在高的两侧分别填充 p h / 2 p_h/2 ph/2行。如果 k h k_h kh是偶数,一种可能是在输入的顶端一侧填充 ⌈ p h / 2 ⌉ \lceil p_h/2\rceil ⌈ph/2⌉行,而在底端一侧填充 ⌊ p h / 2 ⌋ \lfloor p_h/2\rfloor ⌊ph/2⌋行。在宽的两侧填充同理。
-
卷积神经网络经常使用奇数高宽的卷积核,如1、3、5和7,所以两端上的填充个数相等。对任意的二维数组
X,设它的第i行第j列的元素为X[i,j]。当两端上的填充个数相等,并使输入和输出具有相同的高和宽时,我们就知道输出Y[i,j]是由输入以X[i,j]为中心的窗口同卷积核进行互相关计算得到的。

2、卷积步幅
-
在卷积神经网络中,卷积步幅(Stride)指的是卷积窗口(即卷积核)在输入图像或特征图上从左往右、从上到下移动的距离。这个距离决定了卷积核每次移动覆盖的像素数量。
-
步幅的设置对于卷积神经网络的性能有着重要影响。步幅越大,卷积核每次移动的距离就越大,从而输出的特征图尺寸就越小,这有助于减少计算量和参数数量,但也可能导致一些细节信息的丢失。相反,步幅越小,输出的特征图尺寸就越大,能够保留更多的细节信息,但计算量和参数数量也会相应增加。
当高上步幅为 s h s_h sh,宽上步幅为 s w s_w sw时,输出形状为
⌊ ( n h − k h + p h + s h ) / s h ⌋ × ⌊ ( n w − k w + p w + s w ) / s w ⌋ . \lfloor(n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor. ⌊(nh−kh+ph+sh)/sh⌋×⌊(nw−k

最低0.47元/天 解锁文章
1126

被折叠的 条评论
为什么被折叠?



