2、深度学习基础回顾(上):神经网络核心概念、前向传播与反向传播、损失函数与优化器(SGD、Adam)
说实话,很多同学一上来就想直接调 YOLO 的代码,结果连 loss 为什么下降、梯度怎么更新都搞不清楚。我个人习惯是,在碰任何检测模型之前,先把这几块地基夯实了。这一章我们只聊最核心的东西:神经网络怎么算、怎么学、怎么调。
2.1 神经网络核心概念:从感知机到多层网络
神经网络说白了就是一个函数逼近器。你给它一堆输入,它通过层层计算给你一个输出。我刚开始学的时候,总觉得这东西很玄乎,后来发现核心就三个东西:权重、偏置、激活函数。
2.1.1 神经元的基本结构
一个神经元做的事其实很简单:
- 加权求和:把每个输入乘上对应的权重,再加一个偏置
- 非线性变换:把求和结果扔进激活函数
数学上就一行:
z = w1*x1 + w2*x2 + ... + wn*xn + b
a = activation(z)
嗯,这里要注意,如果没有激活函数,多层网络就退化成了线性模型,那跟单层没区别。我在项目里见过有人堆了十几层全连接,结果效果还不如线性回归——就是因为忘了加激活函数。
2.1.2 常见的激活函数
| 激活函数 | 公式 | 特点 | 适用场景 |
|---|---|---|---|
| Sigmoid | σ(x) = 1/(1+e⁻ˣ) | 输出范围(0,1),容易梯度饱和 | 二分类输出层 |
| Tanh | tanh(x) = (eˣ-e⁻ˣ)/(eˣ+e⁻ˣ) | 输出范围(-1,1),零中心 | RNN 常用 |
| ReLU | ReLU(x) = max(0,x) | 计算快,缓解梯度消失 | CNN 默认选择 |
| Leaky ReLU | max(αx, x), α=0.01 | 解决 ReLU 死亡问题 | 深层网络 |
2.1.3 网络结构:层与连接方式
一个典型的全连接网络长这样:
- 输入层:接收原始数据,节点数等于特征维度
- 隐藏层:一层或多层,每层有若干神经元
- 输出层:输出预测结果,节点数取决于任务
举个例子,一个手写数字识别网络:输入 784 个像素值 → 隐藏层 128 个神经元 → 输出层 10 个类别概率。
2.2 前向传播:数据是怎么流过网络的
前向传播就是数据从输入层一路算到输出层的过程。每一层都做两件事:线性变换 + 激活。
假设我们有一个 3 层网络:
# 伪代码:前向传播
h1 = activation(W1 @ x + b1) # 输入层 → 隐藏层1
h2 = activation(W2 @ h1 + b2) # 隐藏层1 → 隐藏层2
y_pred = W3 @ h2 + b3 # 隐藏层2 → 输出层(无激活,回归任务)
你想想看,这个过程其实就是在做特征变换。原始像素经过第一层变成边缘特征,再经过第二层变成纹理特征,最后输出层给出预测。我在做 YOLO 训练时,经常把中间层的特征图可视化出来,看看网络到底学到了什么——这招对调试特别有用。
2.3 反向传播:神经网络的学习引擎
反向传播是神经网络能学习的根本原因。它的核心思想就是链式法则——从输出层开始,逐层往回计算每个参数对损失的梯度。
2.3.1 链式法则直观理解
假设损失 L 是预测值 ŷ 的函数,ŷ 又是权重 w 的函数:
L = f(ŷ)
ŷ = g(w)
∂L/∂w = (∂L/∂ŷ) * (∂ŷ/∂w)
说白了,就是「误差从输出层一层层传回来,告诉每个参数:你该往哪个方向调,调多少」。
2.3.2 反向传播的四个步骤
- 前向传播:计算每层输出,缓存中间结果
- 计算损失:比较预测值和真实值
- 反向传播梯度:从输出层开始,逐层计算 ∂L/∂w 和 ∂L/∂b
- 更新参数:用梯度下降法更新权重
2.4 损失函数:衡量模型的好坏
损失函数就是一把尺子,量一量模型预测得怎么样。不同的任务用不同的损失函数。
2.4.1 回归任务的损失函数
| 损失函数 | 公式 | 特点 |
|---|---|---|
| 均方误差 (MSE) | L = (y - ŷ)² | 对大误差惩罚大,对异常值敏感 |
| 平均误差 (MAE) | L = |y - ŷ| | 对异常值更鲁棒 |
| Huber Loss | MSE 当 |y-ŷ|<δ, 否则 MAE | 结合两者优点 |
2.4.2 分类任务的损失函数
分类任务最常用的是交叉熵损失(Cross-Entropy Loss)。对于二分类:
L = -[y * log(ŷ) + (1-y) * log(1-ŷ)]
对于多分类(比如 YOLO 的类别预测):
L = -Σ y_i * log(ŷ_i)
2.5 优化器:梯度下降的进化史
有了梯度,怎么更新参数?这就是优化器干的事。最朴素的想法就是:沿着梯度的反方向走一步。
2.5.1 随机梯度下降 (SGD)
SGD 的更新公式很简单:
w = w - lr * ∇w
其中 lr 是学习率,∇w 是梯度。SGD 的问题也很明显:
- 学习率固定,不好调
- 容易陷入局部最优或鞍点
- 收敛速度慢
我记得刚开始做项目时,调 SGD 的学习率调到头秃。设大了 loss 爆炸,设小了半天不动。
2.5.2 带动量的 SGD (SGD with Momentum)
动量法引入了「惯性」概念:
v = β * v + (1-β) * ∇w
w = w - lr * v
这样做的好处是:梯度方向一致时加速,方向变化时减速。说白了就是让更新更平滑,不容易卡在局部极小点。
2.5.3 Adam 优化器
Adam 是目前最常用的优化器,它结合了动量和自适应学习率:
- 动量:记录梯度的一阶矩(均值)
- 自适应学习率:记录梯度的二阶矩(方差)
- 偏差校正:解决初始阶段估计不准的问题
Adam 的默认参数(lr=0.001, β₁=0.9, β₂=0.999)在大多数任务上表现都不错。我个人习惯:先用 Adam 快速找到一个好的区域,再换 SGD + Momentum 精调。
| 优化器 | 优点 | 缺点 | YOLO 常用吗? |
|---|---|---|---|
| SGD | 简单,泛化性好 | 调参困难,收敛慢 | YOLOv5 默认 |
| SGD+Momentum | 收敛更快,更稳定 | 仍需要调学习率 | YOLOv8 可选 |
| Adam | 自适应,几乎不用调参 | 有时泛化不如 SGD | YOLOv9 可选 |
| AdamW | 改进了权重衰减 | 比 Adam 稍复杂 | 越来越多项目在用 |
2.6 本章小结
这一章我们聊了神经网络最核心的几个概念:
- 神经元的结构:加权求和 + 激活函数
- 前向传播:数据从输入到输出的计算过程
- 反向传播:用链式法则计算梯度
- 损失函数:衡量预测和真实的差距
- 优化器:SGD、Momentum、Adam 的进化路线