多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

实验2:深度学习基础(PyTorch 基础与螺旋数据分类)

实验2:深度学习基础(PyTorch 基础与螺旋数据分类) 作者Morwen_OUC项目内容作者昵称Morwen_OUC本实验属于哪门课程中国海洋大学《软件工程原理与实践》实验名称实验2深度学习基础完成日期2026年10月8日一、实验内容本次实验是深度学习的入门实践包含两部分①在 Google ColabJupyter Notebook云环境中完成3.1 PyTorch 基础练习与3.2 螺旋数据分类两个代码练习运行代码、观察结果并截图②思考并回答六个深度学习基础问题。运行环境说明代码在 Colab/Jupyter 笔记本环境中运行使用 PyTorch 框架。设备由torch.device(cuda:0 if torch.cuda.is_available() else cpu)自动判断本次在 Colab 中通过“代码执行程序 → 更改运行时类型”选择了T4 GPU 运行时程序实际输出device: cuda:0若在无 GPU 的环境运行则自动回退为cpu同一份代码无需修改、结果相同仅 GPU 速度更快。为保证结果可复现两个练习均设置了随机数种子seed 12345。第一部分代码练习3.1 PyTorch 基础练习PyTorch 是一个提供GPU 加速张量计算与基于自动微分的深度神经网络搭建两大核心能力的 Python 库而张量Tensor是其中最基础的数据结构——它是标量、向量、矩阵乃至高维数组的统称。1张量的创建分别用torch.tensor、torch.ones、torch.empty、torch.rand、torch.zeros、torch.randn等创建标量、向量、矩阵与三维张量运行结果如下图 3.1-1 用 tensor / ones 创建标量、向量、矩阵与三维张量方括号嵌套层数即维数图 3.1-2 empty / rand / zeros / ones / randn 等不同初始化方式的输出个人理解torch.tensor(666)是 0 维标量torch.tensor([1,...,6])是 1 维向量torch.ones(2,3)、torch.ones(2,3,4)分别是 2 维矩阵和 3 维张量方括号的嵌套层数直观反映了张量的维数。几种初始化方式有本质区别不能混用torch.empty只分配内存、不初始化打印出来的是内存中的残留值极小的科学计数法数字并不是随机数torch.rand生成 [0,1) 区间上的均匀分布随机数torch.randn生成均值 0、方差 1 的标准正态分布随机数因此会出现负数torch.zeros/ones默认是float32指定dtypetorch.long后输出整数没有小数点。实际做分类标签时通常用整型张量。2张量的索引、矩阵乘法与广播构造矩阵后练习形状查询、按索引取值、矩阵乘法和广播broadcasting运算图 3.1-3 形状查询size / numel与按索引取值图 3.1-4 矩阵乘法与广播加法的输出个人理解size(0)返回行数、size(1)返回列数numel()返回元素总个数一个 2×4 矩阵numel()为 8。索引规则与 NumPy 一致m[0,2]取第 0 行第 2 列n[0]取一整行matrix[:,0]用冒号取一整列。m[0] matrix是矩阵乘法一个 1×4 的行向量乘 4×2 的矩阵得到 1×2 结果[49, 47]手算 1×22×53×34×749 与之一致。注意矩阵乘和*逐元素乘完全不同。m torch.randn(2,4)中两个形状相同的张量逐元素相加PyTorch 还支持形状不同时的广播机制自动扩展这在给数据批量加偏置、加噪声时非常方便。3形状变换、等差序列与大规模求和练习reshape/view变形、linspace等距采样以及大矩阵运算图 3.1-5 reshape 变形、linspace 等距采样与百万级矩阵求和图 3.1-6 用 view(1, -1) 把 2×4 矩阵展平为 1×8个人理解view(1,-1)/reshape在不改变元素总数和顺序的前提下改变张量形状-1表示该维度由系统自动推算2×4 的矩阵被展平成 1×8。view要求张量在内存中连续reshape更通用必要时会自动复制。torch.linspace(3,8,steps20)在闭区间 [3,8] 上等距取 20 个点相邻两点间距为 (8-3)/19≈0.263与输出吻合常用于生成横坐标。torch.ones(1000,1000).sum()得到 100 万体现了张量批量运算代替显式循环的高效。3.2 螺旋数据分类本练习用神经网络对三类呈螺旋形交织分布的二维数据进行分类通过对比纯线性模型与带 ReLU 激活的两层网络直观感受非线性激活函数的决定性作用。1初始化参数与构造数据首先导入库、由torch.cuda.is_available()自动判断运行设备固定随机种子并设置每类样本数 N1000、特征维度 D2、类别数 C3、隐层单元数 H100。在 Colab 选择 T4 GPU 后设备输出cuda:0图 3.2-1 设备判断T4 GPU 下为 cuda:0、随机种子与超参数初始化随后按螺旋参数方程生成 N×C3000 个样本特征矩阵X形状为 [3000,2]标签向量Y形状为 [3000]图 3.2-2 按螺旋参数方程构造数据X 为 [3000,2]、Y 为 [3000]用plot_data可视化后可以看到红、蓝、黄三类样本各自旋出一条螺旋臂、彼此缠绕是典型的线性不可分数据图 3.2-3 三类螺旋数据的分布线性不可分2线性模型分类无激活函数 SGD先搭建一个只有两个线性层、中间没有任何激活函数的模型用交叉熵损失和 SGD 优化器训练 1000 轮。损失约从 1.11 降到 0.86但准确率最终只停在 0.504图 3.2-4 线性模型训练日志损失持续下降但准确率始终在 0.50 附近徘徊训练中模型对每个样本输出 3 个类别原始分数y_pred形状为 [3000,3]用torch.max(y_pred, 1)沿类别维度取最大分数的下标即为预测类别下图张量上的devicecuda:0也表明计算发生在 GPU 上图 3.2-5 查看模型输出[3000,3] 的类别分数与 argmax 得到的预测类别画出决策边界可以看到线性模型只能用几条直线把平面切成三个扇形区域无法弯曲地贴合螺旋因此准确率最高只有50% 左右图 3.2-6 线性模型的决策边界只能是直线约 50% 准确率3两层神经网络分类加入 ReLU Adam在两个线性层之间加入一个ReLU 非线性激活函数并把优化器换成 Adam其余代码完全不变。损失快速降到 0.18准确率提升到 0.949图 3.2-7 两层 ReLU 网络训练日志损失降到 0.18、准确率提升到 0.949加入 ReLU 后决策边界变得可以任意弯折几乎完美地贴着三条螺旋臂把三类区域分开最终准确率达到94.9%图 3.2-8 两层 ReLU 网络的决策边界非线性、贴合螺旋约 95% 准确率个人想法与解读准确率从 50% 跃升到 95% 的根本原因是 ReLU 带来的非线性而不是优化器。数学上两个线性层之间若没有非线性激活Linear→Linear等价于一个线性层两次线性变换的矩阵相乘仍是线性变换所以第一个模型本质上只是一条直线分类器天然无法处理螺旋数据。ReLU 让网络能够把多条直线边界弯折拼接成复杂的非线性区域。需要特别指出教程在第二个模型里同时改了两处加入 ReLU、把 SGD 换成 Adam因此这是两个变量一起变化的对比不能把准确率的提升简单归功于 Adam。若要严谨比较 SGD 与 Adam应当做只换优化器、网络结构不变的控制变量实验。关于输出含义模型对每个样本输出 3 个原始分数logitsy_pred形状为 [3000,3]torch.max(y_pred,1)沿类别维度取最大值其下标predicted就是预测类别等价于 Softmax 后取概率最大的类。训练循环中zero_grad()→backward()→step()三步分别对应清空上一轮梯度→反向传播算梯度→更新参数顺序不能颠倒且每轮必须先清梯度否则梯度会跨轮累加。第二部分思考题回答问题一AlexNet 有哪些特点为什么可以比 LeNet 取得更好的性能AlexNet2012 年Krizhevsky、Sutskever、Hinton相比 LeNet-51998 年的主要特点网络更深、更宽、参数规模更大。AlexNet 含 8 个可训练层5 个卷积层 3 个全连接层约 6000 万个参数而 LeNet-5 仅 2 个卷积层 3 个全连接层约 6 万个参数。更深的卷积堆叠可以逐层提取边缘→纹理→部件→物体的层次化特征。用 ReLU 取代 sigmoid/tanh 作为激活函数。ReLU 在正区间梯度恒为 1显著缓解了深层网络的梯度消失训练速度大幅提升这是深层网络训得动的关键。使用 Dropout 随机失活全连接层失活率 0.5做正则化训练时随机屏蔽部分神经元强制网络学习更鲁棒的特征抑制过拟合。大量数据增强对 ImageNet 图像做随机裁剪、水平翻转、颜色扰动等等价于扩充训练集提升泛化能力。用两块 GPU 并行训练并采用重叠池化overlapping pooling、局部响应归一化LRN后被证明作用有限等技巧。在大规模数据集 ImageNet约 120 万张图、1000 类上训练而 LeNet 面向的是 28×28 灰度、仅 10 类的 MNIST 手写数字。为什么性能更好表达能力上更深的结构 ReLU 非线性使其能拟合从简单数字到复杂自然图像的高度非线性映射可训练性上ReLU GPU 算力让这种大网络在工程上真正训练得起来LeNet 时代受限于 tanh 梯度消失和算力网络做不深泛化能力上Dropout 数据增强 大数据有效缓解了大网络的过拟合。本质上是算法ReLU/Dropout、算力GPU、数据ImageNet三者同时成熟的结果它也直接引爆了本轮深度学习浪潮。问题二激活函数有哪些作用引入非线性最核心作用。如果不使用激活函数无论堆叠多少层线性层整体都等价于一个线性变换线性函数的复合仍是线性的网络只能表达线性关系。加入非线性激活后网络才具备拟合复杂非线性函数的能力这正是万能近似定理成立的前提。本次实验中线性模型对螺旋数据只有 50% 准确率、加 ReLU 后达到 95%就是最直观的证明。模拟生物神经元的激活/抑制机制。它决定一个神经元接收输入后以多大程度向下一层传递信号类似阈值点火。特定激活函数还承担额外功能ReLU 及其变体通过保持正区间梯度为 1 来缓解梯度消失、加速收敛Sigmoid 把输出压缩到 (0,1)可表示概率或门控Softmax 把一组分数归一化为类别概率分布Tanh 使输出零中心化有时利于优化。一句话总结激活函数是神经网络非线性的来源没有它再深的网络也退化成线性模型。问题三梯度消失现象是什么神经网络通过反向传播、依据链式法则把损失对每一层参数的梯度逐层相乘来更新权重。当使用 sigmoid、tanh 等饱和激活函数时它们的导数最大值很小sigmoid 的导数最大仅 0.25网络一旦较深梯度在从输出层向输入层逐层连乘的过程中就会指数级衰减、趋近于 0导致靠近输入的浅层权重几乎得不到更新、网络收敛极慢甚至无法训练这就是梯度消失Vanishing Gradient。与之相对的是梯度连乘后指数级增大、训练发散的梯度爆炸。常用缓解办法包括改用ReLU / LeakyReLU / GELU等在正区间梯度不衰减的激活函数使用**批归一化Batch Normalization**稳定各层输入分布采用残差连接ResNet为梯度提供直通的高速公路使用Xavier / He 等合理的权重初始化早期 Hinton 提出的逐层预训练也是为缓解该问题。问题四神经网络是更宽好还是更深好没有绝对答案需要权衡但现代深度学习的总体经验是适当加深比单纯加宽更高效。更宽增加单层神经元数单层容量增大、梯度路径短不易梯度消失、易于并行但对边缘→纹理→部件→物体这类层次化结构的表达效率低参数利用率差要达到同等表达能力可能需要指数级增多的神经元且更容易过拟合。更深增加层数能通过特征的逐层复用与组合用少得多的参数表达高度复杂的函数参数效率和泛化能力通常更好但网络过深会带来梯度消失/爆炸、退化层数增加反而效果变差、难训练、难调参等问题需要 BN、残差连接等技术支撑且存在收益递减点。实践中应让宽度与深度相互配合并用正则化和验证实验来选择。本次实验恰好给出启示隐层宽度固定为 H100 时线性堆叠等效单层只有 50% 准确率而加入一层 ReLU、引入真正的非线性层次后立刻达到 95%——在这个例子里决定成败的不是宽度而是非线性与层次。问题五为什么要使用 Softmax在多分类任务的输出层网络直接给出的原始分数logits是任意实数可正可负、不归一化无法直接解释为概率。Softmax通过对每个分数取指数再归一化把它们变成一组非负、且总和为 1 的概率从而给出可解释的类别概率分布每个输出即属于该类的置信度与交叉熵损失天然配套交叉熵衡量预测概率分布与真实标签 one-hot 分布的差异且 Softmax 交叉熵对 logits 的梯度形式非常简洁预测概率 − 标签训练稳定可微、可反向传播直接取最大值的argmax操作不可微无法用于训练而 Softmax 是光滑函数既保留了各类别的相对大小信息又能持续提供梯度指数运算会放大最高分、抑制其他分数使分类结果更明确。需要注意PyTorch 的nn.CrossEntropyLoss内部已经集成了 LogSoftmax因此训练时直接对原始 logits 求损失即可不要再手动加一次 Softmax只有在推理、需要输出概率时才显式调用 Softmax而取预测类别用argmax与取概率最大等价。问题六SGD 和 Adam 哪个更有效二者各有适用场景不能一概而论。SGD随机梯度下降直接沿负梯度方向更新原理简单、内存占用小精调学习率并配合动量后往往能收敛到更平坦的解、泛化性能更好缺点是对学习率敏感、所有参数共用一个学习率在鞍点、稀疏梯度和非凸高维曲面处收敛较慢比较依赖人工调参和学习率衰减策略。Adam结合了动量一阶矩与自适应学习率二阶矩为每个参数自动估计合适的步长因此收敛快、对超参数不敏感、省调参在稀疏梯度和大多数工程任务上表现稳健常作为新项目的默认首选缺点是在部分任务上其最终泛化精度可能略逊于精心调参的 SGD且后期可能因学习率问题在最优点附近震荡。结合本次实验线性模型用 SGD 得到 50%、ReLU 网络用 Adam 得到 95%但如前所述两处对比同时改变了激活函数和优化器准确率差异主要来自 ReLU 而非 Adam。工程上一般先用 Adam 快速验证模型是否有效若追求极致的泛化性能再尝试精调的 SGD Momentum。二、问题总结与体会遇到的问题与解决torch.empty的输出看不懂。起初以为它和rand一样生成随机数查文档后明白empty只分配内存、不初始化打印的是内存残留值需要随机初始化时应使用rand/randn。反向传播前忘记zero_grad()。PyTorch 的梯度默认会累加因此每个 epoch 反向传播前必须先清空上一轮梯度否则梯度会跨批次叠加导致更新错误通过对照教程理解了清零→反传→更新三步的必要性。绘图库需要先下载。教程的plot_data、plot_model来自第三方绘图脚本plot_lib.py必须先运行笔记本最前面的!wget下载格最初若跳过这一格直接画图会报“找不到模块 plot_lib”补运行下载格后即可正常显示数据散点图与决策边界图。设备的自动判断。在 Colab 的“代码执行程序 → 更改运行时类型”里选择 T4 GPU 后程序输出device: cuda:0张量上也带有devicecuda:0标记若不选 GPU 则会自动回退为cpu。这让我理解到设备是由torch.cuda.is_available()自动判断的同一份代码无需修改即可在 CPU/GPU 间切换固定随机种子后结果可以复现。收获与体会系统掌握了 PyTorch 张量的创建、索引、矩阵运算、广播与形状变换理解了 Tensor 作为深度学习通用数据结构的地位完整走通了准备数据→定义模型→指定损失与优化器→循环训练前向、算损失、清梯度、反传、更新→可视化决策边界的深度学习最小闭环最深刻的体会是非线性激活函数的决定性作用仅仅增加一个 ReLU决策边界就从无能为力的直线变成贴合螺旋的复杂曲线准确率从 50% 跃升到 95%这比单纯看书要直观得多通过回答六个思考题把 AlexNet、激活函数、梯度消失、网络宽深、Softmax、优化器等知识点串成了体系也学会了用控制变量的眼光审视教程中的对比实验。对课程的建议建议在代码练习之外增加一两组控制变量对比如固定网络只换 SGD/Adam、固定优化器只换有无 ReLU并让大家把损失/准确率曲线画成折线图能更严谨地归因各因素的作用也能加深对实验设计方法的理解。
返回列表