PyTorch的基础模块
我们来用一种更概念化和比喻性的方式来介绍 PyTorch 的核心基础模块 可以把搭建和训练一个神经网络想象成组装和启动一台复杂的机器
1. torch.Tensor:核心构件与原材料
PyTorch 世界的原子,即 张量 可以把它想象成一种智能的、多维的数字网格或积木块。所有的数据,无论是输入图像、文本向量,还是模型内部的参数,最终都会被表示成这种形式
核心特点
-
多维性
它可以是0维(一个数字)、1维(一个向量)、2维(一个矩阵)、3维(如一张彩色图片)甚至更高维度
-
GPU 加速能力
它是连接 CPU 和 GPU 的桥梁
可以简单地命令这个“构件”移动到 GPU 上,之后所有对它的计算都会享受 GPU 带来的惊人加速
-
自动求梯度
这是它最优秀的设计 它能记住自己是如何被计算出来的(即它的“血缘关系”或“家谱”),为后续的自动微分系统提供支持
2. torch.autograd:自动化的物理引擎与反馈系统
PyTorch 的核心物理引擎:自动求导,它赋予了 Tensor 记忆和反思的能力
工作原理
使用 tensor进行计算时,例如数据通过神经网络,autograd会自动构建一个计算图,记录所有操作的依赖。当需要更新模型时,autograd可据此追溯,计算每个参数的对误差 的梯度
核心价值
避免手动求导的繁杂工作,让开发者只需关注模型的设计,即前向传播的计算,而 autograd 来自动处理反向传播的流程
3. torch.nn:模型的蓝图与零件库
构建神经网络模型的核心工具箱,内部又包含几个关键部分:
-
nn.Module
-
这是所有神经网络模型的设计蓝图或基类 所有要构建的模型,无论是简单的线性回归还是复杂的巨型网络,都必须继承这个基类
-
作用
它提供了一个标准的框架,可以方便地管理模型的所有 “零件”(如网络层和参数),并定义了数据如何从输入流向输出(即前向传播的路径)
-
-
网络层 Layers
-
工具箱里预先制作好的、标准化的 “积木块”
例如 nn.Linear (全连接层), nn.Conv2d (卷积层), nn.LSTM (循环神经网络层) 等
-
作用
无需从最底层的数学原理开始构建,直接拿出这些标准零件,像搭积木一样将它们堆叠和连接起来,就能快速构建出强大的网络结构
-
-
激活函数
- 为神经网络注入 “非线性” 能力的 “活力催化剂”,例如 ReLU, Sigmoid, Tanh
- 作用 如果没有它们,再深的神经网络也只相当于一个简单的线性模型 激活函数允许模型学习和拟合远比直线复杂得多的复杂模式和关系 而为什么要使用 激活函数将模型引向非线性结构,请看此处
-
损失函数 Loss Functions
-
用于评估模型的性能和计算误差值
例如 nn.MSELoss (均方误差损失), nn.CrossEntropyLoss (交叉熵损失)
-
作用
它负责衡量模型当前的预测结果与真实目标之间的差距 损失值是整个模型学习和优化的根本依据,损失越大,说明模型表现越差
-
4. torch.optim:模型的优化器与调校工具
-
负责更新和优化模型参数的 “智能调校引擎”,它实现了各种优化算法。
-
工作原理
- 它接收模型中所有需要学习的参数,并从 autograd 系统那里获取每个参数的梯度
- 然后,它根据一套特定的规则(例如 SGD、Adam、RMSProp 等算法)来微调这些参数,目标是让下一次计算出的损失值变得更小
-
作用
将 “计算梯度” 和 “更新参数” 这两个步骤解耦开来,只要选择一个合适的引擎(优化器),它就会自动地、迭代地改进你的模型
5. torch.utils.data:高效的数据供给系统
负责处理和供给数据的 “后勤部门”,主要由两个部分组成:
-
Dataset
-
“数据仓库”的标准化接口
它负责告诉 PyTorch 数据集有多大,以及如何获取其中任何一个数据样本(例如,一张图片和它对应的标签)
-
作用
将你的原始数据(无论存储在硬盘的哪个角落,格式如何)包装成一个 PyTorch 能够理解的标准格式
-
-
DataLoader
-
自动化数据传送带
-
作用
它从 Dataset “仓库” 中取出数据,并自动完成一系列后勤工作,包括:
- 批量处理 Batching: 一次性给模型喂送一小批数据,而不是一个一个地喂
- 数据打乱 Shuffling: 在每个训练周期开始时打乱数据顺序,防止模型学到数据的排列顺序
- 并行加载 Multiprocessing: 使用多个CPU核心在后台预先加载数据,避免GPU在训练时因等待数据而 “挨饿”
-
总结:整个流程
- 使用 torch.utils.data (后勤部门) 来准备和输送数据
- 这些数据被转换成 torch.Tensor (核心构件)
- Tensor 流入用 torch.nn (蓝图与零件库) 搭建好的模型中进行计算
- autograd (物理引擎) 默默记录下整个计算过程
- 模型的输出与真实标签通过 torch.nn 中的损失函数计算出误差
- 命令 autograd 计算所有参数的梯度(误差归因)
- 最后,torch.optim (调校引擎) 根据梯度来更新模型中的参数,完成一轮学习
而这个流程会不断循环,直到到达规定的Epoch
PyTorch框架的核心:模型/算法的构建,而不是数据的处理
PyTorch的核心还是模型/算法的构建,而不是数据的处理,数据处理使用的是数据分析三剑客+seaborn等其他高级库
在典型的机器学习/深度学习工作流中,各个库所扮演的核心角色:
Pandas, NumPy, Matplotlib负责数据的预处理PyTorch负责神经网络的搭建
可以把整个流程想象成一个汽车制造的流水线,每个库都是一个高度专业化的车间:
1. 原材料检验与加工:数据预处理
在这个阶段,数据就像刚运来的各种原材料(铁矿石、橡胶、玻璃等),它们可能混杂、有缺陷、尺寸不一,需要对它们进行检验和初步处理
-
Pandas (pd.DataFrame)
加载和清洗非结构化/表格化数据
- 从各种文件(CSV, Excel, SQL)中读取数据
- 处理缺失值 (.fillna(), .dropna())
- 处理不同数据类型(文本、日期、类别)
- 进行初步的数据探索 (
.describe(), .info(), .value_counts()) - 就像一个多功能的质检和清洗车间
-
NumPy (np.ndarray)
数值计算
- 当数据被清洗成纯数值形态后,NumPy 提供了强大的数学和线性代数运算能力
- 它是 Pandas 和 Scikit-learn 的底层计算基石
- 就像一个高精度的切割和塑形车间
-
Matplotlib / Seaborn
数据可视化
- 通过绘制直方图、散点图、热力图等,帮助直观地理解数据的分布、相关性和异常值
- 就像质检部门的各种精密仪表和监控屏幕,让你看清原材料的特性
这个阶段的目标是:理解数据,清洗数据,并将其转换为适合模型使用的、干净的、纯数值的格式:通常是 NumPy 数组(因为PyTorch接收的数据类型是NP),也可以转为 DF
2. 零件标准化与上料:PyTorch的数据加载
主力工具:torch.utils.data.Dataset和 torch.utils.data.DataLoader
现在原材料已经处理好了,但需要把它们变成标准化的零件,并高效地送到总装线(模型)上 这是 PyTorch 开始介入数据处理的地方,但它的处理是为了训练服务的
-
Dataset
将数据封装成标准接口
- 它定义了数据有多长 (
__len__),以及如何获取任何一个数据样本 (__getitem__) - 就像 “零件规格书”,告诉流水线工头 DataLoader仓库里有什么,以及怎么取
- 它定义了数据有多长 (
-
DataLoader
自动化供给数据
- 从 Dataset 中取出数据,并自动打包成 小批量
- 打乱数据顺序 shuffle,防止模型学到数据的排列规律
- 使用多进程并行加载,确保 GPU 在计算时,CPU 已经在后台准备好了下一批数据,防止生产线停工
DataLoader就是那条高效、智能的自动化传送带
该阶段 PyTorch 并不关心你的数据 “是什么”,而是关心 “怎么把数据喂给模型”
3. 引擎组装与调校:模型构建与训练
主力工具:torch.nn,torch.optim,torch.Tensor:PyTorch 的核心
PyTorch的 核心地带,也是模型/算法的构建:
-
torch.nn:
提供了所有预制好的 “发动机零件”(卷积层、线性层、激活函数等)和 “组装图纸”:
nn.Module -
torch.autograd
提供了自动化的 “反馈系统”,能自动计算梯度,告诉每个零件应该如何微调
-
torch.optim
提供了各种 调校工具(Adam, SGD 等优化器),根据梯度反馈来实际执行参数的微调
该阶段的目标:搭建出强大的引擎(模型),并通过不断地测试和训练,使其性能达到最佳
总结
这是一个分工明确的生态系统:
| 阶段 | 主要工具 | 核心任务 |
|---|---|---|
| 数据分析与预处理 | Pandas, NumPy, Matplotlib | 理解、清洗、转换原始数据 |
| 数据供给 | PyTorch DataLoader | 高效、批量化地为模型提供标准化的数据 |
| 模型构建与训练 | PyTorch nn, optim | 设计、训练和优化深度学习模型 |
PyTorch 的核心竞争力在于它为构建和训练复杂模型提供了无与伦比的灵活性和效率(尤其是 GPU 加速和自动求导)
它也提供了必要的数据加载工具来 “喂饱” 这个强大的计算引擎,但对于训练前对原始数据进行的探索性分析和复杂清洗,使用专业的“数据分析三剑客”会更加方便和强大
激活函数
作用
提问
1. 神经网络使用激活函数,将模型引向非线性的原因
要理解为什么激活函数能引入非线性,首先要看看如果没有激活函数会发生什么:
想象一个简单的两层网络: 数据 首先经过第一个线性层(矩阵乘法 和偏置 ),得到 ; 然后 再经过第二个线性层(),得到最终输出
- 第一步:
- 第二步:
现在,把第一步的 代入第二步:
整理后得到:
你会发现, 仍然是一个矩阵:称为 ,而 仍然是一个偏置向量:称为
所以,整个式子可以被简化为:
结论是惊人的:一个没有激活函数的、无论多么 “深” 的神经网络,其数学本质都等价于一个单层的线性网络 它所能做的,永远只是画一条直线(或一个平面/超平面)来进行划分或拟合,增加再多的层数也无法增强其表达能力。
激活函数的作用:
现在,在两层之间插入一个非线性激活函数 ,比如 ReLU
- 第一步:
- 激活:
- 第二步:
把 代入第二步时:
由于 A() 的存在,无法再通过简单的矩阵乘法来合并 。这个非线性函数 A() “打破” 了线性关系,使得多层网络的叠加变得有意义
一个比喻:
- 线性层 是一根笔直的乐高积木
- 没有激活函数的网络 是把很多根笔直的积木首尾相连——得到的永远是一根更长的、笔直的积木
- 非线性激活函数 是一个乐高铰链或关节零件
- 带有激活函数的网络 是在每两根积木之间都加了一个关节
- 现在,你可以用这些积木和关节搭建出各种复杂的形状、角度和结构,而不仅仅是一条直线
所以,激活函数通过在模型的线性变换之间引入非线性的 “扭结”,使得多层网络的组合能够产生远比单层线性模型复杂得多的函数,从而将模型引向了非线性
2. 模型变为非线性后,为什么模型的预测能力大幅提升
答案很简单:因为生活的真实世界,几乎所有有价值的问题,都是非线性的
线性模型只能理解和描述简单的、直线型的关系
例如:
- “学习时间越长,考试分数越高”(假设关系是笔直的)
- “房子面积越大,价格越高”(同样假设是笔直的关系)
但现实远比这复杂得多。
现实世界的非线性
- 图像识别(例如:猫 vs. 狗)
- 在由成千上万个像素值组成的高维空间中,能够区分所有 “猫” 的图片和所有 “狗” 的图片的分界线,绝对不是一条直线或一个平面
- 这个分界线是极其复杂、弯曲、扭转的。它需要捕捉到 “尖耳朵和胡须” 的组合模式,“大鼻子和 floppy ears” 的组合模式等等。只有非线性模型才能学习并画出这样复杂的决策边界
- 房价预测
- 房价和面积的关系就不是纯线性的 面积从50平米增加到100平米,价格可能翻倍;但从200平米增加到250平米,价格的增长率就会放缓(边际效益递减),这是一个曲线关系
- 此外,房子的位置、楼层、朝向等特征之间存在复杂的交互作用 例如,“学区房” 这个特征会极大地放大 “面积” 这个特征对价格的影响,这种交互作用也是非线性的
- 自然语言处理
- 一个词的意义严重依赖于上下文,这不是简单的线性叠加 例如,“苹果” 在 “我吃了一个苹果”和 “我买了一台苹果电脑” 中意义完全不同,模型必须能理解这种依赖于上下文的、高度非线性的关系。
非线性模型的能力:万能函数逼近器
一个带有非线性激活函数的神经网络(特别是包含至少一个隐藏层的网络)在理论上被称为 “万能函数逼近器”: Universal Function Approximator
- 这意味着,理论上,只要有足够多的神经元,一个神经网络就可以逼近任何连续函数到任意想要的精度
- 为什么重要: 因为真实世界中数据点之间的复杂关系,无论它多么奇怪和弯曲,都可以被看作是一个复杂的函数 神经网络的强大之处就在于,它不预设这个函数的形式,而是通过训练,自动学习和塑造自己,去无限接近这个未知的、复杂的真实函数
总结
| 特性 | 线性模型 | 非线性模型(带激活函数) |
|---|---|---|
| 能力 | 只能学习直线/平面的关系 | 可以学习任意复杂的曲线/曲面关系 |
| 决策边界 | 只能画直线来分类 | 可以画出任意形状的边界来分类 |
| 表达能力 | 有限,无法捕捉复杂模式 | 极强,是“万能函数逼近器” |
| 对应世界 | 对应一个高度简化的、理想化的世界 | 对应一个复杂的、充满交互和变化的真实世界 |
因此,将模型变为非线性,本质上是赋予了模型足够的 “灵活性”和 “表达能力”,使其能够去拟合和理解真实世界中普遍存在的复杂模式和关系,从而大幅提升了其预测能力
隐藏层
隐藏层的激活函数作用是:增加模型的表达能力
隐藏层激活函数(如ReLU)的核心任务是引入非线性
这打破了多层线性变换的局限,使得神经网络能够学习和逼近现实世界中几乎任何复杂的函数关系,从而拥有强大的特征提取和模式识别能力
它的目标是让模型学得更深、更复杂
输出层
输出层激活函数的作用:格式化模型的输出以匹配任务目标
当数据流经所有隐藏层到达网络末端时,得到的是一组原始的、未经处理的数值(通常称为 logits)
这些 logits 的范围可以是任意的负数或正数。输出层激活函数的作用不是为了增加模型的复杂度,而是将这些原始的 logits 转换成一种特定且有价值的格式,使 logits与任务的预期输出完全匹配
具体来说,它的作用可以分为以下几种情况:
-
回归问题
-
目标:
预测一个连续的数值,例如房价、温度。这个数值可能是任意大小的
-
激活函数:
通常不使用任何激活函数,或者说使用线性激活函数,即
这允许模型的输出是任意的实数,完全匹配回归任务的目标
-
-
二元分类 Binary Classification
-
目标:
预测一个样本属于某个类别的概率,例如一封邮件是垃圾邮件的概率。这个输出必须在 0 到 1 之间
-
激活函数:
使用 Sigmoid 函数。它能将任意的 logits 值压缩到 (0, 1) 的区间内,可以直接解释为概率
-
-
多类别分类 Multi-class Classification
-
目标:
预测一个样本在多个互斥类别中的每一个类别上的概率,例如一张图片是猫、狗、鸟的概率。所有类别的概率之和必须等于 1
-
激活函数:
使用 Softmax 函数
它能够接收一组 logits,并将它们转换为一个总和为 1 的概率分布,完美地满足了多类别分类任务的需求
-
总结:隐藏层激活函数是为模型的训练过程服务的,旨在增强模型能力;输出层激活函数是为期望输出服务的,旨在格式化最终答案
常见的激活函数
隐藏层
-
ReLU (Rectified Linear Unit)
现代神经网络的默认首选,几乎是所有类型的神经网络的默认激活函数
-
表达式
当输入为正时,输出等于输入;当输入为负时,输出为 0
是一个分段线性函数,不是处处平滑,但在除原点外 处处可微
-
优点:计算速度极快,有效避免了梯度消失问题,使得训练深度网络成为可能
-
缺点:
Dying ReLU Problem (神经元死亡问题): 如果一个神经元的输入在训练过程中始终为负,那么它的输出将永远是 0,梯度也永远是 0 这个神经元就 “死亡” 了,再也无法通过梯度下降进行更新
-
场景:几乎所有类型的神经网络隐藏层都可以优先尝试使用 ReLU
-
-
Leaky ReLU 及其变体 (PReLU, ELU)
-
优点:
ReLU 的改进版,旨在解决 “神经元死亡” 问题。当输入为负数时,它们允许一个微小的、非零的梯度通过,使得神经元有机会 “复活”
在实践中,它们有时会比 ReLU 带来轻微的性能提升
-
场景:
当你发现使用 ReLU 后的模型性能不佳,或者怀疑有大量神经元 “死亡” 时,可以尝试替换为 Leaky ReLU。
-
-
GELU (Gaussian Error Linear Unit)
-
优点:GELU 是一个更平滑的 ReLU 近似,它根据输入的数值大小(而不仅仅是符号)来决定输出的权重
-
场景:
在现代最先进的 Transformer架构中(例如 BERT, GPT 系列模型),GELU 已经基本取代了 ReLU,成为事实上的标准激活函数
-
-
Swish (或 SiLU, Sigmoid-weighted Linear Unit)
-
优点:
由谷歌研究人员提出, 它在很多任务上的性能都略优于 ReLU,并且函数平滑、非单调
-
场景:在一些先进的计算机视觉模型(如 EfficientNet)和推荐系统中表现优异
-
使用小结:
从 ReLU 开始,如果想进一步优化或使用更现代的模型架构,可以考虑 Leaky ReLU、GELU 或 Swish
而 Sigmoid 和 Tanh 由于梯度消失问题,现在已很少用于较深的神经网络隐藏层中
输出层
输出层的选择非常直接,完全由任务类型决定,几乎没有争议:
| 任务类型 | 常用输出层激活函数 | 解释 |
|---|---|---|
| 回归 (Regression) | 无 (线性 / Linear) | 预测连续值,不需要限制输出范围 |
| 二元分类 (Binary Classification) | Sigmoid | 输出一个 0 到 1 之间的概率 |
| 多类别分类 (Multi-class Classification) | Softmax | 输出一个在所有类别上总和为 1 的概率分布 |
| 多标签分类 (Multi-label Classification) | Sigmoid | 任务是预测一个样本可能同时属于多个类别(例如一篇文章同时有“科技”和“金融”两个标签)。此时,对每个类别都进行一次独立的二元分类判断,因此输出层的每个神经元都接一个 Sigmoid |
softmax介绍:
其中 为第 个节点的输出值, 为输出节点个数,即分类的类别个数
函数特性:
- 将一个包含任意实数的 K 维向量,转换为一个 K 维的、所有元素之和为 1 的概率分布
- 通过 函数就可以将多分类的输出值转换为范围在 和为1的概率分布