第P2周: CIFAR10彩色图片识别

📅 2026/7/31 6:40:00 👁️ 阅读次数 📝 编程学习
第P2周: CIFAR10彩色图片识别

第P2周: CIFAR10彩色图片识别

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊

文章目录

  • 第P2周: CIFAR10彩色图片识别
    • 一、 前期准备
      • 1、设置GPU
      • 2. 导入数据
      • 3. 数据可视化
    • 二、构建简单的CNN网络
    • 三、训练模型
      • 1. 设置超参数
      • 2. 编写训练函数
      • 3. 编写测试函数
      • 4. 正式训练
    • 四、结果可视化
    • 五、个人总结
      • 1. 学习目标
      • 2. 核心知识点
      • 3. 遇到的问题与解决
      • 4. 结果分析
      • 5. 改进方向
      • 6. 收获与体会

一、 前期准备

1、设置GPU

importtorchimporttorch.nnasnnimportmatplotlib.pyplotaspltimporttorchvision device=torch.device("cuda"iftorch.cuda.is_available()else"cpu")device
device(type='cuda')

2. 导入数据

train_ds=torchvision.datasets.CIFAR10('data',train=True,transform=torchvision.transforms.ToTensor(),# 将数据类型转化为Tensordownload=True)test_ds=torchvision.datasets.CIFAR10('data',train=False,transform=torchvision.transforms.ToTensor(),# 将数据类型转化为Tensordownload=True)
D:\Software\anaconda3\envs\pytorch-latest\Lib\site-packages\torchvision\datasets\cifar.py:83: VisibleDeprecationWarning: dtype(): align should be passed as Python or NumPy boolean but got `align=0`. Did you mean to pass a tuple to create a subarray type? (Deprecated NumPy 2.4) entry = pickle.load(f, encoding="latin1")
batch_size=32train_dl=torch.utils.data.DataLoader(train_ds,batch_size=batch_size,shuffle=True)test_dl=torch.utils.data.DataLoader(test_ds,batch_size=batch_size)
imgs,label=next(iter(train_dl))imgs.shape
torch.Size([32, 3, 32, 32])

3. 数据可视化

importnumpyasnp plt.figure(figsize=(20,5))fori,imgsinenumerate(imgs[:20]):npimg=imgs.numpy().transpose(1,2,0)plt.subplot(2,10,i+1)plt.imshow(npimg,cmap=plt.cm.binary)plt.axis('off')


二、构建简单的CNN网络

importtorch.nn.functionalasF num_classes=10classModel(nn.Module):def__init__(self):super().__init__()# 特征提取网络self.conv1=nn.Conv2d(3,64,kernel_size=3)self.pool1=nn.MaxPool2d(kernel_size=2)self.conv2=nn.Conv2d(64,64,kernel_size=3)self.pool2=nn.MaxPool2d(kernel_size=2)self.conv3=nn.Conv2d(64,128,kernel_size=3)self.pool3=nn.MaxPool2d(kernel_size=2)#分类网络self.fc1=nn.Linear(512,256)self.fc2=nn.Linear(256,num_classes)defforward(self,x):x=self.pool1(F.relu(self.conv1(x)))x=self.pool2(F.relu(self.conv2(x)))x=self.pool3(F.relu(self.conv3(x)))x=torch.flatten(x,start_dim=1)x=F.relu(self.fc1(x))x=self.fc2(x)returnx

加载并打印模型

%pip install torchinfofromtorchinfoimportsummary# 将模型转移到GPU中(模型运行均在GPU中)model=Model().to(device)summary(model)
Requirement already satisfied: torchinfo in D:\Software\anaconda3\envs\pytorch-latest\Lib\site-packages (1.8.0) Note: you may need to restart the kernel to use updated packages. ================================================================= Layer (type:depth-idx) Param # ================================================================= Model -- ├─Conv2d: 1-1 1,792 ├─MaxPool2d: 1-2 -- ├─Conv2d: 1-3 36,928 ├─MaxPool2d: 1-4 -- ├─Conv2d: 1-5 73,856 ├─MaxPool2d: 1-6 -- ├─Linear: 1-7 131,328 ├─Linear: 1-8 2,570 ================================================================= Total params: 246,474 Trainable params: 246,474 Non-trainable params: 0 =================================================================

三、训练模型

1. 设置超参数

loss_fn=nn.CrossEntropyLoss()learn_rate=1e-2opt=torch.optim.SGD(model.parameters(),lr=learn_rate)

2. 编写训练函数

# 训练循环deftrain(dataloader,model,loss_fn,optimizer):size=len(dataloader.dataset)# 训练集的大小,一共60000张图片num_batches=len(dataloader)# 批次数目,1875(60000/32)train_loss,train_acc=0,0# 初始化训练损失和正确率forX,yindataloader:# 获取图片及其标签X,y=X.to(device),y.to(device)# 计算预测误差pred=model(X)# 网络输出loss=loss_fn(pred,y)# 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失# 反向传播optimizer.zero_grad()# grad属性归零loss.backward()# 反向传播optimizer.step()# 每一步自动更新# 记录acc与losstrain_acc+=(pred.argmax(1)==y).type(torch.float).sum().item()train_loss+=loss.item()train_acc/=size train_loss/=num_batchesreturntrain_acc,train_loss

3. 编写测试函数

deftest(dataloader,model,loss_fn):size=len(dataloader.dataset)# 测试集的大小,一共10000张图片num_batches=len(dataloader)# 批次数目,313(10000/32=312.5,向上取整)test_loss,test_acc=0,0# 当不进行训练时,停止梯度更新,节省计算内存消耗withtorch.no_grad():forimgs,targetindataloader:imgs,target=imgs.to(device),target.to(device)# 计算losstarget_pred=model(imgs)loss=loss_fn(target_pred,target)test_loss+=loss.item()test_acc+=(target_pred.argmax(1)==target).type(torch.float).sum().item()test_acc/=size test_loss/=num_batchesreturntest_acc,test_loss

4. 正式训练

epochs=10train_loss=[]train_acc=[]test_loss=[]test_acc=[]forepochinrange(epochs):model.train()epoch_train_acc,epoch_train_loss=train(train_dl,model,loss_fn,opt)model.eval()epoch_test_acc,epoch_test_loss=test(test_dl,model,loss_fn)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)template=('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')print(template.format(epoch+1,epoch_train_acc*100,epoch_train_loss,epoch_test_acc*100,epoch_test_loss))print('Done')
Epoch: 1, Train_acc:14.0%, Train_loss:2.281, Test_acc:21.9%,Test_loss:2.183 Epoch: 2, Train_acc:26.3%, Train_loss:1.995, Test_acc:31.5%,Test_loss:1.866 Epoch: 3, Train_acc:35.9%, Train_loss:1.758, Test_acc:36.2%,Test_loss:1.709 Epoch: 4, Train_acc:41.6%, Train_loss:1.604, Test_acc:42.0%,Test_loss:1.593 Epoch: 5, Train_acc:45.6%, Train_loss:1.499, Test_acc:44.1%,Test_loss:1.533 Epoch: 6, Train_acc:49.2%, Train_loss:1.411, Test_acc:51.0%,Test_loss:1.361 Epoch: 7, Train_acc:52.3%, Train_loss:1.331, Test_acc:51.4%,Test_loss:1.352 Epoch: 8, Train_acc:54.9%, Train_loss:1.264, Test_acc:55.3%,Test_loss:1.249 Epoch: 9, Train_acc:57.4%, Train_loss:1.202, Test_acc:55.3%,Test_loss:1.264 Epoch:10, Train_acc:59.5%, Train_loss:1.146, Test_acc:57.5%,Test_loss:1.209 Done

四、结果可视化

importmatplotlib.pyplotasplt#隐藏警告importwarnings warnings.filterwarnings("ignore")#忽略警告信息plt.rcParams['font.sans-serif']=['SimHei']# 用来正常显示中文标签plt.rcParams['axes.unicode_minus']=False# 用来正常显示负号plt.rcParams['figure.dpi']=100#分辨率fromdatetimeimportdatetime current_time=datetime.now()# 获取当前时间epochs_range=range(epochs)plt.figure(figsize=(12,3))plt.subplot(1,2,1)plt.plot(epochs_range,train_acc,label='Training Accuracy')plt.plot(epochs_range,test_acc,label='Test Accuracy')plt.legend(loc='lower right')plt.title('Training and Validation Accuracy')plt.xlabel(current_time)# 打卡请带上时间戳,否则代码截图无效plt.subplot(1,2,2)plt.plot(epochs_range,train_loss,label='Training Loss')plt.plot(epochs_range,test_loss,label='Test Loss')plt.legend(loc='upper right')plt.title('Training and Validation Loss')plt.show()


五、个人总结

1. 学习目标

本周通过 CIFAR-10 彩色图片识别任务,系统实践了一个完整的深度学习图像分类流程,包括:数据加载与可视化、CNN 网络搭建、模型训练与测试、结果可视化等核心环节。

2. 核心知识点

(1)数据 pipeline

  • 使用torchvision.datasets.CIFAR10自动下载并加载彩色图片数据集(训练集 50000 张,测试集 10000 张,10 个类别)
  • transform=ToTensor()将 PIL 图片转为[C, H, W]的 Tensor 并归一化到[0, 1]
  • DataLoader实现批量加载(batch_size=32)和打乱顺序,shuffle=True仅用于训练集

(2)CNN 网络结构

  • 三个Conv2d + ReLU + MaxPool2d模块构成特征提取部分:通道数变化3 → 64 → 64 → 128
  • 卷积核kernel_size=3(无 padding,尺寸逐层缩小)、池化核kernel_size=2(尺寸减半)
  • torch.flatten(x, start_dim=1)[B, C, H, W]展平为[B, C*H*W]start_dim=1保留 batch 维度
  • 两个全连接层完成分类:512 → 256 → 10
  • 模型总参数量约 24.6 万,是一个轻量级 baseline 网络

(3)训练流程

  • 损失函数CrossEntropyLoss(内含 softmax,故网络最后一层不加激活函数)
  • 优化器SGD,学习率1e-2
  • model.train()/model.eval()切换训练/测试模式
  • 测试时用torch.no_grad()关闭梯度计算,节省显存
  • 训练循环:zero_grad() → backward() → step()三步反向传播

3. 遇到的问题与解决

问题原因解决方法
DataLoader拼写为Dataloader报错类名 L 需大写(驼峰命名)改为DataLoader
super.__init__()报错super是函数,需加括号调用改为super().__init__()
fc2被调用两次、维度不匹配32x10 vs 256x10笔误 + fc1 输出与 fc2 输入维度未对齐只调用一次 fc2,并保证fc1 输出 = fc2 输入
torchinfo未安装第三方库未安装%pip install torchinfo在 Jupyter 中安装

4. 结果分析

训练 10 个 epoch 后的结果:

指标Epoch 1Epoch 10说明
训练准确率14.0%59.5%持续上升,未收敛
测试准确率21.9%57.5%训练/测试差距小,未过拟合
训练损失2.2811.146稳步下降
测试损失2.1831.209稳步下降

观察:

  • 训练和测试准确率曲线接近,说明模型容量适中、没有明显过拟合
  • 10 轮训练准确率仅约 60%,模型还有较大提升空间
  • 损失仍在下降,说明继续训练还能进一步收敛

5. 改进方向

  1. 增加训练轮数:当前损失仍在下降,可增加到 30~50 个 epoch
  2. 更换优化器:用Adam替代SGD,收敛更快
  3. 加入数据增强RandomCropRandomHorizontalFlip等提升泛化能力
  4. 加深网络:增加卷积层或使用nn.Sequential简化结构
  5. 加入 BatchNorm 和 Dropout:加速训练、抑制过拟合
  6. 学习率调度:使用StepLRCosineAnnealingLR动态调整学习率

6. 收获与体会

  • 通过本次实践,完整跑通了一个图像分类项目的全流程,对 PyTorch 的数据加载、模型定义、训练循环有了更直观的理解
  • 踩坑过程中加深了对 Python 类继承(super().__init__())、PyTorch 命名规范、张量维度匹配等基础知识的印象
  • 体会到 CNN 中维度计算的重要性:每一层的输出形状必须与下一层的输入严格对齐,否则前向传播会报错
  • 学会了用torchinfo.summary查看模型结构和参数量,比print(model)更直观