当前位置: 首页 > news >正文

8.13打卡 DAY 41 简单CNN

DAY 41 学习文档: 简单CNN入门

你好!昨天我们发现,即使加深了网络,普通的多层感知机(MLP)在处理像CIFAR-10这样的图像任务时,准确率也难以突破瓶颈(大约50-55%)。根本原因在于MLP的“展平”操作破坏了图像宝贵的空间结构信息

为了解决这个问题,我们今天的主角——卷积神经网络(Convolutional Neural Network, CNN)登场了!CNN通过卷积层池化层,能够有效地提取和利用图像的局部特征,是计算机视觉领域的基石。

今日学习要点:

  1. 数据增强:学习如何在训练前对图像进行变换,以提升模型的泛化能力。
  2. CNN模型定义:掌握CNN模型的基本组件(卷积、池化、全连接)及其在PyTorch中的写法。
  3. Batch归一化:理解其作用以及为何在CNN中如此常用。
  4. 特征图:了解CNN是如何通过特征图来“看”世界的。
  5. 学习率调度器:学习一种高级的超参数优化技巧,让模型训练更智能。

1. 数据增强 (Data Augmentation)

在训练数据有限的情况下,为了让模型见到“更多样”的数据,防止过拟合,我们常常在数据预处理阶段引入数据增强

核心思想:对训练集中的每张图片进行一系列随机的几何或像素变换,生成“新”的、但语义不变的训练样本。这极大地丰富了数据的多样性。

常见增强策略:

  • 几何变换:随机裁剪、随机水平翻转、随机旋转。
  • 像素变换:调整亮度、对比度、饱和度。

注意:数据增强只在训练集上使用!测试集必须保持原始状态,以真实地评估模型的性能。

代码示例:为CIFAR-10添加数据增强

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt# 检查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 1. 定义数据预处理
# 训练集:使用多种数据增强方法
train_transform = transforms.Compose([transforms.RandomCrop(32, padding=4),       # 随机裁剪transforms.RandomHorizontalFlip(),          # 随机水平翻转transforms.ColorJitter(brightness=0.2, contrast=0.2), # 随机颜色抖动transforms.RandomRotation(15),              # 随机旋转transforms.ToTensor(),                      # 转换为张量# 标准化:使用CIFAR-10数据集的均值和标准差transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])# 测试集:仅进行必要的标准化
test_transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])# 2. 加载数据集
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, transform=test_transform)# 3. 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)

2. CNN模型定义的写法

一个典型的CNN模型由两部分组成:

  1. 特征提取器:由多个卷积块(卷积层、激活函数、可选的归一化层、池化层)堆叠而成。
  2. 分类器:由全连接层组成,负责根据提取到的特征进行分类。
卷积层 (nn.Conv2d) 的关键参数:
  • in_channels: 输入特征图的通道数(对于第一层,就是图像的通道数,如RGB为3)。
  • out_channels: 输出特征图的通道数,也代表了卷积核的数量
  • kernel_size: 卷积核的大小,如 3(3, 3)
  • padding: 在图像边缘填充的像素数,padding=1 通常用于保持3x3卷积后的尺寸不变。

代码示例:定义一个简单的CNN模型

class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()# --- 特征提取器 ---# 卷积块 1self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1)self.bn1 = nn.BatchNorm2d(32) # Batch归一化层self.relu1 = nn.ReLU()self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2) # 池化层,尺寸减半# 卷积块 2self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)self.bn2 = nn.BatchNorm2d(64)self.relu2 = nn.ReLU()self.pool2 = nn.MaxPool2d(kernel_size=2)# 卷积块 3self.conv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)self.bn3 = nn.BatchNorm2d(128)self.relu3 = nn.ReLU()self.pool3 = nn.MaxPool2d(kernel_size=2)# --- 分类器 ---# 展平后的维度: 128通道 * 4x4尺寸 = 2048self.fc1 = nn.Linear(128 * 4 * 4, 512)self.dropout = nn.Dropout(0.5)self.fc2 = nn.Linear(512, 10) # 输出10个类别def forward(self, x):# 输入尺寸: [batch_size, 3, 32, 32]# 卷积块 1x = self.pool1(self.relu1(self.bn1(self.conv1(x)))) # 输出: [batch_size, 32, 16, 16]# 卷积块 2x = self.pool2(self.relu2(self.bn2(self.conv2(x)))) # 输出: [batch_size, 64, 8, 8]# 卷积块 3x = self.pool3(self.relu3(self.bn3(self.conv3(x)))) # 输出: [batch_size, 128, 4, 4]# 展平x = x.view(-1, 128 * 4 * 4) # 输出: [batch_size, 2048]# 全连接层x = self.fc1(x)x = self.relu3(x) # 可以复用激活函数对象x = self.dropout(x)x = self.fc2(x) # 输出: [batch_size, 10]return x# 初始化模型
model = CNN().to(device)

3. Batch归一化 (Batch Normalization)

这是CNN中一个非常重要的“插件”,通常放在卷积层之后、激活函数之前。

作用

  • 解决内部协变量偏移:在深层网络中,前一层参数的更新会导致后一层输入的分布不断变化,使得学习困难。Batch归一化通过对每个批次(batch)的数据进行标准化(调整为均值为0,方差为1),使得每层网络的输入分布相对稳定。
  • 加速训练:由于输入分布更稳定,我们可以使用更大的学习率,从而加快模型收敛。
  • 防止过拟合:有一定的正则化效果。

注意:它在训练和推理(测试)阶段的行为是不同的。训练时,它使用当前batch的均值和方差;推理时,它使用在整个训练集上学习到的全局统计量。model.train()model.eval() 会自动切换这两种模式。


4. 特征图 (Feature Map)

卷积层的输出被称为特征图。它保留了空间维度(高和宽),而通道数则由卷积核的数量决定。

  • 直观理解:每个特征图可以看作是模型从图像中提取到的一种特定特征。例如,浅层的特征图可能检测到边缘、颜色、纹理;深层的特征图则能组合这些低级特征,形成更复杂的模式,如“眼睛”、“车轮”等。
  • 与MLP的区别:MLP的隐藏层输出是一维向量,不包含空间信息,因此无法被称为特征图。

通过可视化这些中间层的特征图,我们可以更好地理解CNN的学习过程,这也是深度学习可解释性的一个重要方向。


5. 学习率调度器 (Learning Rate Scheduler)

优化器(如Adam)负责根据梯度更新参数,而学习率调度器则负责动态地调整优化器中的基础学习率

核心思想

  • 训练初期:使用一个较大的学习率,让模型快速收敛。
  • 训练后期:当模型性能趋于稳定时,减小学习率,帮助模型更精细地探索最优解。

代码示例:使用 ReduceLROnPlateau 调度器
这个调度器会在监测的指标(如测试集损失)停止改善时,自动降低学习率。

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)# 定义学习率调度器
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer,        # 作用于哪个优化器mode='min',       # 监测指标越小越好(如loss)patience=3,       # 如果连续3个epoch指标没有改善,就降低学习率factor=0.5        # 学习率降低的倍数 (new_lr = old_lr * 0.5)
)# 在训练循环的每个epoch结束后,需要调用它
# scheduler.step(epoch_test_loss)

对比优化器和调度器

组件作用调整对象
优化器 (Optimizer)根据梯度更新模型参数模型权重和偏置
调度器 (Scheduler)根据训练进程调整学习率优化器中的基础学习率

整合训练

现在,我们把所有部分整合起来,用新的CNN模型和调度器来训练CIFAR-10。你会发现,准确率有了显著的提升!

# (此处省略和之前重复的train和plot函数定义)def train_cnn(... scheduler, ...):# ... (训练循环内部不变) ...# 在每个epoch的测试阶段之后epoch_test_loss, epoch_test_acc = test(...)# 更新学习率调度器scheduler.step(epoch_test_loss)print(...)# 执行训练
epochs = 20
print("开始使用CNN训练模型...")
final_accuracy = train_cnn(model, train_loader, test_loader, criterion, optimizer, scheduler, device, epochs)
print(f"训练完成!最终测试准确率: {final_accuracy:.2f}%")

预期结果:使用这个简单的CNN,CIFAR-10的测试准确率可以轻松达到**75%-80%**以上,远超MLP的50%左右。这充分证明了CNN在提取图像空间特征方面的强大能力。


@浙大疏锦行

http://www.lryc.cn/news/619628.html

相关文章:

  • 多模态RAG赛题实战之策略优化--Datawhale AI夏令营
  • 桌面运维如何深造
  • MySQL表约束
  • Spring Boot项目中线程池的全面教程
  • 中高级餐饮服务食品安全员考试核心知识点汇总
  • Spring Boot初级概念及自动配置原理
  • Spring Boot 3 连接池最大连接数设置建议
  • sample_kol里配置为 deep sleep mode,则系统进入 STR
  • Spring、Spring MVC、Spring Boot与Spring Cloud的扩展点全面梳理
  • Python【算法中心 03】Docker部署Django搭建的Python应用流程实例(Docker离线安装配置+Django项目Docker部署)
  • django name ‘QueryDict‘ is not defined
  • 更改webpack默认配置项
  • Git Bash
  • 导轨焊接机器人:重塑高效精准焊接的新标杆
  • VUE3中的内置 API
  • amis表单较验
  • SpringCloud(1)
  • 从“存得对”到“存得准”:MySQL 数据类型与约束全景指南
  • opencv:直方图
  • Java pdf工具
  • 想要PDF翻译保留格式?用对工具是关键
  • java中数组和list的区别是什么?
  • 双屏加固笔记本电脑C156-2:坚固与高效的完美融合
  • 如何在 Ubuntu 24.04 LTS Noble Linux 上安装 FileZilla Server
  • Prompt工程师基础技术学习指南:从入门到实战
  • 为什么要使用消息队列呢?
  • STM32学习笔记10—DMA
  • 408每日一题笔记 41-50
  • 2023 年全国硕士研究生招生考试真题笔记
  • C语言零基础第15讲:字符函数和字符串函数