一、神经元与感知机模型
神经网络的基础,说白了就是模仿人脑的工作方式。人脑里有大约860亿个神经元,每个神经元接收信号、处理、再传给下一个。我们做NPU设计,本质上就是在硅片上复现这个过程。
先看最简单的神经元模型——感知机。它由数学家Rosenblatt在1957年提出,结构其实很简单:
- 输入:多个信号x₁, x₂, ..., xₙ
- 权重:每个输入对应一个权重w₁, w₂, ..., wₙ
- 偏置:一个常数b,相当于神经元的"阈值"
- 激活函数:决定是否"点火"输出
数学表达式就是:y = f(w₁x₁ + w₂x₂ + ... + wₙxₙ + b)
我在做第一版NPU原型时,发现很多新人搞不清权重和偏置的物理意义。其实你可以这么理解:权重决定了输入的重要性,偏置决定了神经元激活的难易程度。权重越大,这个输入对结果的影响就越大;偏置越大,神经元越不容易被激活。
核心要点:感知机只能解决线性可分问题。比如AND、OR这类逻辑门可以,但XOR就不行。这就是为什么后来需要多层神经网络。
二、激活函数
激活函数是神经网络里最关键的组件之一。没有它,再多层网络也只是线性变换的堆叠,表达能力有限。我挑三个最常用的来讲。
2.1 Sigmoid函数
Sigmoid把输入映射到(0,1)区间,公式是:σ(x) = 1 / (1 + e⁻ˣ)
它的优点是平滑、可导,输出可以解释为概率。但有个致命问题——梯度消失。当|x|很大时,梯度趋近于0,网络几乎学不动了。
避坑指南:我曾经在做一个深度推荐模型时,用了Sigmoid做隐藏层激活,结果训练到第3层梯度就几乎为零了。后来换成ReLU,训练速度快了10倍。所以Sigmoid现在基本只用在输出层做二分类。
2.2 ReLU函数
ReLU(Rectified Linear Unit)是目前最常用的激活函数:f(x) = max(0, x)
它的好处很明显:
- 计算简单,只需比较大小
- 不会饱和,正区间梯度恒为1
- 稀疏激活,很多神经元输出为0
但ReLU也有个坑——神经元死亡。如果某个神经元一直输出负值,它的梯度就是0,权重再也更新不了。我见过一个项目,因为学习率设太大,导致一半神经元都"死"了。
2.3 Tanh函数
Tanh是Sigmoid的变体,输出范围是(-1,1):tanh(x) = (eˣ - e⁻ˣ) / (eˣ + e⁻ˣ)
它比Sigmoid好的一点是零中心化,输出均值接近0,有利于下一层的学习。但梯度消失问题依然存在。
| 激活函数 | 输出范围 | 优点 | 缺点 | NPU实现难度 |
|---|---|---|---|---|
| Sigmoid | (0, 1) | 平滑、概率解释 | 梯度消失 | 中(需要指数运算) |
| ReLU | [0, ∞) | 简单、无饱和 | 神经元死亡 | 低(只需比较器) |
| Tanh | (-1, 1) | 零中心化 | 梯度消失 | 中(需要指数运算) |
个人经验:在NPU设计中,ReLU是最容易硬件实现的,只需要一个比较器。而Sigmoid和Tanh需要查表或近似计算,会占用更多面积和功耗。所以工业界的NPU几乎都优先支持ReLU。
三、前向传播与反向传播
这两个概念是神经网络训练的"左右手"。前向传播做预测,反向传播做修正。
3.1 前向传播
前向传播就是从输入到输出的计算过程。数据从输入层开始,逐层计算,直到输出层。每一层的计算就是:
z = W · x + b # 线性变换
a = f(z) # 激活函数
举个例子,一个3层网络(输入层2节点,隐藏层3节点,输出层1节点)的前向传播:
# 输入层到隐藏层
z₁ = W₁ · x + b₁
a₁ = ReLU(z₁)
# 隐藏层到输出层
z₂ = W₂ · a₁ + b₂
a₂ = Sigmoid(z₂) # 二分类问题
# 输出就是 a₂
前向传播的计算量主要来自矩阵乘法。这也是NPU最擅长的——矩阵乘累加运算。我设计的NPU里,专门有一个MAC(乘累加)阵列来加速这个操作。
3.2 反向传播
反向传播是训练的核心。它的思路是:计算输出误差,然后从输出层往回传,逐层更新权重。
具体步骤:
- 计算输出层的误差 δ_output
- 用链式法则,把误差传到隐藏层
- 根据误差计算梯度
- 用梯度更新权重
数学上,对于输出层第j个神经元:
δⱼ = ∂L/∂zⱼ = (aⱼ - yⱼ) · f'(zⱼ)
对于隐藏层第j个神经元:
δⱼ = f'(zⱼ) · Σ(Wₖⱼ · δₖ)
权重更新:
W_new = W_old - η · δ · a_input
关键理解:反向传播的本质就是链式法则。你不需要记住所有公式,但一定要理解"误差从后往前传"这个思想。在NPU设计里,反向传播需要额外的存储和计算资源,因为要保存中间层的激活值。
四、损失函数与梯度下降
4.1 损失函数
损失函数衡量模型预测和真实值的差距。不同的任务用不同的损失函数:
- 均方误差(MSE):L = ½(y_pred - y_true)²,适合回归问题
- 交叉熵损失:L = -[y·log(p) + (1-y)·log(1-p)],适合分类问题
- Hinge损失:L = max(0, 1 - y·y_pred),适合SVM
我个人的习惯是:回归用MSE,分类用交叉熵。交叉熵在分类问题上收敛更快,因为它的梯度更大。
4.2 梯度下降
梯度下降就是沿着损失函数的负梯度方向更新参数,让损失越来越小。公式很简单:
θ = θ - η · ∇L(θ)
其中η是学习率,∇L(θ)是梯度。
梯度下降有三种常见变体:
| 方法 | 每次更新用的数据 | 优点 | 缺点 |
|---|---|---|---|
| 批量梯度下降 | 全部数据 | 稳定、方向准确 | 慢、内存大 |
| 随机梯度下降 | 1个样本 | 快、能跳出局部最优 | 震荡大 |
| 小批量梯度下降 | batch个样本 | 折中、最常用 | 需要调batch size |
避坑指南:我曾经因为学习率设得太大,导致损失直接发散到无穷大。后来我养成了一个习惯——先用0.001的学习率试跑几个epoch,观察损失曲线。如果下降太慢就调大,如果震荡就调小。另外,学习率衰减也是个好技巧,训练到后期减小学习率,有助于收敛到更优解。
知识体系总览
下面这张图总结了本章的核心内容,我建议你把它打印出来贴在工位上:
嗯,以上就是神经网络基础的全部内容。这些概念是后续学习NPU设计的基石,尤其是前向传播和反向传播的数学原理,会直接影响NPU的架构设计。我个人建议你动手实现一个简单的全连接网络,哪怕只有几层,也能帮你把今天讲的内容串起来。
学习建议:别急着看NPU硬件设计,先把软件层面的神经网络搞明白。我见过太多人一上来就研究脉动阵列、数据流,结果连反向传播的链式法则都写不对。基础不牢,地动山摇。