端侧 AI · 15 天工程路线
目录首页 English
DAY 01

神经网络基础

从张量、层与损失函数建立神经网络直觉

建议阅读:约 18 分钟

学习目标

理解神经网络如何把输入张量变成可优化的预测,并能读懂一次前向与反向传播。

本章关键词

关键词解释ESP32 工程类比
张量带形状与数据类型的多维数值块,是模型输入、权重和中间结果的统一表示。像注明采样数、通道数和位宽的 DMA buffer。
Shape描述各维长度及其语义,例如 NCHW 的 batch、通道、高和宽。像协议帧的字段布局,顺序错了数据仍存在但含义全错。
激活函数在线性变换后加入非线性,使多层网络能表达复杂边界。像状态机中的条件分支,不能被简单合并。
MAC一次乘加运算;常用于粗略估算计算量。像内层 DSP 循环的工作次数,还需结合取数成本。

承上:回顾与定位

这是课程起点。你已经熟悉 ESP32 中“外设产生数据、DMA 搬运数据、任务消费数据”的链路;本章把这条链路抽象为神经网络的输入、层和输出,为后续讨论训练与部署建立共同语言。

从源头看今天

历史发展脉络

神经网络并不是突然从“大模型”时代出现的。它经历了从逻辑化神经元、可学习的线性分类器,到多层网络的信用分配,再到 GPU 上的大规模表示学习这一条长线。今天在端侧工程里看到的张量、算子、自动微分和部署图,正是这条历史逐步沉淀出的分层接口。

1943

神经元第一次被写成可计算的逻辑单元

McCulloch 与 Pitts 用阈值化单元和连接网络描述神经活动,证明一组简单单元可以组合出逻辑行为。它没有现代训练算法,却奠定了“复杂功能可由大量统一小单元连接而成”的计算视角。

McCulloch 与 Pitts 原始论文 ↗
1958

感知机把“连接”变成可由样本修正的权重

Rosenblatt 的感知机引入依据分类误差调整连接权重的机制,使神经网络从手工逻辑结构走向数据驱动学习。它主要解决线性可分问题,也让“模型参数是训练结果”成为后来网络的核心观念。

Rosenblatt 感知机原始论文 ↗
1986

反向传播让隐藏层收到可计算的误差信号

Rumelhart、Hinton 与 Williams 展示了误差反向传播如何逐层计算导数并调整权重。隐藏单元不再依赖人工指定含义,而能为任务形成内部表示;这让多层非线性网络拥有了实用的统一训练方法。

Nature 反向传播原始论文 ↗
2012

AlexNet 把深层表示学习推入大数据与 GPU 时代

AlexNet 在大规模图像数据上训练深卷积网络,并用 GPU 承担密集张量计算。突破不只来自网络结构,还来自数据、并行计算和正则化共同到位;从此参数量、MAC、显存与吞吐成为同一套工程预算。

AlexNet 原始论文 ↗
2019

命令式张量程序与自动微分成为日常工具

PyTorch 论文总结了命令式前端、动态图与高性能张量后端的组合。研究者可以像普通程序一样写控制流,同时由系统记录运算关系并求梯度;今天导出前先确认动态图路径,也源自这种灵活性。

PyTorch 系统原始论文 ↗
今天为什么仍然重要: 历史留下的工程启示是:网络能力来自可组合的非线性单元,学习来自可追踪的误差链,而性能来自张量程序对硬件的有效映射。端侧阅读一个模型时,应同时沿“数值如何变换”“梯度曾如何塑造权重”“部署时 buffer 如何存活”三条线检查。
借熟悉的系统建立直觉

类比图解

一座会根据试音记录自动调节的连锁调音台

想象一支乐队的多轨录音进入几排调音台。每排先按旋钮比例混合声道,再经过只允许某些信号通过的噪声门;末端监听员把成品与参考录音比较,并把“哪里偏响、哪里偏弱”的修正便签沿线路反向传回。重复试音后,旋钮位置就是学到的权重。

成排旋钮 权重与偏置决定各输入对下一层的贡献
噪声门 激活函数加入非线性并改变可表达的边界
参考监听 损失函数把预测与目标压缩成可优化的误差
反向便签 链式法则把输出误差分摊到每个参数

类比的边界: 这个类比只帮助理解信号变换与误差反馈。真实网络的一个“旋钮”会参与高维批量运算,梯度是局部导数的精确组合而不是主观意见;类比也无法表示参数共享、卷积布局和数值精度造成的耦合。

本章讲解

Shape 之外还要读 stride 与轴语义

同样是 3072 个数,[1,3,32,32] 与 [1,32,32,3] 的物理含义完全不同;即使 shape 相同,转置后的 view 也可能不连续。层通常按约定的轴做归一化、卷积或归约,广播又可能让错误 shape “合法运行”。读网络时应给每一轴标注 N/C/H/W、单位和 dtype,并在边界打印 stride,避免把可执行误当作语义正确。

非线性真正改变的是可组合的决策边界

仿射层只做旋转、缩放和平移,多层仿射映射仍可折成一次矩阵乘法。ReLU 等激活把输入空间切成多个区域,不同区域使用不同线性关系,于是层叠才会增长表达能力。工程上还要关注激活的输出范围:大面积恒为零可能意味着输入偏移或坏初始化,过大数值则可能在低精度部署时饱和。

损失是目标接口,梯度是逐层信用账本

损失函数规定什么差异值得惩罚;链式法则再把总误差对每个中间量的敏感度逐层相乘。梯度为零不等于已经学好,可能是饱和激活、断开的计算图或数值下溢;梯度爆大也会使一次更新越过有效区域。训练实验应同时记录 loss、梯度范数与参数更新量,而不是只盯最终准确率。

参数、MAC 与峰值激活回答三种不同问题

参数量近似回答权重需要多少存储,MAC 粗略回答进行了多少乘加,激活生命周期才决定运行时工作台有多大。卷积的权重可很少,却可能产生巨大的特征图;逐元素算子几乎没有参数,仍会完整读写张量。端侧预算应按执行顺序画出存活区间,再把 workspace、对齐填充和 I/O buffer 加入峰值。

用有限差分和中间张量验证“会算”

为一个极小网络固定权重与输入,保存每层输出、损失和解析梯度;再让单个参数增加与减少一个很小的 ε,用两次损失差近似导数。两者不符时,常见原因是转置、批量归约、原地修改或激活边界。部署侧不需要反向传播,但这份 golden tensor 可继续验证导出、量化和 kernel 是否保持前向语义。

让数据真正跑起来

动态过程演示

一拍训练脉冲:数据如何变成一次权重更新

播放器每前进一步就点亮一个张量及其所有者;前半程信号向右流,后半程梯度向左回传,最后权重刻度发生细小移动。

步骤 1 / 6

装载样本 · x:[B,2],y:[B,1]

高亮 batch 轴,并把输入送入第一层

观察点

先确定 shape、dtype 与轴语义,后续计算才有共同契约

循环 / 返回条件: 更新后的权重重新接收下一批样本;动画回到装载样本,并保留一条逐步下降的 loss 轨迹作为跨轮次状态。

查看静态全景图
输入 x
  │ Linear(Wx+b)
  ▼
激活 f(·) ──► 预测 ŷ ──► Loss(ŷ,y)
  ▲                         │
  └────── 梯度反传 ◄─────────┘

代码或命令示例

y = x @ W.T + b
y = relu(y)
loss = mean((y - target) ** 2)
loss.backward()

动手实验

用一个 2→3→1 的 MLP,手算每层形状、参数量和一次 MSE;再用任意框架验证梯度方向。
实验记录与导出

工程陷阱

避免误判: 只比较参数量会漏掉激活张量和工作区。MCU 上让系统崩溃的往往是推理瞬间的峰值 RAM,而不是 Flash 中权重文件的大小。

核心测试

完成 3 道单选题后提交;提交前不会显示答案。

1. 为什么两个线性层之间没有激活函数时可合并?
2. 输入 shape 为 [1, 3, 32, 32] 最先应该确认什么?
3. 对端侧延迟更直接的粗略计算指标是什么?

一起把本章讲清楚

在 GitHub 上讨论本章

登录 GitHub 后提问、补充实测或分享你的实现;评论会保存在本课程的 GitHub Discussions 中。

评论区需要 JavaScript;也可以直接打开 GitHub 讨论区: 打开 GitHub 讨论区 ↗

延伸阅读

启下:下一章如何使用本章能力

下一章把训练态、推理态、计算图、权重与格式契约连成一条可验证的模型交付链。

下一天: Day 2 · 训练与推理