深度解析:卷积神经网络的奥秘与魅力
在当今的人工智能时代,卷积神经网络(Convolutional Neural Networks,简称CNNs)无疑是计算机视觉领域的明星。从图像识别到自动驾驶汽车,从医学影像分析到艺术创作,CNNs的应用无处不在,其背后的原理也值得我们深入探索。本文旨在揭开CNNs的神秘面纱,揭示其工作原理与内在机制。
一、起源与灵感
CNNs的设计灵感源自于对生物视觉系统的观察。1959年,Hubel和Wiesel在猫的视觉皮层中发现了两种类型的神经元——简单细胞和复杂细胞,它们分别对特定方向的边缘和纹理敏感。这一发现启发了计算机科学家,他们开始模仿大脑的视觉处理方式,设计出能够自动学习和识别图像特征的神经网络模型。
二、卷积神经网络的优点
在传统神经网络中,我们要识别下图红色框中的图像时,我们很可能识别不出来,因为这六张图的
位置都不通,计算机无法分辨出他们其实是一种形状或物体。

传统神经网络原理

传统神经网络用大量物体位于不同位置的数据训练,同时增加网络的隐藏层个数从而扩大网络学习这些变体的能力。
卷积神经网络(CNN)等深度学习模型在卷积层中使用了卷积操作,这个操作根据物体的不变性,可以捕捉到图像中的局部特征而不受其位置的影响。 这样一个物体不管在画面左侧还是右侧,都会被识别为同一物体,实现平移不变性。
三、什么是卷积?
在卷积神经网络中,卷积操作是指将一个可移动的小窗口(称为数据窗口,如下图绿色矩形)与图像进行逐元素相乘然后相加的操作。这个小窗口其实是一组固定的权重,它可以被看作是一个特定的滤波器(filter)或卷积核。这个操作的名称“卷积”,源自于这种元素级相乘和求和的过程。这一操作是卷积神经网络名字的来源。

上面的那个绿色的小窗口是资料视窗。简单来说,卷积运算就是利用一个具有一定权值的可动窗来抽取一幅图象中的特征,并在此基础上对其进行卷积运算,使其可以有效地从图象中抽取出具有不同特性的信息。如果用文字说明的话,可能会让人看不懂,上图:
在这个图片中,蓝色的方框表示的是一个数据窗,红色的方框表示卷积核(滤波器),最终生成的绿色方框表示卷积的结果(数据窗口中的数据与卷积核逐个元素相乘再求和)。
四、卷积的计算
a.步长stride:每次滑动的位置步长。
b. 卷积核的个数:决定输出的depth厚度。同时代表卷积核的个数。
c. 填充值zero-padding:在外围边缘补充若干圈0,方便从初始位置以步长为单位可以刚好滑倒末尾位置,通俗地讲就是为了总长能被步长整除。
以上图为例,那么:
• 数据窗口每次移动两个步长取 3*3 的局部数据,即 stride=2 。
• 两个神经元,即 depth=2 ,意味着有两个滤波器。
• zero-padding=1 。
为什么要进行数据填充:
假设有一个大小为 4x4 的输入图像:
| 1 | 2 | 3 | 4 |
| 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 |
现在,我们要应用一个 3x3 的卷积核进行卷积操作,步幅(stride)为 1,且要使用填充(padding)为 1。如果不使用填充,卷积核的中心将无法对齐到输入图像的边缘,导致输出特征图尺寸变小。假设我们使用步幅(stride)为 1 进行卷积,那么在不使用填充的情况下,输出特征图的尺寸将是 2x2。
所以我们要在它的周围填充一圈0,填充为 1 意味着在输入图像的周围添加一圈零值。添加填充后的图像:
| 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 2 | 3 | 4 | 0 |
| 0 | 5 | 6 | 7 | 8 | 0 |
| 0 | 9 | 10 | 11 | 12 | 0 |
| 0 | 13 | 14 | 15 | 16 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 |
现在,我们将 3x3 的卷积核应用于这个填充后的输入图像,计算卷积结果,得到大小不变的特征图。
数据填充的主要目的是确保卷积核能够覆盖输入图像的边缘区域,同时保持输出特征图的大小。这对于在CNN中保留空间信息和有效处理图像边缘信息非常重要。
五、卷积神经网络的组成
1.基础:输入层
输入层接收原始图像数据。图像通常由三个颜色通道(红、绿、蓝)组成,形成一个二维矩阵,表示像素的强度值。
2.核心组件:卷积层
CNNs的核心是卷积层,它通过一系列可学习的滤波器(filters)或卷积核(kernels)来扫描输入数据,捕捉局部特征。每一个滤波器都像一个小型的专家,专注于识别图像中的特定模式,如边缘、形状或纹理。卷积操作遵循权重共享原则,这意味着同一滤波器在整个输入空间内重复使用,这不仅减少了参数数量,还提高了模型的计算效率。
3.激活函数:引入非线性
为了使CNNs能够学习复杂的函数映射,激活函数(activation functions)扮演着至关重要的角色。它们打破了线性关系,赋予网络学习非线性决策边界的强大能力。ReLU(Rectified Linear Unit)是最常用的激活函数之一,它简单而有效,能够促进模型的收敛速度,同时也避免了梯度消失的问题。
4.池化层:特征降维与平移不变性
紧随卷积层之后的是池化层(pooling layers),其主要任务是降低特征映射的空间维度,减少计算量和参数数量,同时增强模型的平移不变性。最常用的池化策略是最大池化(max pooling),它选择区域内最大的值作为输出,从而保留关键信息,同时丢弃不那么重要的细节。
5.全连接层:决策与分类
在经历了多次卷积和池化之后,得到的特征映射会被展平为一个向量,送入全连接层(fully connected layers)。全连接层负责将学到的特征映射转换为分类或回归的结果。这里的每一层神经元都与前一层的所有神经元相连,通过加权求和和非线性变换,最终输出模型的预测。
6.反向传播与优化
CNNs的训练依赖于反向传播算法,这是一种高效的梯度计算方法,能够从输出层开始,逐层向前传播误差梯度,调整网络中的权重和偏置,以最小化损失函数。现代深度学习框架,如TensorFlow和PyTorch,提供了自动微分工具,极大地简化了这一过程。
7.正则化与批量归一化
为了防止过拟合,CNNs通常会采用正则化技术,如L1或L2正则化,以及Dropout,来限制模型的复杂度。批量归一化(batch normalization)则是另一种流行的技巧,它通过对每一批次数据进行标准化,加快了训练速度,提高了模型的稳定性。
六、代码示例
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Normalize
# Check if CUDA (GPU) is available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 加载数据
training_data = datasets.MNIST(
root="data",
train=True,
download=True,
transform=ToTensor(),
)
test_data = datasets.MNIST(
root="data",
train=False,
download=True,
transform=ToTensor(),
)
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=False)
# 搭建卷积神经网络模型
class Modified_CNN_MNIST(nn.Module):
def __init__(self):
super(Modified_CNN_MNIST, self).__init__()
self.features = nn.Sequential(
# 第一个卷积层
nn.Conv2d(1, 8, kernel_size=3, padding=1), # 输入1x28x28,输出8x28x28
nn.BatchNorm2d(8),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2), # 输出8x14x14
# 第二个卷积层
nn.Conv2d(8, 16, kernel_size=3, padding=1), # 输入8x14x14,输出16x14x14
nn.BatchNorm2d(16),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2), # 输出16x7x7
# 第三个卷积层
nn.Conv2d(16, 32, kernel_size=3, padding=1), # 输入16x7x7,输出32x7x7
nn.BatchNorm2d(32),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2), # 输出32x3x3
# 第四个卷积层
nn.Conv2d(32, 16, kernel_size=3, padding=1), # 输入32x3x3,输出16x3x3
nn.BatchNorm2d(16),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2), # 输出16x1x1
)
self.classifier = nn.Sequential(
nn.Dropout(p=0.5),
nn.Linear(16 * 1 * 1, 64),
nn.BatchNorm1d(64),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(64, 10),
)
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
model = Modified_CNN_MNIST().to(device)
# 定义训练和验证函数
def train(dataloader, model, loss_fn, optimizer):
model.train()
for batch_idx, (X, y) in enumerate(dataloader):
X, y = X.to(device), y.to(device)
optimizer.zero_grad()
pred = model(X)
loss = loss_fn(pred, y)
loss.backward()
optimizer.step()
def test(dataloader, model, loss_fn):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for X, y in dataloader:
X, y = X.to(device), y.to(device)
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= len(dataloader.dataset)
accuracy = correct / len(dataloader.dataset) * 100
print(f"Test result:\n Accuracy: {accuracy:.2f}%, Avg loss: {test_loss:.4f}")
# 定义损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 训练和评估模型
epochs = 20
for epoch in range(epochs):
print(f"Epoch {epoch + 1}\n-------------------------------")
train(train_dataloader, model, loss_fn, optimizer)
test(test_dataloader, model, loss_fn)
print("Done!")
这段代码是一个使用PyTorch框架构建的修改后的卷积神经网络模型,用于对MNIST数据集进行训练和测试。以下是代码的简要概述:
1. 导入了必要的PyTorch模块: torch 、 nn 、 DataLoader 、 datasets 以及 ToTensor 等。
2. 检查并设置运行设备为CUDA(GPU)或CPU。
3. 加载MNIST数据集,包括训练数据集和测试数据集,并进行转换为张量格式。
4. 创建训练数据加载器 train_dataloader 和测试数据加载器 test_dataloader ,每次加载64个样本。
5. 定义了一个名为 Modified_CNN_MNIST 的卷积神经网络模型,包括特征提取层和分类器层,并实现了前向传播逻辑。
6. 定义了训练函数 train 和测试函数 test ,用于训练模型和评估模型性能。
7. 使用交叉熵损失函数和Adam优化器进行模型训练。
8. 进行多轮训练(共20轮),每轮训练后输出测试结果,包括准确率和平均损失。
9. 最终输出"Done!"表示训练和测试完成。
更多推荐
所有评论(0)