当输入为图像数据时,每张照片具有百万级像素,这意味着网络的每次输入都有一百万个维度。使得全连接层参数爆炸,对于GPU的性能非常高,想要训练这个模型几乎是不可能实现的。所以学者们提出在上一层提取特征的基础上获得更高级别的特征。
一:卷积神经网络
1:卷积操作
通过卷积核从左到右、从上到下的滑动,将卷积核与输入按元素相乘,得出输出。如输出中的19计算为:0*0+1*1+3*2+4*3=19。其余值以此类推。
卷积操作的实现:
import torch
from torch import nn
def corr2d(X, K):
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 = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
output = corr2d(X, K)
print(output)
在卷积操作中,还可以通过填充和步长影响输出的大小。填充就是在输入图像的边界填充元素,通常填充元素是0。代码实现时通过设置padding这个参数控制填充的大小。
卷积核向下,向右移动时,通常默认滑动一个元素,但有时候为了高效计算或是缩减采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素。如图所示垂直步长为3,水平步长为2,代码实现时通过设置stride参数来控制步长的大小。
填充和步长的代码实现:
import torch
from torch import nn
def comp_conv2d(conv2d, X):
X = X.reshape((1, 1) + X.shape)
Y = conv2d(X)
return Y.reshape(Y.shape[2:])
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
output_shape = comp_conv2d(conv2d, X).shape
print(output_shape)
2:池化操作
与卷积层类似,池化计算也是由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动。然而,不同于卷积操作的是,池化层通常计算汇聚窗口中所有元素的最大值或平均值。如图所示为最大池化的计算:
代码实现:
import torch
from torch import nn
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
max_pool2d = nn.MaxPool2d(3, padding=1, stride=2)
avg_pool2d = nn.MaxPool2d(3, padding=1, stride=2)
Max_Y = max_pool2d(X)
Avg_Y = avg_pool2d(X)
print(Max_Y, Avg_Y)
卷积神经网络就是由多个卷积层和池化层构成,后面连接全连接网络。
二:LeNet
LeNet网络架构如图所示:
代码实现:
import torch
from torch import nn
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Flatten(),
nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
nn.Linear(120, 84), nn.Sigmoid(),
nn.Linear(84, 10))