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 了。为什么?因为它有两个硬伤:

  1. 梯度饱和:输入很大或很小时,梯度几乎为0,训练极慢
  2. 输出不是零中心的:这会导致下一层的输入全是正数,影响收敛速度

避坑指南:我曾经在做一个二分类的端侧模型时,为了“保险”在隐藏层用了 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 不下降就停止训练。这是最简单也最有效的方法。
  • 数据增强:对输入数据做随机变换,相当于变相增加了训练数据量。

核心要点:正则化的本质是给模型增加“约束”,让它不能太自由地拟合训练数据。在端侧部署中,正则化还有一个额外的好处——它往往能让模型更鲁棒,对输入噪声的容忍度更高。

嗯,这一章的内容就这些。你可能会觉得都是基础,但相信我,基础不牢,地动山摇。我在做端侧部署时,每次遇到模型精度上不去,回头排查的往往就是这些最基础的东西——激活函数选对了没?损失函数匹配任务吗?优化器有没有调好?正则化够不够?

下一章咱们开始讲模型压缩,那才是端侧部署的重头戏。