【深度学习】MLP(多层感知机):神经网络基础
MLP(多层感知机)从零开始教程
写在前面
MLP 是最基础的神经网络,也是理解所有后续网络(CNN、RNN、Transformer)的起点。这篇文档从"一个神经元在做什么"讲起,逐步搭建到完整的 MLP 分类器,最后对照本项目的代码把每一行都讲透。
第一章 一个神经元——最小的"决策单元"
1.1 神经元的灵感来源
人脑有约 860 亿个神经元,每个神经元的工作模式是:
- 从其他神经元接收信号
- 把信号汇总
- 如果汇总结果超过某个阈值,就"激发",向下游发出信号;否则保持沉默
人工神经元直接模仿了这个过程。
1.2 人工神经元的数学表达
输入 x₁ ──→ ×w₁ ──┐
输入 x₂ ──→ ×w₂ ──┼──→ 加权求和 z = w₁x₁ + w₂x₂ + w₃x₃ + b ──→ 激活函数 f(z) ──→ 输出 y
输入 x₃ ──→ ×w₃ ──┘
| 符号 | 含义 | 直觉 |
|---|---|---|
| x₁, x₂, x₃ | 输入 | 从上游接收到的信号 |
| w₁, w₂, w₃ | 权重 | 每个输入有多重要(可正可负) |
| b | 偏置 | 调节"激发门槛"的旋钮 |
| z | 加权求和 | 把所有信号汇总成一个数 |
| f | 激活函数 | 决定是否"激发"、输出多少 |
| y | 输出 | 传递给下游的信号 |
1.3 一个具体的例子
假设一个神经元用来判断"今天适不适合出门":
输入:
x₁ = 温度(25°C → 归一化后 0.8)
x₂ = 下雨概率(80% → 0.8)
x₃ = 心情好不好(好 → 1.0)
权重(训练学到的):
w₁ = +0.6 (温度越高越想出门)
w₂ = -0.9 (下雨越大越不想出门)
w₃ = +0.5 (心情好更想出门)
b = -0.1
计算:
z = 0.6×0.8 + (-0.9)×0.8 + 0.5×1.0 + (-0.1)
= 0.48 - 0.72 + 0.50 - 0.10
= 0.16
ReLU(0.16) = 0.16 > 0 → "激发",输出 0.16 → 倾向出门(但不太强烈)
权重的正负决定了这个输入是"鼓励"还是"抑制"输出。这些权重不是人工设定的,而是训练过程中自动学出来的。
1.4 单个神经元的局限
一个神经元只能画一条直线来划分"是/否"。现实中的问题往往需要弯弯曲曲的分类边界。解决办法:把很多神经元组合起来——这就是"层"和"网络"。
第二章 从单个神经元到"层"
2.1 什么是层
把多个神经元并排放在一起,它们接收相同的输入,但各自有各自的权重和偏置,各自输出一个值。
神经元1 → y₁
输入 x → 神经元2 → y₂ ← 这就是一个"层",有 4 个神经元
神经元3 → y₃
神经元4 → y₄
4 个神经元就输出 4 个数,组成一个长度为 4 的向量。
一个常见的误解:一个神经元关注一个特性。实际上,一个神经元关注的是一种输入的组合模式。它的权重决定了它对哪些输入更敏感——可能侧重某一个(权重特别大),也可能综合多个。比如权重 [0.9, 0.01, 0.02] 几乎只看第一个输入,而 [0.4, 0.3, 0.3] 则均匀地看三个。很多神经元并排放一起,就有了很多种不同的"投票策略",合起来覆盖各种情况。
2.2 用矩阵表示
把 4 个神经元的权重排成一个矩阵,一层的计算就是一次矩阵乘法:
z=W⋅x+bz = W \cdot x + bz=W⋅x+b
y=f(z)y = f(z)y=f(z)
比如输入有 3 个值、这一层有 4 个神经元,那么 W 是 4×3 的矩阵,b 是长度 4 的向量。
这就是为什么深度学习离不开线性代数——一层的前向传播 = 一次矩阵乘法 + 一次激活。
2.3 "全连接"是什么意思
如果每一个输入都和每一个神经元相连,就叫全连接层(Fully Connected,也叫 Dense 或 Linear)。
输入(3个) 全连接层(4个神经元)
x₁ ─────┬──→ n₁
├──→ n₂ 每个输入到每个神经元都有一条线 = 全连接
x₂ ─────┼──→ n₃ 一共 3×4 = 12 条线(12 个权重)
├──→ n₄
x₃ ─────┘
为什么要全连接? 让每个神经元都能"看到"全部输入信息,自己决定关注什么。如果某个神经元只连了部分输入,它就永远看不到其余输入的信息,哪怕那些信息很重要。全连接保证了信息不被人为截断——至于每个输入到底重不重要,交给权重去学:重要的权重变大,不重要的趋近于 0。
缺点:输入一多,连接数就爆炸。3072 个输入 × 1024 个神经元 = 300 万条线。这也是后来 CNN 用卷积代替全连接的动机——不需要每个像素都和每个神经元相连,只看局部就够了。
全连接层是 MLP 的唯一"砖块"。
第三章 MLP = 多层全连接网络
3.1 什么是 MLP
MLP(Multi-Layer Perceptron,多层感知机)就是把多个全连接层串起来,每两层之间加一个激活函数。
输入层 隐藏层1 隐藏层2 输出层
(原始数据) (中间特征) (更高级特征) (分类得分)
x₁ ──┐ ┌── h₁ ──┐ ┌── h₁ ──┐ ┌── y₁
x₂ ──┼──→ ├── h₂ ──┼──→ ├── h₂ ──┼──→ ├── y₂
x₃ ──┤ ├── h₃ ──┤ └── h₃ ──┘ └── y₃
⋮ ┘ └── h₄ ──┘
3.2 为什么叫"深度"学习
只有一个隐藏层的 MLP 叫"浅层网络"。有两个及以上隐藏层的就算"深层网络"——这也是"深度学习"这个名字的由来。层数越多,网络能表达的函数越复杂。
3.3 每一层在干什么
| 层 | 作用 | 比喻 |
|---|---|---|
| 输入层 | 接收原始数据 | 把题目摆到桌上 |
| 隐藏层 | 提取中间特征 | 画草稿、做中间推理 |
| 输出层 | 给出最终结果 | 写下最终答案 |
隐藏层之所以叫"隐藏",是因为我们只能看到输入和输出,中间层学到的东西是网络自己决定的,对我们是"隐藏"的。
3.4 激活函数为什么必须存在
如果去掉所有激活函数,整个 MLP 就变成了:
y=W3⋅(W2⋅(W1⋅x))=(W3W2W1)⋅x=W′⋅xy = W_3 \cdot (W_2 \cdot (W_1 \cdot x)) = (W_3 W_2 W_1) \cdot x = W' \cdot xy=W3⋅(W2⋅(W1⋅x))=(W3W2W1)⋅x=W′⋅x
多层线性变换等价于一层线性变换——深度白加了。激活函数(ReLU)在每层之后引入"拐点",才让多层叠加有了意义。
常见误解:激活函数是为了去掉不重要的参数。 不是。激活函数的目的是引入非线性——让网络能画出弯曲的分类边界,而不只是一条直线。之所以会有这个误解,是因为 ReLU 恰好会把负数归零,看起来像在"丢东西"。但换成 Tanh(输出在 -1 到 1 之间,不归零)同样能当激活函数,因为它也是非线性的。一句话:激活函数 = 给网络装弯道,没有它只能走直线。
第四章 MLP 处理图片——拍平的代价
4.1 图片怎么变成 MLP 能接收的输入
MLP 的输入必须是一维向量。但图片是二维的(宽×高),还有颜色通道(RGB),所以是三维的。
解决办法很粗暴:直接拍平。
一张 32×32×3 的彩色图片:
R 通道(32×32) G 通道(32×32) B 通道(32×32)
┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ │ │ │ │
│ 32×32 │ │ 32×32 │ │ 32×32 │
│ │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘
↓ Flatten(拍平)
[ r₁, r₂, ..., r₁₀₂₄, g₁, g₂, ..., g₁₀₂₄, b₁, b₂, ..., b₁₀₂₄ ]
└───────────────────────── 3072 个数字 ──────────────────────────────┘
4.2 拍平丢掉了什么
拍平后,所有的空间位置关系都没了:
- 左上角的像素和右下角的像素在向量里可能相邻,也可能相隔很远
- 一只猫的眼睛和耳朵明明是邻居,拍平后网络不知道它们挨着
- 即使把图片旋转 90°、翻转,拍平后得到的向量完全不同——MLP 认不出是同一张图
这就是 MLP 做图像分类天生的劣势,也是后来 CNN 被发明的直接原因。
4.3 那 MLP 做图片分类还有什么意义
作为基线(baseline)。在实验中先用 MLP 跑一组结果,再用 CNN 跑一组,对比之下就能清楚地看出 CNN 的优势有多大。
第五章 本项目 MLP 的完整结构
5.1 网络结构图
输入: [batch, 3, 32, 32] ← 一批 32×32 的 RGB 图片
Flatten → [batch, 3072] 把图片拍成一维向量
↓
Linear(3072 → 1024) → [batch, 1024] 第一个全连接层
ReLU → [batch, 1024] 激活:负数归零
Dropout(0.3) → [batch, 1024] 随机关掉 30% 的神经元
↓
Linear(1024 → 512) → [batch, 512] 第二个全连接层
ReLU → [batch, 512] 激活
Dropout(0.3) → [batch, 512] 再随机关掉 30%
↓
Linear(512 → 100) → [batch, 100] 输出层:100 个类别各一个得分
最终: 取 100 个得分中最大的那个对应的类别,作为预测结果
5.2 逐层解读
Flatten:拍平
把 32×32×3 = 3072 个像素值排成一行。这一步没有任何可学习的参数,纯粹是数据形状变换。
Linear(3072, 1024):第一个全连接层
3072 个输入,1024 个输出。参数量 = 3072×1024 + 1024(偏置)= 3,146,752。
这一层做的事情:把 3072 维的原始像素信息"混合压缩"成 1024 维的中间表示。你可以想象成:3072 个人投票,1024 个委员会成员分别统计不同维度的民意。
ReLU:激活
把负数变成 0。没有可学习参数。作用:让网络能表达非线性关系。
Dropout(0.3):防过拟合
训练时随机关掉 30% 的神经元。没有可学习参数。作用:防止网络死记硬背训练数据。
测试时所有神经元都打开(PyTorch 的 model.eval() 自动处理)。
Linear(1024, 512):第二个全连接层
把 1024 维进一步压缩到 512 维。参数量 = 1024×512 + 512 = 524,800。
这一层在提取更高级的特征组合。
Linear(512, 100):输出层
512 维压缩到 100 维。每个输出值代表"这张图属于第 i 类的得分"。参数量 = 512×100 + 100 = 51,300。
注意输出层没有激活函数——因为交叉熵损失函数内部自带 softmax,不需要额外加。
5.3 参数量总结
| 层 | 参数量 | 占比 |
|---|---|---|
| Linear(3072→1024) | 3,146,752 | 84.5% |
| Linear(1024→512) | 524,800 | 14.1% |
| Linear(512→100) | 51,300 | 1.4% |
| 总计 | 3,722,852 | 100% |
绝大多数参数集中在第一层——因为输入维度太高(3072)。而 CNN 用卷积的权重共享机制,同样的输入只需要几千个参数就能处理,这是 CNN 参数效率远高于 MLP 的根本原因。
第六章 MLP 的训练过程
6.1 整体流程
┌──────────────────────────────────────────────────┐
│ │
│ ① 取一批图片 → ② 拍平 → ③ 前向传播得到预测 │
│ ↓ │
│ ④ 和标签比较算损失 │
│ ↓ │
│ ⑤ 反向传播算梯度 │
│ ↓ │
│ ⑥ 优化器更新参数 │
│ ↓ │
│ 回到 ① 取下一批 ────→│
│ │
└──────────────────────────────────────────────────┘
一个 epoch = 把训练集完整过一遍
6.2 前向传播的数值示例
用一个极简版(3 个输入、2 个隐藏、2 个输出)演示一轮计算:
输入: x = [0.5, 0.3, 0.8]
第一层 Linear(3→2):
W₁ = [[0.1, 0.4, -0.2], b₁ = [0.1, -0.1]
[0.3, -0.1, 0.5]]
z₁ = W₁·x + b₁ = [0.1×0.5 + 0.4×0.3 + (-0.2)×0.8 + 0.1,
0.3×0.5 + (-0.1)×0.3 + 0.5×0.8 + (-0.1)]
= [0.05 + 0.12 - 0.16 + 0.1, 0.15 - 0.03 + 0.40 - 0.1]
= [0.11, 0.42]
ReLU:
h₁ = [max(0, 0.11), max(0, 0.42)] = [0.11, 0.42] (都是正数,不变)
第二层 Linear(2→2):
W₂ = [[0.2, -0.3], b₂ = [0.0, 0.1]
[-0.1, 0.6]]
z₂ = W₂·h₁ + b₂ = [0.2×0.11 + (-0.3)×0.42 + 0.0,
(-0.1)×0.11 + 0.6×0.42 + 0.1]
= [-0.104, 0.341]
输出(logits): [-0.104, 0.341]
→ 第 2 类得分更高 → 模型预测为第 2 类
6.3 损失函数怎么算
假设真实标签是第 1 类(索引 0),交叉熵损失会说:“你给第 1 类打了 -0.104 分,给第 2 类打了 0.341 分,你打错了,损失很大!”
然后反向传播从这个损失出发,算出 W₁、b₁、W₂、b₂ 每个参数该怎么调,优化器执行调整,下一轮预测就会更准一点。
6.4 训练多久
本项目默认训练 10 个 epoch,每个 epoch 遍历全部训练集一次。每个 epoch 结束后在验证集上测一次准确率,记录最好的那一轮模型。
第七章 MLP 的弱点与 CNN 的优势对比
| 维度 | MLP | CNN |
|---|---|---|
| 处理图片方式 | 拍平成向量,丢掉空间信息 | 保留二维结构,用卷积扫描 |
| 参数量 | 第一层就 300 万+ | 卷积层参数很少(权重共享) |
| 平移不变性 | 没有,图片移动一下结果就变 | 有,同一个卷积核到处扫 |
| 适合的数据 | 表格数据、低维特征 | 图像、音频等有空间/时间结构的数据 |
| 在 CIFAR-100 上的表现 | 较差(通常 30-40%) | 较好(通常 50-70%) |
为什么 MLP 在图片上差这么多
一个核心原因:MLP 学不到"局部模式可以在任意位置出现"这个先验。
比如"猫耳朵"可能出现在图片的左上角,也可能在右下角。CNN 用同一个卷积核在整张图上滑动,只要学会"猫耳朵长什么样",不管它在哪都能检测到。而 MLP 把图片拍平后,左上角和右下角的像素在向量里位置不同,它需要分别学两套权重来识别,等于同一个知识学了两遍——数据不够时就学不好。
第八章 MLP 的适用场景
虽然 MLP 不擅长图片,但在很多其他场景它是主力:
| 场景 | 输入 | 为什么 MLP 够用 |
|---|---|---|
| 表格数据分类 | 年龄、收入、学历等特征 | 没有空间结构,不需要卷积 |
| 推荐系统 | 用户特征 + 物品特征 | 拼接成向量后全连接就能学 |
| Transformer 的内部 | 注意力层的输出 | Transformer 每个块里都有一个两层 MLP(叫 FFN) |
| 强化学习 | 状态向量 | DQN、PPO 的 Q 网络和策略网络本质就是 MLP |
| 嵌入层之后 | 低维表示 | 任何网络的最后几层分类头通常就是 MLP |
一句话:MLP 是深度学习的"万金油",几乎所有网络内部都在用它,只是不擅长直接处理有空间结构的原始数据。
总结:MLP 核心概念一页纸
┌─────────────────────────────────────────────────────────────┐
│ MLP 核心概念速查 │
├─────────────┬───────────────────────────────────────────────┤
│ 神经元 │ 加权求和 + 偏置 + 激活函数 = 最小决策单元 │
│ 权重 w │ 每个输入的"重要程度",正=鼓励,负=抑制 │
│ 偏置 b │ 调节激发门槛的旋钮 │
│ 全连接层 │ 每个输入连接每个输出,一次矩阵乘法 │
│ 隐藏层 │ 输入和输出之间的中间层,提取特征 │
│ ReLU │ max(0, x),引入非线性,负数归零 │
│ Dropout │ 训练时随机关神经元,防过拟合 │
│ Flatten │ 多维数据拍成一维向量,MLP 处理图片的前提 │
│ 前向传播 │ 输入 → 逐层计算 → 输出预测 │
│ 反向传播 │ 从损失出发,逆向算每个参数的梯度 │
│ 参数量 │ 全连接层参数 = 输入维度 × 输出维度 + 偏置 │
│ 局限性 │ 拍平丢掉空间信息,不适合直接处理图像 │
│ 真正擅长 │ 表格数据、作为其他网络的内部组件 │
└─────────────┴───────────────────────────────────────────────┘
更多推荐


所有评论(0)