PyTorch 入门与感知机实验手记


一、实验内容

二、实验过程

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) :会生成一个从 startend-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,然后为每个类别生成从中心向外延伸的螺旋形坐标点,通过 sincos 将极坐标转换为 (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 激活函数有哪些作用?

  • 激活函数用于引入非线性,使神经网络能够逼近复杂函数关系。如果没有它,整个网络只是线性变换的组合。激活函数还能帮助网络学习到更抽象的特征层级,它也决定了网络的表达能力与收敛速度。
  • 常见激活函数包括 SigmoidTanhReLU

快速了解激活函数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/

softmax回归https://www.bilibili.com/video/BV1Cw411C71D/

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为什么能把结果变成概率。虽然我现在还没有完全弄懂这些原理,但我已经能感受到它们在神经网络训练中的重要作用。接下来我会多看可视化例子和代码运行过程,试着从实验结果去理解这些理论,让自己慢慢掌握这一部分内容。


Author: linda1729
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source linda1729 !
评论
  TOC