第二章 神经网络基础回顾
好,咱们正式开始。这一章我打算带大家快速过一遍神经网络的基础。你可能会问:做NPU硬件,为什么还要学这些?
原因很简单——不懂算法,你设计出来的加速器就是瞎蒙。我在项目里见过太多硬件工程师,把乘法器堆得飞快,结果发现算法根本用不上那种并行度。嗯,那叫一个尴尬。
2.1 感知机与多层感知机
先说感知机。这是神经网络的老祖宗,1957年由Frank Rosenblatt提出。说白了,就是一个线性二分类器。
// 感知机的数学形式
y = sign(w·x + b)
它做的事情很简单:输入特征加权求和,加上偏置,过个阶跃函数,输出0或1。我在做第一版NPU原型时,就用感知机做过一个简单的AND门验证——结果发现单层感知机连XOR都搞不定。
核心局限:单层感知机只能解决线性可分问题。XOR这种非线性问题,它无能为力。
怎么解决?加层。这就是多层感知机(MLP)的由来。
MLP在输入和输出之间插入了隐藏层,每个隐藏层都带非线性激活函数。常用的激活函数有Sigmoid、Tanh、ReLU。我个人习惯用ReLU,原因很简单——计算快,梯度不饱和。
// 两层MLP的前向计算
h = ReLU(W1·x + b1)
y = softmax(W2·h + b2)
你想想看,MLP本质上就是在做特征变换。把原始输入空间映射到更高维的隐空间,让数据变得线性可分。我在做NPU的矩阵计算单元时,MLP的矩阵乘法模式就是最基础的算子模板。
硬件视角:MLP的计算核心是矩阵-向量乘法和激活函数。NPU设计时,这两个模块要重点优化。
2.2 卷积神经网络(CNN)核心算子
CNN是图像处理的标配。它的核心思想是局部连接和权值共享。说白了,就是用一个小窗口(卷积核)在图像上滑动,提取局部特征。
卷积操作其实不复杂:
// 2D卷积示例(简化版)
for (int i = 0; i < H_out; i++) {
for (int j = 0; j < W_out; j++) {
sum = 0;
for (int m = 0; m < K; m++) {
for (int n = 0; n < K; n++) {
sum += input[i+m][j+n] * kernel[m][n];
}
}
output[i][j] = sum;
}
}
这段代码看着简单,但硬件实现时坑很多。我曾经在项目里遇到过卷积核尺寸不匹配的问题——输入特征图和卷积核的通道数对不上,结果仿真跑出来全是NaN。排查了整整两天。
CNN里还有几个关键算子:
- 池化(Pooling):最大池化、平均池化。作用是降采样,减少计算量。
- 批归一化(BatchNorm):稳定训练,加速收敛。推理时可以融合进卷积层。
- 全连接层:其实就是MLP,放在CNN尾部做分类。
这里有个避坑指南:我曾经在NPU设计时,把卷积的stride和padding搞混了。stride是步长,控制窗口移动的间隔;padding是填充,控制输出尺寸。这两个参数直接影响硬件的数据流调度,千万要分清楚。
| 参数 | 作用 | 硬件影响 |
|---|---|---|
| stride | 卷积核移动步长 | 决定数据读取间隔 |
| padding | 输入边缘填充 | 影响输出尺寸和存储 |
| dilation | 空洞卷积的扩张率 | 改变感受野,增加计算复杂度 |
注意:CNN的硬件加速器设计,核心是数据复用。输入特征图、卷积核、输出特征图,这三者的数据流要精心设计,否则带宽会爆炸。
2.3 循环神经网络(RNN)与LSTM
RNN处理的是序列数据。它的特点是:当前时刻的输出,依赖于上一时刻的隐藏状态。
// RNN单元
h_t = tanh(W_hh * h_{t-1} + W_xh * x_t + b_h)
y_t = softmax(W_hy * h_t + b_y)
看着简单吧?但RNN有个致命问题——梯度消失/爆炸。序列长了,前面的信息传不过来。我在做语音识别NPU时,试过用普通RNN处理10秒的音频,结果后面完全学不到前面的内容。
LSTM就是来解决这个问题的。它引入了门控机制:遗忘门、输入门、输出门。说白了,就是让网络自己决定:哪些信息要记住,哪些要忘掉。
// LSTM单元(简化版)
f_t = sigmoid(W_f * [h_{t-1}, x_t] + b_f) // 遗忘门
i_t = sigmoid(W_i * [h_{t-1}, x_t] + b_i) // 输入门
o_t = sigmoid(W_o * [h_{t-1}, x_t] + b_o) // 输出门
c_t = f_t * c_{t-1} + i_t * tanh(W_c * [h_{t-1}, x_t] + b_c)
h_t = o_t * tanh(c_t)
你想想看,LSTM的计算量比普通RNN大了好几倍。每个时间步要算4个门控的矩阵乘法。硬件设计时,我建议把LSTM的矩阵运算和逐元素运算分开——矩阵乘法用专门的MAC阵列,逐元素运算用ALU流水线。
硬件优化点:LSTM的四个门控矩阵可以合并成一个大的矩阵乘法,减少访存次数。我在项目里用这个技巧,把LSTM的推理速度提升了30%。
2.4 Transformer与注意力机制
Transformer是2017年Google提出的,现在几乎统治了NLP领域。它的核心是注意力机制(Attention)。
注意力机制说白了就是:让模型在计算某个位置的输出时,能够关注到输入序列中所有位置的信息,并给每个位置分配不同的权重。
// Scaled Dot-Product Attention
Attention(Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * V
Q是查询,K是键,V是值。Q和K的点积表示相似度,softmax归一化成权重,再加权求和V。这个操作在硬件上就是矩阵乘法加softmax。
Transformer里还有个关键设计——多头注意力(Multi-Head Attention)。把Q、K、V分别投影到多个子空间,并行计算注意力,再拼接起来。这样做的好处是:模型可以从不同角度关注信息。
我在设计NPU时,专门为Transformer做了优化:
- 矩阵乘法单元:支持大尺寸矩阵分块计算
- Softmax加速器:用查找表+线性插值实现快速指数运算
- LayerNorm:用定点数实现,避免浮点开销
个人经验:Transformer的推理瓶颈往往在注意力层。QK^T的计算复杂度是O(n²),序列长度n一长,计算量就爆炸。我建议在硬件里加入稀疏注意力支持,只计算部分位置的注意力权重。
还有一个重要概念——位置编码(Positional Encoding)。因为Transformer没有循环结构,它不知道输入的顺序。所以需要显式地加入位置信息。常用的有正弦余弦编码和可学习的位置编码。
嗯,到这里,神经网络的基础就回顾完了。这些内容看似简单,但都是NPU设计的基石。下一章,我们会深入NPU的微架构设计,从计算单元开始讲起。
最后提醒:别小看这些基础算子。我见过太多人一上来就搞大模型加速器,结果连卷积的数据复用都搞不清楚。基础不牢,地动山摇。