一、实验内容
- 本实验主要涉及了一些深度学习基础知识
- 其中深度学习概述的内容可见视频:https://www.jianguoyun.com/p/Da26fK0QrKKIBhi_0okGIAA
- 实验1.1 pytorch基础练习的教程可见飞书:https://oucaigroup.feishu.cn/wiki/Pnpaw9OUoiahoQkI89xcfwfLnWc
- 实验1.2 螺旋数据分类的教程可见飞书:https://oucaigroup.feishu.cn/wiki/Nbddwe54fiolLFkoslfcdYcAnZe
- 代码练习可以使⽤⾕歌的 Colab(它是⼀个 Jupyter 笔记本环境,已经默认安装好 pytorch,完全在云端运⾏),使用教程:https://www.cnblogs.com/lfri/p/10471852.html
二、实验过程
2.1.代码练习
2.1.1 Pytorch基本操作
(1)定义数据
- 通过不同的方法,PyTorch可以生成标量、一维向量、二维矩阵以及更高维度的张量。除了直接使用
torch.tensor()创建以外,还可以利用torch.zeros()、torch.ones()、torch.randn()、torch.arange()、torch.linspace()等函数来快速构造具有特定值或形状的张量。 - 还有如
new_ones()、rand_like()、randn_like()等方法,它们能够基于已有张量的属性(如大小、dtype、device)来生成新的张量。 - 实验结果如图1~图9。
- 一般定义数据使用torch.tensor。
- tensor是 PyTorch 中的基本数据单元,是数字各种形式的总称。 它不仅能表示各种数值结构,还可以与 GPU 设备无缝切换,为后续的自动求导和模型训练提供统一的数据接口。
![]() |
![]() |
|---|---|
| 图1 定义数据 | 图2 定义一维数组(张量) |
![]() |
![]() |
| 图3 定义二维数组 | 图4 定义多维数组 |
![]() |
![]() |
| 图5 创建空张量 | 图6 创建随机初始化张量 |
![]() |
![]() |
| 图7 创建long型全0张量 | 图8 继承旧tensor属性创建全1张量 |
![]() |
|
| 图9 借用旧tensor形状与属性创建随机数值张量 |
(2)定义操作
PyTorch 支持丰富的数学函数、逻辑判断和线性代数运算。具有如下特点:
- 张量运算可以直接在 GPU 上完成,计算速度快。
- 所有运算都保持自动求导的兼容性,方便后续梯度计算。
- PyTorch 的函数接口设计简洁,与 NumPy 类似,易于上手。
疑点补充:
- 冒号: 表示“取所有“,在行/列切片中可以取完整一行/列。
- 列切片: 常用于提取矩阵中的特定列数据,比如神经网络中的某一特征列。
- 行切片: 通常用于访问矩阵的某一条样本记录。
torch.arange(start, end):会生成一个从start到end-1的整数序列。- 广播机制: 当两个张量的维度兼容时,会自动扩展维度以进行逐元素计算。
- 生成等间隔序列
torch.linspace(3, 8, 20):从 起点 3 到 终点 8(包含) 之间平均分成 20 个点,返回长度为 20 的 1D 张量。和arange的区别:linspace(start, end, steps):指定点数,包含end。arange(start, end, step):指定步长,通常不包含end且受浮点误差影响更大。
实验结果如图10~图23。
![]() |
![]() |
|---|---|
| 图10 输出行、列、尺寸 | 图11 输出总元素个数 |
![]() |
![]() |
| 图12 索引操作 | 图13 列切片 |
![]() |
![]() |
| 图14 行切片 | 图15 生成一个范围张量 |
![]() |
![]() |
| 图16 张量的矩阵乘法 | 图17 取矩阵部分做乘法 |
![]() |
![]() |
| 图18 广播加法 | 图19 矩阵转置(对二维张量两方法等价) |
![]() |
![]() |
| 图20 生成等间隔序列 | 图21 直方图(1000个随机数、100个柱子) |
![]() |
![]() |
| 图22 直方图(1,000,000 个随机数) | 图23 拼接张量 |
2.1.2 螺旋数据分类
准备工作
因为原图片的地址被修改了,所以需要在下载下来的
plot_lib.py中替换旧地址。# 1) 下载绘图工具 !wget -q https://raw.githubusercontent.com/Atcold/NYU-DLSP21/refs/heads/master/res/plot_lib.py -O plot_lib.py # 2) 修补 ziegler.png 的新地址 import re, io with open('plot_lib.py', 'r', encoding='utf-8') as f: txt = f.read() # 把老链接都替换为新地址 new_url = 'https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/ziegler.png' txt = re.sub(r'https://raw\.githubusercontent\.com/Atcold/.+?/res/ziegler\.png', new_url, txt) with open('plot_lib.py', 'w', encoding='utf-8') as f: f.write(txt)
导库并初始化参数
torch.manual_seed(seed)作用是:固定 PyTorch 的 CPU/GPU 随机数(包括torch.randn、层参数初始化等)。这样每次运行可以得到相同的数据、相同的模型初始化,训练曲线和最终边界会更稳定、方便对比。(如图24)
图24 导库并初始化参数与输出
生成螺旋形状的三分类数据集
先创建空的特征矩阵
X和标签Y,然后为每个类别生成从中心向外延伸的螺旋形坐标点,通过sin和cos将极坐标转换为 (x, y) 坐标。每类各有 1000 个点,共 3000 个样本。用
plot_data(X, Y)可视化,结果显示三条颜色不同的螺旋臂交织在一起。(如图25)
图25 生成螺旋形状的三分类数据集
线性螺旋分类
模型由两层全连接层组成,没有激活函数,因此整体仍是线性模型。
训练过程中使用交叉熵损失函数和带 L2 正则化的随机梯度下降优化器,循环 1000 次更新参数。每 100 个 epoch 输出一次当前的损失值和准确率,以观察模型的学习情况。(如图26)

图26 构建线性螺旋分类
激活函数分类
建立并训练包含ReLU 激活函数的两层神经网络用于对螺旋数据进行分类。
与线性模型不同,ReLU 为网络引入了非线性,使其能够学习复杂的弯曲决策边界。训练使用交叉熵损失函数和 Adam 优化器,共进行 1000 次迭代,每 100 次输出一次损失和准确率。(如图27)

图27 使用激活函数对螺旋数据进行分类
2.1.3 想法与解读
(1)PyTorch 基本操作
tensor是一切的起点。无论是一维向量、二维矩阵还是更高维数据,PyTorch 都用统一的 tensor 抽象来承载,并且可以无缝切换到 GPU 上计算。
使用
torch.tensor/zeros/ones/randn/arange/linspace等函数可以很快搭好数据骨架,再通过切片、拼接、广播把形状和维度对齐到想要的样子。值得注意的两点是:第一,我们需要时刻关注 dtype 与 device(例如后面做分类时标签要用
long)。第二,应当固定随机种子与尽量避免歧义的切片写法,能让调试更可复现和可控。
(2)螺旋数据分类
这一部分的核心是:先用极坐标(r, θ)生成三臂螺旋数据并可视化。然后分别训练线性分类器与含 ReLU 的两层神经网络,再比较它们的学习能力与边界形状。
- 线性模型
使用两层线性但没有激活函数,因此从函数复合的角度看仍是整体线性映射。可见不管中间有没有看起来更宽的层,只要没有非线性,模型就只能学到近似线性的决策边界。 - 两层 MLP + ReLU
在两层Linear之间加入ReLU()使得网络具备了非线性表达能力,可以用分段线性的方式逼近复杂边界。可见对螺旋这种高度非线性的分布,这一步是从从不能到能学到的根本性跃迁。
2.2.回答相关问题
2.2.1 AlexNet有哪些特点?为什么可以比LeNet取得更好的性能?
AlexNet的特点是它在结构上比LeNet更深更复杂,首次大规模使用了GPU并行计算、ReLU激活函数、Dropout防止过拟合、局部响应归一化以及重叠池化操作。它采用了五个卷积层和三个全连接层,参数量大但能够捕捉更多复杂特征。
相比之下,LeNet只有两层卷积和两层全连接,处理的图像较小、特征简单。
AlexNet通过更深的网络结构、更大的数据集以及更有效的训练方法,使得模型在特征提取和泛化性能上显著优于LeNet。
比较易懂的相关讲解链接:https://www.bilibili.com/video/BV1RJ4m177hi/
2.2.2 激活函数有哪些作用?
- 激活函数用于引入非线性,使神经网络能够逼近复杂函数关系。如果没有它,整个网络只是线性变换的组合。激活函数还能帮助网络学习到更抽象的特征层级,它也决定了网络的表达能力与收敛速度。
- 常见激活函数包括 Sigmoid、Tanh、ReLU。
快速了解激活函数:https://www.bilibili.com/video/BV1qB4y1e7GJ/
快速了解常见激活函数:https://www.bilibili.com/video/BV1Kc411D7uo/
2.2.3 梯度消失现象是什么?
梯度消失指在反向传播过程中,梯度值逐层变小,导致靠近输入层的参数几乎无法更新。它常出现在使用 Sigmoid 或 Tanh 等饱和函数的深层网络中,因为这些函数在输入较大时导数趋近于 0,结果是网络训练变慢甚至停止学习。
为解决该问题,人们引入了 ReLU 激活函数、残差连接(ResNet) 等方法使梯度能够更有效地传递。
2.2.4 神经网络是更宽好还是更深好?
- “更宽”指增加每层的神经元数量,“更深”指增加层数。 一般来说,更深的网络可以学习更复杂的分层特征,但训练难度更大且容易出现梯度问题。更宽的网络参数多,计算量大且易过拟合。
- 现代架构(如 ResNet、DenseNet)倾向于适度加深并引入跳跃连接,在深度与宽度之间取得平衡。因此是否更好取决于任务规模、数据量及正则化手段。
2.2.5 为什么要使用 Softmax?
Softmax 是一种将任意实数向量映射为概率分布的函数,常用于多分类问题的输出层。它将每个类别的得分通过指数化后归一化,使输出值在 0~1 之间且总和为 1。这样模型的输出可以被直接解释为属于各类别的概率,可以突出最大的预测概率,同时压制其他较小的值,从而更直观地表示模型对各类别的置信度。
此外,Softmax 与交叉熵损失结合使用时能有效指导梯度更新,提高分类性能。
可以对比看一下:
logistic回归:https://www.bilibili.com/video/BV18u4y147ew/
2.2.6 SGD 和 Adam 哪个更有效?
SGD(随机梯度下降)是最基础的优化算法,每次随机选取一小批样本更新参数,虽然计算高效,但对学习率敏感、收敛速度慢,且容易陷入局部最优。
Adam(Adaptive Moment Estimation)结合了动量和自适应学习率的思想。它会自动为每个参数计算一阶与二阶动量,使更新方向更稳定,能在稀疏梯度或非平稳目标函数中表现良好。
Adam 的优势是训练初期收敛快,对超参数不敏感,常用于深层或复杂模型。但Adam 在后期可能过快收敛到次优点,泛化性能略逊于 SGD。 实际应用中,常见策略是先用 Adam 快速预热训练,再切换到 SGD 进行精调,以兼顾速度与精度。
三、问题总结与体会
3.1问题总结
问题1: (应该是在做矩阵计算的时候)运行模型时报错提示数据类型不匹配(expected Float but got Long),导致无法进行矩阵计算。
原因与方案: 这是因为输入张量的数据类型不是浮点型,而是整数类型。可通过在定义或加载数据时加上 .float() 或指定dtype=torch.float 将其转换为浮点类型x = torch.tensor([1,2,3], dtype=torch.float)。
问题2: 绘图函数 plot_model() 报错,提示缺失背景图片 ziegler.png。
原因与方案: 通过修改 plot_lib.py 中的图片 URL,将其更新为新的位置 https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/ziegler.png,问题顺利解决。
问题3: 初次生成数据时,三类样本颜色显示混乱、无法清晰区分。
原因与方案: 检查发现是标签张量 Y 的类型未设置为整型long,导致绘图函数无法正确区分类别。将 Y 定义修改为dtype=torch.long 后,三类螺旋臂在可视化图中颜色区分明显。
3.2体会
这次实验的内容包含了AlexNet、激活函数、Softmax以及优化算法等知识。我发现这些概念之间联系紧密但不太容易理解,比如为什么ReLU能缓解梯度消失、Softmax为什么能把结果变成概率。虽然我现在还没有完全弄懂这些原理,但我已经能感受到它们在神经网络训练中的重要作用。接下来我会多看可视化例子和代码运行过程,试着从实验结果去理解这些理论,让自己慢慢掌握这一部分内容。






















