第二章:神经网络基础回顾——感知机与多层感知机、激活函数、前向与反向传播

各位同学,欢迎来到第二章。说实话,每次讲这一章我都特别感慨。为什么?因为我在做AI芯片之前,一直觉得神经网络是搞算法的人的事,跟我们硬件工程师八竿子打不着。直到有一次,我接手了一个嵌入式视觉项目,客户要求在0.5W功耗下跑人脸检测——嗯,从那以后我再也不敢说「算法跟我无关」了。

这一章,我们就把神经网络最核心的几个概念掰开揉碎。你不需要有数学恐惧症,我会用做硬件设计的思路来讲。

2.1 感知机:最简单的「神经元」

感知机(Perceptron)是1957年提出的。说白了,它就是模仿人脑神经元的一个简化模型。

一个感知机接收多个输入,每个输入有一个权重,然后求和,再经过一个阶跃函数输出0或1。数学表达很简单:

y = 1  if (w1*x1 + w2*x2 + ... + wn*xn + b) > 0
y = 0  otherwise

我在项目中遇到过一件事:有次调试一个简单的二分类电路,死活不收敛。查了半天,发现是偏置b忘了加。你想想看,没有偏置,决策边界就必须过原点,很多问题根本解不了。所以,偏置不是可选项,是必需品

注意: 感知机只能解决线性可分问题。异或(XOR)问题它搞不定。这是它的硬伤,也是后来多层感知机出现的原因。

2.2 多层感知机:打破线性限制

既然一个感知机不够,那就堆多个。多层感知机(MLP)就是在输入和输出之间加了「隐藏层」。每一层的输出作为下一层的输入。

我习惯把MLP看作一个「函数逼近器」。理论上,只要隐藏层神经元够多,它可以拟合任意复杂的函数。这个结论叫「万能逼近定理」,做硬件加速器的时候,我们就是靠这个定理吃饭的。

举个例子,一个两层的MLP(一个隐藏层)可以解决XOR问题:

隐藏层神经元1:实现 AND
隐藏层神经元2:实现 OR
输出层:组合它们得到 XOR

嗯,这里要注意:隐藏层的层数和神经元数量不是越多越好。我见过有人为了追求精度,堆了20层,结果在嵌入式芯片上推理延迟直接爆炸。做硬件的人,一定要有「成本意识」。

我的经验: 对于嵌入式场景,2-4层隐藏层通常就够用了。每层神经元数量建议从输入维度的2倍开始试,逐步缩减。

2.3 激活函数:给网络注入「非线性」

如果没有激活函数,多层感知机就退化成单层线性变换。那跟没有隐藏层一样。激活函数的作用,就是引入非线性。

常用的激活函数有几种,我列个表对比一下:

激活函数 公式 优点 缺点
Sigmoid σ(x) = 1/(1+e-x) 输出范围(0,1),适合概率输出 梯度饱和,容易梯度消失
ReLU f(x) = max(0, x) 计算简单,缓解梯度消失 神经元「死亡」问题
Tanh tanh(x) = (ex-e-x)/(ex+e-x) 输出范围(-1,1),零中心 同样有梯度饱和

我个人强烈推荐ReLU。为什么?因为它在硬件上实现太友好了。你想想看,Sigmoid需要指数运算,在嵌入式芯片上要消耗大量逻辑门。而ReLU就是一个比较器加一个选择器,几乎不占资源。

不过ReLU有个坑——「神经元死亡」。如果输入一直为负,梯度就是0,这个神经元就再也学不动了。我曾经在一个项目中踩过这个坑,后来用Leaky ReLU解决了:

Leaky ReLU: f(x) = x if x > 0, else α*x (α很小,比如0.01)
硬件视角: 选择激活函数时,不仅要看精度,还要看硬件实现成本。ReLU系列是嵌入式加速器的首选。

2.4 前向传播:数据从输入到输出

前向传播很简单,就是数据从输入层流到输出层的过程。每一层做两件事:线性变换 + 激活。

数学上,第l层的输出a[l]可以写成:

z[l] = W[l] · a[l-1] + b[l]
a[l] = g(z[l])   // g是激活函数

做硬件加速的时候,前向传播就是我们的「推理」过程。我习惯把权重W看作「固定的参数」,把输入a看作「流动的数据」。在芯片上,这就是一个乘累加(MAC)阵列不断重复的过程。

你可能会问:为什么叫「前向」?因为信息只往前走,不回头。这在硬件上意味着——流水线设计非常自然。每一层可以做成一个独立的流水级,数据一拍一拍往下传。

2.5 反向传播:让网络学会「纠错」

反向传播(Backpropagation)是训练神经网络的核心算法。说白了,就是计算每个参数对最终误差的「贡献度」,然后沿着梯度下降的方向更新参数。

我刚开始学的时候,觉得反向传播特别玄乎。后来用链式法则一拆,其实就三件事:

  1. 计算输出层的误差:预测值 - 真实值
  2. 误差反向传递:用链式法则,把误差从输出层传到输入层
  3. 更新参数:每个参数减去它的梯度乘以学习率

公式长这样(以均方误差损失为例):

输出层误差: δ[L] = (a[L] - y) * g'(z[L])
隐藏层误差: δ[l] = (W[l+1])T · δ[l+1] * g'(z[l])
梯度更新: W[l] = W[l] - η * δ[l] · (a[l-1])T

嗯,这里要注意:反向传播在硬件上实现比前向传播难得多。为什么?因为前向传播只需要乘累加,而反向传播需要矩阵转置、激活函数求导、还要保存中间结果。这就是为什么训练通常在GPU上做,推理才放到嵌入式芯片上。

避坑指南: 我曾经在FPGA上实现过反向传播,结果发现中间激活值的存储需求是前向传播的3倍。如果你要做片上训练(on-chip training),一定要提前规划好存储带宽。

2.6 从算法到硬件的桥梁

讲到这里,我想强调一个观点:理解神经网络的计算模式,是设计加速器的第一步

前向传播的本质是什么?是大量的矩阵乘法(或者卷积)。反向传播的本质是什么?是矩阵乘法 + 转置 + 逐元素操作。这些操作在硬件上都可以映射到乘累加阵列(Systolic Array)上。

我建议你记住这张对应关系表:

算法操作 硬件实现 关键参数
线性变换 (Wx+b) MAC阵列 + 加法树 数据位宽、并行度
激活函数 (ReLU/Sigmoid) 查找表或分段线性逼近 精度、延迟
反向传播梯度计算 共享MAC阵列(时分复用) 存储带宽、控制逻辑
权重更新 加法器 + 乘法器 学习率量化

说白了,你只要把前向传播和反向传播的数学吃透了,后面设计加速器架构就是「如何用硬件高效实现这些计算」的问题。

我的建议: 初学者可以先在Python里手写一个简单的MLP(不要用框架),把前向和反向传播的每一步都打印出来看。我当年就是这么干的,虽然代码丑,但对理解帮助极大。

好,这一章就到这里。下一章我们会深入卷积神经网络(CNN),看看它是怎么处理图像数据的。到时候你会发现,很多思路跟MLP是一脉相承的。