《软件工程原理与实践》Lab3——卷积神经网络
姓名zgx 学号******姓名和学号zgx******本实验属于哪门课程中国海洋大学25秋《软件工程原理与实践》实验名称实验3卷积神经网络一、实验内容本次实验中的三个实验均来自以下链接实验三https://oucaigroup.feishu.cn/wiki/ZZHiwlpZJiLuNCkpL5ScHeCTnqg实验四https://oucaigroup.feishu.cn/wiki/TWFdw1ecwiBILikSJrecvMX4nOf实验五https://oucaigroup.feishu.cn/wiki/UJ1xwmjVziixTckP1zFcgNgcnqc1. 实验三MNIST数据集分类1.1 基础准备这部分代码主要是在为后面的神经网络训练做准备导入所需要的库、定义模型参数统计函数、设置运行设备三部分。1.2 加载数据MNIST1.2.1 MNIST 数据集并预处理设置图片的大小为28*28, 并设置输出类别数100-9 并加载训练集和测试集。对图片进行预处理 transforms.ToTensor()将图片转换成张量并将像素值缩放到0~1 通过transforms.Normalize()对像素进行标准化并用transforms.Compose()将上述操作组合起来依次执行。1.2.2 显示数据集中的部分图像取出 MNIST 训练集中的前 20 张图片按 4 行 5 列显示出来。1.3 创建网络1.3.1 构建神经网络这部分代码定义了两种模型全连接神经网络首先将每张 28×28 的图片展开为包含 784 个像素值的一维向量然后依次通过两个隐藏层和一个输出层引入ReLU函数进行非线性变换最后通过LogSoftmax输出各类别的对数概率。卷积神经网络直接接收二维图像通过两次卷积提取图像中的笔画、边缘等局部特征并在每次卷积后使用 ReLU 激活和最大池化。将大小从 28×28 逐步缩小为 4×4再将特征展开送入两个全连接层完成分类。这部分主要是为后续训练和比较分类做准备。1.3.2 定义训练和测试函数对每批数据依次执行梯度清零、前向传播、损失计算、反向传播和参数更新。损失计算采用负对数似然损失主要是用于衡量预测结果与真实标签之间的差距。每隔 100 个批次输出一次训练进度和当前批次的损失这样也能方便观察结果。最后是进行测试选择每张图片输出值最大的类别作为预测标签将预测标签与真实标签进行比较累计预测正确的数量同时累加各批次的损失。计算平均损失和分类准确率。1.4 进行训练1.4.1 在小型全连接网络上训练先设置隐藏层有8个神经元随后调用FC2Layer创建全连接模型。开始进行训练。其中的optim.SGD()是带动量的随机梯度下降优化器。有动量可以使参数更新时结合之前积累的更新方向可以更好的减少来回震荡。1.4.2 在卷积神经网络上训练定义两个卷积层各输出 6 个特征通道利用CNN(创建卷积神经网络也同样为CNN设计带动量的随机梯度下降优化器。最后输出结果通过以上对比可以发现含有相同参数的CNN效果要明显优于简单的全连接网络。主要是因为CNN中的“卷积”和“池化”可以更好的挖掘图像中的信息帮助神经网络更精准的识别。1.5 打乱像素顺序后再次在两个网络上训练与测试如果把图像中的像素打乱顺序“卷积”和“池化”会难以发挥作用。再次实验观察两个神经网络上的结果如何。1.5.1 随机打乱像素后图像的形态把像素打乱就是把图片中的28\×28784 个像素随机打乱然后组成的图像。1.5.2 重新定义训练与测试函数与之前的测试函数没多大区别只是对数据进行了打乱顺序操作。1.5.3 在小型全连接网络上训练1.5.3 在卷积神经网络上训练通过两次实验来看全连接网络的性能基本没有发生变化但是卷积神经网络的性能明显的下降。这主要因为卷积神经网络的核心“卷积”和“池化”需要利用像素之间的局部关系打乱之后这些像素关系就无法得到利用了也大大影响了卷积神经网络的性能。2. 实验四CIFAR10 数据集分类2.1 基础准备这部分代码完成了模型训练前的数据准备工作。先导入所需的库并根据 GPU 是否可用选择计算设备。随后将图片转换成张量把像素值调整到 −1 到 1 的范围再分别加载 CIFAR-10 的训练集和测试集使用 DataLoader 按批次读取数据。训练时打乱样本顺序测试时保持原有顺序。最后定义类别名称方便查看预测结果。展示CIFAR10 数据集里面的一些照片这里的迭代器没有 .next() 方法需要使用 Python 内置的 next() 函数获取一批数据。将原语句改成images, labels next(iter(trainloader))即可2.2 定义网络、损失函数和优化器这部分代码搭建了一个包含两个卷积层、两次最大池化和三个全连接层的分类网络。卷积层负责提取图片特征池化层负责缩小特征图、减少计算量全连接层则综合这些特征输出十个类别的得分。图片经过卷积和池化后每张得到 16 张大小为 5×5 的特征图需要先展开成 400 个特征值再送入全连接层。最后代码将模型放到指定设备上并设置交叉熵损失函数和 Adam 优化器为后续计算损失和更新参数做好准备。2.3 训练网络这部分代码实现了模型的训练过程共训练 10 轮每轮按批次读取训练图片和真实标签。每个批次先清空之前的梯度再将图片输入模型计算预测结果与真实标签之间的损失随后通过反向传播计算梯度由优化器更新模型参数。反向传播负责计算梯度而优化器负责真正调整参数两者共同完成模型的学习。训练过程中还会定期输出当前批次的损失方便观察训练进度和损失变化。2.4 训练结果从测试集中取出8张图片这部分也会报错解决方法同2.1中的图片展示方法把图片输入模型看看CNN把这些图片识别成了什么可以看出有部分图片识别错了。网络在整个数据集上的表现3. 实验五VGG16对CIFAR10分类3.1 定义dataloader这部分代码在原有的数据加载流程中加入了数据增强并分别设置训练集和测试集的预处理方式。训练图片经过随机裁剪和水平翻转后同一张图片在不同轮次中可能呈现不同的位置和朝向有助于模型适应图片的变化。测试图片只进行张量转换和标准化保证评估过程稳定。同时代码调整了各通道的标准化参数并将训练和测试的批次大小都设为 128。也就是 transform 负责处理单张图片而 DataLoader 负责把图片和标签组织成批次两者共同为模型提供训练和测试数据。3.2 VGG网络定义模型实现代码这部分代码通过配置列表搭建了一个 VGG 风格的卷积神经网络列表中的数字表示卷积层的输出通道数M 表示最大池化。相比之前的简单网络它增加了卷积层和通道数并在每个卷积层后加入批量归一化和 ReLU以提取更丰富的特征并帮助稳定训练。图片经过五次池化后由 32×32 缩小到 1×1最终得到 512 个特征再通过全连接层输出十个类别的得分。代码中需要修改一下语句self.features self._make_layers(self.cfg)否则会导致变量名不一致。代码中需要修改一下语句self.classifier nn.Linear(512, 10)将2048改为512适配后续的代码参数否则无法运行成功。初始化一些参数使用交叉熵损失函数衡量预测结果与真实标签之间的差距再设置学习率为 0.001 的 Adam 优化器用于后续更新模型参数。对比之下损失函数和优化方式是保持不变的。3.3 网络训练训练的代码和之前完全一样3.4 测试验证准确率测试代码也与之前完全一样根据结果可以发现一个简化版的VGG网络就可以将准确率从63%大幅度提升到82.79%二、问题总结与体会1. DataLoader 中shuffle 取不同值有什么区别shuffle决定读取样本时是否打乱顺序。shuffleTrue每轮训练重新打乱样本顺序使不同 batch 的样本组合发生变化减少固定排列顺序对训练的影响。shuffleFalse按数据集原有顺序读取每轮的样本顺序基本一致。训练集通常设为True测试集通常设为False。训练时打乱数据有助于模型学习测试时不更新参数一般不需要打乱也方便对应预测结果和原始样本。2. transform 中取不同值有什么区别Normalize(mean, std)是按通道调整像素数值改变输入数据的中心位置和数值尺度。其计算公式为x′x−meanstd x\frac{x-\text{mean}}{\text{std}}x′stdx−mean例如Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))中两组数分别对应 RGB 三个通道的均值参数和标准差参数。输入已经在[0,1]时变换后落在[-1,1]。这不代表处理后的实际均值一定为 0、标准差一定为 1。只有使用数据本身的相应统计量才有这样的标准化效果。3. epoch 和 batch 的区别是什么batch一次送入模型的一批样本。batch size这一批包含多少个样本。epoch整个训练集被完整训练一遍。例如训练集有 50,000 张图片batch_size100那么1 个 epoch500 个 batch 1\text{ 个 epoch}500\text{ 个 batch}1个epoch500个batch通常每处理一个 batch就进行一次前向传播、损失计算、反向传播和参数更新。因此训练 10 个 epoch通常会更新参数 5,000 次。4. 1×1 卷积和 FC 有什么区别主要起什么作用1×1 卷积虽然只覆盖一个空间位置但会使用该位置的所有输入通道对这些通道进行加权组合。例如输入为64×32×32使用 16 个 1×1 卷积核步长为 1、不填充时输出为64×32×32→16×32×32 64\times32\times32\rightarrow16\times32\times3264×32×32→16×32×32空间尺寸不变通道数由 64 变成 16。对比项1×1 卷积展平后的全连接层 FC处理范围每个空间位置上的所有通道展平后的全部输入特征空间结构保留二维位置结构通常输出一个特征向量参数共享不同空间位置使用同一组权重不同输入位置通常使用不同权重常见作用通道融合、升降维、残差分支对齐综合全部特征输出分类得分1×1 卷积主要有三个用途调整通道数使特征符合后续网络的输入要求。融合不同通道的信息学习新的特征组合。先降低通道数再进行较大的卷积减少后续计算量。它本身仍是线性变换接上 ReLU 等激活函数后才能引入非线性。5. Residual Learning 为什么能够提升准确率普通网络直接学习目标映射 H(x)残差学习则让网络学习F(x)H(x)−x F(x)H(x)-xF(x)H(x)−x最后通过跳跃连接得到yF(x)x yF(x)xyF(x)x也就是保留输入特征再学习需要补充或修正的部分。它主要解决深层网络难以训练的问题保留原有信息。输入可以通过跳跃连接直接传到后面不必完全依赖多层变换。方便梯度传播。反向传播多了一条直接路径有助于减轻深层网络的梯度传播困难。缓解网络退化。如果新增的层暂时学不到有效改进可以让残差接近 0使整个模块接近恒等映射避免新增层必须重新学会“保留输入”。因此残差结构使更深的网络更容易优化从而有机会学到更有效的特征并提高准确率。6. 代码练习二中的网络与 LeNet 有什么区别代码练习二采用的是 LeNet 风格的卷积神经网络由两层卷积、两次最大池化和三层全连接组成用于 CIFAR-10 图像分类。输入图片为三通道的 32×32 彩色图像数据在网络中的变化如下表中省略 batch 维度网络操作输出形状输入图片3×32×32第一层 5×5 卷积接 ReLU6×28×28第一次 2×2 最大池化6×14×14第二层 5×5 卷积接 ReLU16×10×10$第二次 2×2 最大池化16×5×516×5×516×5×5展平400第一层全连接接 ReLU120第二层全连接接 ReLU84第三层全连接10两者的共同点是采用局部连接和卷积权重共享来提取图像特征再通过后续网络完成分类。它们都能直接从图像中学习特征不需要手工设计特征。主要区别如下激活函数不同。早期网络使用 sigmoid 类的平滑饱和激活函数本实验在两层卷积和前两层全连接之后使用 ReLU。ReLU 计算简单正值区域不会因激活函数饱和而使梯度趋近于零有利于训练。下采样方式不同。本实验明确使用MaxPool2d(2, 2)每次取局部区域的最大值使特征图的高、宽减半。1989 年网络的下采样由其局部连接和步长设计实现不能直接理解为本代码中的独立最大池化层。具体层结构和参数配置不同。本实验的卷积通道数为3→6→16全连接层为400→120→84→10。它沿用了 LeNet 系列的设计思路但不是对 1989 年网络结构的原样复现。训练配置不同。本实验使用交叉熵损失和 Adam 优化器学习率为0.001。最后一层直接输出 10 个类别的原始得分由CrossEntropyLoss完成相应计算因此不需要在网络末尾额外添加 Softmax。7. 卷积后 feature map 尺寸变小如何应用 Residual Learning关键要求是参与相加的两个张量必须具有相同的形状包括通道数、高和宽。尺寸一致时可以直接相加yF(x)x yF(x)xyF(x)x尺寸不一致时需要先调整捷径分支yF(x)P(x) yF(x)P(x)yF(x)P(x)其中P(x) 是用于匹配形状的变换。情况处理方法高、宽不变通道数改变用 1×1 卷积调整通道数高、宽缩小例如减半用带步长的 1×1 卷积同时调整空间尺寸和通道数因未填充卷积而缩小如32→28可修改主分支的 padding 保持尺寸或对捷径分支进行匹配的裁剪等处理例如主分支把输入从64×32×32变成128×16×16捷径分支可以使用nn.Conv2d(64, 128, kernel_size1, stride2)。这样两条分支的输出都是128×16×16可以逐元素相加。8. 有什么方法可以进一步提升准确率可以从以下方面改进方法具体做法与目的数据增强训练时加入适度的随机裁剪、水平翻转减少对固定图像形式的依赖调整学习率前期使用合适的学习率后期逐步降低让参数调整更细调整训练轮数训练不足时增加 epoch出现过拟合时不再单纯延长训练改进网络结构适当增加卷积通道或深度引入残差连接提高特征提取能力加入 BatchNorm改善网络的训练稳定性控制过拟合使用权重衰减、适当的 Dropout 或早停检查数据处理确保标签正确、标准化一致测试时调用model.eval()首先应同时观察训练集和验证集的损失、准确率再决定调整方向训练集和验证集都表现不好重点检查是否训练不足、学习率不合适或模型能力不足。训练集很好验证集明显较差重点考虑过拟合增加数据增强和正则化。训练过程中损失反复大幅波动优先检查学习率和输入数据。心得体会通过本次实验我学会了在 Colab 中使用 PyTorch 完成 MNIST 和 CIFAR-10 图像分类熟悉了数据预处理、模型搭建、训练和测试的基本过程也弄清了卷积层、池化层和全连接层各自的作用对图片如何经过网络得到分类结果有了更具体的认识。通过简单卷积网络和 VGG 网络的对比我进一步理解了不同网络结构在特征提取方式上的差异。同时也认识到图像分类效果不仅与网络结构有关数据增强、学习率和训练轮数等设置也会影响结果不能只靠增加网络层数来提高准确率。这次实验让我逐渐能够把代码中的操作与所学原理联系起来对卷积神经网络有了更清楚的理解也学会了在遇到维度不匹配等问题时沿着数据在网络中的变化过程查找原因。