4. 深度学习基础回顾:神经网络前向传播、激活函数、损失函数、优化器、过拟合与正则化
好,咱们进入正题。这一章我打算把深度学习里最核心的几个概念串一遍。你可能会觉得这些是基础,但说实话,我在端侧部署项目里踩过的坑,十有八九都跟这些基础概念没吃透有关。咱们一个一个来。
4.1 神经网络前向传播:数据是怎么“流”过去的?
前向传播,说白了就是数据从输入层一路算到输出层的过程。你想想看,每一层神经元都做两件事:加权求和,然后过激活函数。
举个例子,一个简单的全连接层:
# 伪代码,理解意思就行
z = W * x + b # 线性变换
a = activation(z) # 非线性激活
嗯,这里要注意:矩阵乘法的维度一定要对。我在项目中遇到过好几次,模型在PC上跑得好好的,一部署到嵌入式设备上就报错,查了半天发现是输入张量的shape跟权重不匹配。端侧设备的内存对齐要求更严格,这种问题特别容易暴露。
核心要点:前向传播就是一系列矩阵乘法和激活函数的组合。每一层的输出是下一层的输入,直到最后一层给出预测结果。
4.2 激活函数:为什么不能全是线性?
如果没有激活函数,那神经网络再深也就是个线性变换的堆叠,表达能力有限。激活函数引入非线性,让网络能拟合复杂函数。
4.2.1 ReLU:目前最常用的选择
ReLU 公式很简单:f(x) = max(0, x)。我个人的习惯是,默认首选 ReLU,除非有特殊原因。
- 优点:计算快,梯度不饱和,能缓解梯度消失
- 缺点:神经元“坏死”问题——如果某个神经元一直输出负值,它的梯度就永远是0,再也学不动了
我的经验:在端侧部署时,ReLU 的零值特性还能帮我们做模型剪枝。那些输出一直为0的神经元,直接砍掉,模型体积能小不少。
4.2.2 Sigmoid:二分类的“老将”
Sigmoid 把输出压缩到 (0, 1) 之间,适合做概率输出。公式:f(x) = 1 / (1 + e^(-x))。
但说实话,现在隐藏层很少用 Sigmoid 了。为什么?因为它有两个硬伤:
- 梯度饱和:输入很大或很小时,梯度几乎为0,训练极慢
- 输出不是零中心的:这会导致下一层的输入全是正数,影响收敛速度
避坑指南:我曾经在做一个二分类的端侧模型时,为了“保险”在隐藏层用了 Sigmoid,结果训练了200个epoch loss 还是降不下去。换成 ReLU 后,50个epoch就收敛了。从那以后,我只有在输出层做概率预测时才用 Sigmoid。
4.3 损失函数:模型学得好不好,得有个“尺子”
损失函数衡量模型预测值和真实值之间的差距。不同的任务用不同的损失函数。
| 任务类型 | 常用损失函数 | 说明 |
|---|---|---|
| 回归 | 均方误差 (MSE) | 对异常值敏感,误差越大惩罚越重 |
| 二分类 | 二元交叉熵 (BCE) | 配合 Sigmoid 使用,梯度更稳定 |
| 多分类 | 交叉熵 (CE) | 配合 Softmax 使用,输出概率分布 |
我个人建议:分类任务无脑用交叉熵。MSE 在分类任务上收敛慢,而且容易陷入局部最优。你想想看,分类任务关心的是“对不对”,而不是“差多少”,交叉熵天然适合这种场景。
4.4 优化器:怎么让损失降下去?
有了损失函数,接下来就是怎么调整权重让损失最小化。这就是优化器干的事。
4.4.1 SGD:最朴素的梯度下降
SGD 就是每次用一小批数据计算梯度,然后沿着梯度反方向更新参数:
w = w - learning_rate * gradient
优点:简单、稳定。缺点:学习率不好调,而且容易在鞍点附近震荡。
我的习惯:在端侧模型微调时,我经常用 SGD + 动量。动量能加速收敛,还能帮助跳出局部极小值。动量系数我一般设 0.9。
4.4.2 Adam:自适应学习率的“万金油”
Adam 结合了动量和自适应学习率的思路。它会为每个参数单独调整学习率,所以对初始学习率不那么敏感。
说实话,现在大部分项目默认都用 Adam。我刚开始做部署时也这样,但后来发现一个问题:Adam 训练出来的模型,权重的分布往往比较“散”,量化时精度损失比 SGD 大。
避坑指南:如果你打算做模型量化部署,我建议先用 Adam 快速找到一个好的起点,然后切到 SGD 做微调。这样既利用了 Adam 的快速收敛,又保留了 SGD 的“干净”权重分布。我在一个语音唤醒词项目里试过,量化后精度高了 2 个百分点。
4.5 过拟合与正则化:模型“死记硬背”怎么办?
过拟合就是模型在训练集上表现很好,但在测试集上一塌糊涂。说白了,模型把训练数据里的噪声也学进去了,没有学到真正的规律。
4.5.1 怎么判断过拟合?
看训练 loss 和验证 loss 的差距。如果训练 loss 一直降,但验证 loss 开始回升,那就是过拟合了。
4.5.2 常用的正则化方法
- L1/L2 正则化:在损失函数里加一个权重的惩罚项。L1 会让权重变稀疏,L2 会让权重变小。我个人的经验是,端侧模型优先考虑 L1,因为稀疏权重更容易做剪枝。
- Dropout:训练时随机“丢弃”一部分神经元,强迫网络学习冗余表示。推理时所有神经元都参与,但权重要乘以保留概率。
- 早停法:验证 loss 连续 N 个 epoch 不下降就停止训练。这是最简单也最有效的方法。
- 数据增强:对输入数据做随机变换,相当于变相增加了训练数据量。
核心要点:正则化的本质是给模型增加“约束”,让它不能太自由地拟合训练数据。在端侧部署中,正则化还有一个额外的好处——它往往能让模型更鲁棒,对输入噪声的容忍度更高。
嗯,这一章的内容就这些。你可能会觉得都是基础,但相信我,基础不牢,地动山摇。我在做端侧部署时,每次遇到模型精度上不去,回头排查的往往就是这些最基础的东西——激活函数选对了没?损失函数匹配任务吗?优化器有没有调好?正则化够不够?
下一章咱们开始讲模型压缩,那才是端侧部署的重头戏。