第5章 卷积神经网络基础¶
📌 章节定位:本文档隶属于计算机视觉教程体系,侧重CNN在视觉任务中的实际应用。 - 本文档重点:CNN在图像分类任务中的应用、OpenCV/PyTorch工具链使用、实际项目开发流程、模型训练与调优 - 理论原理方向:如需深入了解卷积运算的数学推导、感受野计算公式、各种卷积变体(转置卷积、深度可分离卷积等)的原理,请参考 深度学习/02-卷积神经网络/01-卷积神经网络基础.md
📚 章节概述¶
本章深入介绍卷积神经网络(CNN)的核心原理,包括卷积操作、池化、激活函数、反向传播等。CNN是现代计算机视觉的基石,理解其原理对于掌握深度学习至关重要。
学习时间:5-7天 难度等级:⭐⭐⭐⭐⭐ 前置知识:第1-4章、深度学习基础
🎯 学习目标¶
完成本章后,你将能够: - 深入理解CNN的工作原理 - 掌握卷积、池化等核心操作 - 理解CNN的反向传播 - 能够从零实现简单的CNN - 完成手写数字分类项目
5.1 CNN基本原理¶
5.1.1 为什么需要CNN?¶
传统方法的局限: - 手工特征提取(SIFT、HOG)依赖经验 - 难以处理大规模数据 - 特征表达能力有限
CNN的优势: - 自动学习特征 - 层次化特征表示 - 参数共享(减少参数量) - 平移不变性
5.1.2 CNN架构¶
核心组件: 1. 卷积层:特征提取 2. 激活层:非线性变换 3. 池化层:降维、不变性 4. 全连接层:分类
5.2 卷积操作¶
5.2.1 卷积原理¶
注意:在深度学习中,卷积层实际实现的是互相关(Cross-Correlation)操作,而非严格数学意义上的卷积。互相关无需对核进行翻转,因此深度学习框架(如PyTorch、TensorFlow)中实现的是互相关,但在传统文献中常统称为"卷积"。
数学定义 - 互相关操作:
严格数学意义上的卷积(需要先翻转核):
本文档中以及深度学习框架中使用的均为互相关操作,为简化表述,统一称为"卷积"。
代码实现:
import numpy as np
def conv2d(image, kernel, stride=1, padding=0):
"""2D卷积(互相关操作)"""
# 添加padding
if padding > 0:
image = np.pad(image, padding, mode='constant')
# 计算输出尺寸(考虑padding)
h_out = (image.shape[0] - kernel.shape[0]) // stride + 1
w_out = (image.shape[1] - kernel.shape[1]) // stride + 1
# 简化公式:h_out = (H - K + 2P) / S + 1
# 其中 H 为输入高度,K 为核大小,P 为 padding,S 为 stride
# 初始化输出
output = np.zeros((h_out, w_out))
# 卷积操作
for i in range(h_out):
for j in range(w_out):
region = image[i*stride:i*stride+kernel.shape[0], j*stride:j*stride+kernel.shape[1]]
output[i, j] = np.sum(region * kernel)
return output
# 示例
image = np.random.rand(5, 5)
kernel = np.array([[1, 0, -1], [1, 0, -1], [1, 0, -1]]) # np.array创建NumPy数组
result = conv2d(image, kernel, stride=1, padding=0)
5.2.2 使用PyTorch¶
import torch
import torch.nn as nn
# 定义卷积层
conv = nn.Conv2d(
in_channels=3, # 输入通道数
out_channels=64, # 输出通道数
kernel_size=3, # 卷积核大小
stride=1, # 步长
padding=1 # padding
)
# 输入图像
x = torch.randn(1, 3, 32, 32)
# 前向传播
output = conv(x)
print(f"输出尺寸: {output.shape}") # [1, 64, 32, 32]
5.3 池化层¶
5.3.1 最大池化¶
def max_pooling(image, kernel_size=2, stride=2):
"""最大池化"""
h_out = (image.shape[0] - kernel_size) // stride + 1
w_out = (image.shape[1] - kernel_size) // stride + 1
output = np.zeros((h_out, w_out))
for i in range(h_out):
for j in range(w_out):
region = image[i*stride:i*stride+kernel_size, j*stride:j*stride+kernel_size]
output[i, j] = np.max(region)
return output
# PyTorch实现
pool = nn.MaxPool2d(kernel_size=2, stride=2)
output = pool(x)
5.3.2 平均池化¶
5.4 激活函数¶
5.4.1 ReLU¶
5.4.2 其他激活函数¶
# Leaky ReLU
leaky_relu = nn.LeakyReLU(negative_slope=0.01)
# GELU
gelu = nn.GELU()
# Swish
swish = nn.SiLU()
5.5 从零实现CNN¶
import torch
import torch.nn as nn
import torch.nn.functional as F
class SimpleCNN(nn.Module): # 继承nn.Module定义网络层
def __init__(self, num_classes=10):
super(SimpleCNN, self).__init__()
# 卷积层
self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
# 池化层
self.pool = nn.MaxPool2d(2, 2)
# 全连接层
self.fc1 = nn.Linear(64 * 7 * 7, 128)
self.fc2 = nn.Linear(128, num_classes)
def forward(self, x):
# 卷积 + ReLU + 池化
x = self.pool(F.relu(self.conv1(x))) # F.xxx PyTorch函数式API
x = self.pool(F.relu(self.conv2(x)))
# 展平
x = x.view(-1, 64 * 7 * 7) # 重塑张量形状
# 全连接
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
# 创建模型
model = SimpleCNN(num_classes=10)
print(model)
5.6 训练CNN¶
import torch.optim as optim
from torchvision import datasets, transforms
# 数据预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
# 加载数据
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True) # DataLoader批量加载数据
# 损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练循环
def train(model, loader, criterion, optimizer, epochs=5):
model.train() # train()训练模式
for epoch in range(epochs):
running_loss = 0.0
for i, (images, labels) in enumerate(loader): # enumerate同时获取索引和元素
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播
optimizer.zero_grad() # 清零梯度
loss.backward() # 反向传播计算梯度
optimizer.step() # 更新参数
running_loss += loss.item() # 将单元素张量转为Python数值
print(f'Epoch {epoch+1}, Loss: {running_loss/len(loader):.4f}')
# 训练
train(model, train_loader, criterion, optimizer, epochs=5)
5.7 练习题¶
基础题¶
- 简答题:
- CNN相比全连接网络有什么优势?
①局部连接:每个神经元只连接局部区域,捕捉局部空间特征;②参数共享:同一卷积核在整个图像上滑动,大幅减少参数量;③平移不变性:无论特征在图像哪个位置都能被检测到;④层次化特征提取:浅层学习边缘纹理,深层学习高级语义。
- 卷积操作的作用是什么?
卷积操作通过滑动卷积核与局部区域做加权求和来提取特征。不同卷积核可提取不同类型特征(边缘、纹理、形状),多层卷积堆叠可从低级特征逐步构建高级语义特征。
进阶题¶
- 编程题:
- 从零实现一个卷积层。
- 实现一个简单的CNN分类器。
5.8 面试准备¶
大厂面试题¶
Q1: 为什么CNN适合图像处理?
参考答案: - 局部连接:捕捉局部特征 - 参数共享:减少参数量 - 平移不变性:对位置变化鲁棒 - 层次化特征:从低层到高层
Q2: 1x1卷积的作用是什么?
参考答案: - 降维/升维(改变通道数) - 增加非线性 - 跨通道信息交互 - 计算高效
5.9 本章小结¶
核心知识点¶
- CNN原理:局部连接、参数共享
- 卷积操作:特征提取
- 池化:降维、不变性
- 激活函数:ReLU等
- 训练:前向传播、反向传播
下一步¶
下一章:06-经典CNN架构.md - 学习经典架构
恭喜完成第5章! 🎉