使用卷积进行泛化

卷积神经网络基础概念

卷积神经网络(convolutional neural network,CNN)是一种前馈神经网络,由一个或多个卷积层和顶端的全连通层,同时也包括关联权重和池化层。卷积具有两种性质:平移不变性和局部性。

重新考虑全连接层(全连接层的公式考虑为\(y = w^{T}X+b\))的概念,将输入和输出变形为矩阵,可以得到以下的公式: \[ [H]_{i,j} = [U]_{i,j}+ {\textstyle \sum_{k}^{}} {\textstyle \sum_{l}^{}}[W] _{i,j,k,l}\times [X]_{k,l} \] 其中公式解释如下:

  • \([H]_{i,j}\):表示隐藏层在位\((i,j)\)的值。
  • \([U]_{i,j}\)表示一个偏置项
  • \([W] _{i,j,k,l}\) 是一个四阶权重张量,表示输入像素\((k,l)\)对隐藏层位置\((i,j)\)的影响权重,\([W] _{i,j,k,l}\times [X]_{k,l}\)表示像素\((K,L)\)对隐藏层位置\((i,j)\)的贡献。
  • \([X]_{k,l}\)是输入图像在位置\((i,j)\)的像素位置。

进行一个假设用\([V] _{i,j,a,b}\)替换\([W] _{i,j,k,l}\),并假设:\(k = i+a\)\(l = i+a\) ,我们通过改变下标,得到全新的权重(v是w的重新索引)\(v_{i,j,a,b} = w_{i,j,i+a,j+b}\)最终得到公式:

\[ [H]_{i,j} = [U]_{i,j}+ {\textstyle \sum_{a}^{}} {\textstyle \sum_{b}^{}}[V] _{i,j,a,b}\times [X]_{x+a,j+b} \] 这实际上是一个典型的卷积操作,其中\([V] _{i,j,a,b}\)是卷积核,\([X]_{x+a,j+b}\)是图像在卷积核覆盖的区域内的像素。这种表示方式在图像处理和神经网络中被广泛使用,因为它利用图像的局部空间关系,同时减少模型的参数量。

卷积的性质

平移不变性

引用上述第一个原则:平移不变性。及意味着检测对象在输入\(X\)中的平移,应仅仅导致隐藏向量\(H\)的平移,也就是说\(v\)不应该依赖于\(j\)。因此,公式可以简化为 \[ [\mathbf{H}]_{i, j} = u + \sum_a\sum_b [\mathbf{V}]_{a, b} [\mathbf{X}]_{i+a, j+b}. \]

局部性

局部性,用来训练参数\([\mathbf{H}]_{i, j}\)的相关信息,我们不应偏离到距\((i,j)\)很远的地方。所以给出的解决方案是\(|a|> \Delta\)或者\(|b| > \Delta\),设置\([\mathbf{V}]_{a, b} = 0\),及公式表示为: \[ [\mathbf{H}]_{i, j} = u + \sum_{a = -\Delta}^{\Delta} \sum_{b = -\Delta}^{\Delta} [\mathbf{V}]_{a, b} [\mathbf{X}]_{i+a, j+b}. \]

卷积的数学公式

数学中,两个函数形如如下形式的(\(f, g: \mathbb{R}^d \to \mathbb{R}\)),定义为卷积: \[ (f * g)(\mathbf{x}) = \int f(\mathbf{z}) g(\mathbf{x}-\mathbf{z}) d\mathbf{z}. \] 卷积是把一个函数”翻转“并位移x,测量\(f\)\(g\)之间的重叠。党为离散对象时,积分则变为求和,可以得到如下定义,该一维公式多用于文本、语言、时间序列的模型中。 \[ (f * g)(i) = \sum_a f(a) g(i-a). \] 以此类推,二维卷积的公式为: \[ (f * g)(i, j) = \sum_a\sum_b f(a, b) g(i-a, j-b). \]

感受野的概念

卷积神经网络中每一层输出的特征图上的像素点在原始图像上映射的区域大小,第一层卷积层的输出特征图像素的感受野大小等于卷积核的大小,其它卷积层的输出特征的感受野的大小和它之前所有层的卷积核的大小和步长都有关。

感受野图例

感受野的计算公式: \[ r_{n} = r_{n-1}+(k_{n-1})\coprod_{i = 1}^{n-1}S_{i} \] 其中\(n\ge 2\)

\(k:\)kernel size

\(S_{n}:\)Stride

\(r_{n}:\)receptive-field,感受野,n表示层数

感受野计算例子

计算过程:

\(r_{1} = 11\)

\(r_{2} = 11+(3-1)\times 4 = 19\)

\(r_{3} = 19+(5-1)\times 4\times 2 = 51\)

填充和步幅

卷积的输出形状填充\((padding)\)和步幅\((stride)\)的影响。

填充,以下图为例在图像的边界填充元素:

在卷积周围填充0

如果我们添加\(p_{h}\)行填充和\(p_{w}\)列填充,则输出的形状为: \[ (n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1)。 \] 步幅,滑块滑动的距离被称为步幅,如下图为垂直步幅为3,水平步幅为2的二维互相关运算。

image-20241108142335406

如果\(p_{h}\)行填充和\(p_{w}\)列填充,水平步幅为\(s_{w}\)、垂直步幅为\(s_{h}\)则输出的形状为: \[ \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. \]

池化

卷积对位置信息较为敏感,用池化减少位置信息对卷积层:

最大池化层样例

简单池化层基础实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch
from torch import nn
from d2l import torch as d2l

def pool2d(X, pool_size, mode='max'):
# 定义池化层的长、宽参数
p_h, p_w = pool_size
# 初始化池化层的输出
Y = torch.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] = X[i: i + p_h, j: j + p_w].max()

elif mode == 'avg':
Y[i, j] = X[i: i + p_h, j: j + p_w].mean()

卷积的代码理解

一维卷积的运算:

单通道计算

多输入通道的计算:

两个输入通道的互相关计算

定义一个卷积层:

1
2
3
4
5
6
7
8
class Conv2D(nn.Block):
def __init__(self, kernel_size, **kwargs):
super().__init__(**kwargs)
self.weight = self.params.get('weight', shape=kernel_size)
self.bias = self.params.get('bias', shape=(1,))

def forward(self, x):
return corr2d(x, self.weight.data()) + self.bias.data()

定义一个可学习的卷积核:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2D(1, kernel_size=(1, 2), use_bias=False)
conv2d.initialize()

# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
# 其中批量大小和通道数都为1


X = X.reshape(1, 1, 6, 8)
Y = Y.reshape(1, 1, 6, 7)
lr = 3e-2 # 学习率

for i in range(10):
with autograd.record():
Y_hat = conv2d(X)
l = (Y_hat - Y) ** 2
l.backward()
# 迭代卷积核
conv2d.weight.data()[:] -= lr * conv2d.weight.grad()
if (i + 1) % 2 == 0:
print(f'epoch {i+1}, loss {float(l.sum()):.3f}')

输出:

1
2
3
4
5
6
epoch 2, loss 4.949
epoch 4, loss 0.831
epoch 6, loss 0.140
epoch 8, loss 0.024
epoch 10, loss 0.004
[07:16:32] ../src/base.cc:48: GPU context requested, but no GPUs found.

带有步幅和填充的例子:

1
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))

使用卷积进行泛化
http://example.com/2024/11/03/使用卷积进行泛化/
作者
yzcabe
发布于
2024年11月3日
许可协议