Ch03: 前置知识检查清单
Part 1: 导读总纲 前置章节:Ch02: 阅读路线图——三条路径任你选 后续章节:Ch04: 神经网络推理的本质——从训练到部署(Part 2 开篇)
引言
学任何东西之前,最痛苦的不是内容难,而是”以为自己准备好了,结果读到一半才发现缺了某块基础知识,不得不中断回去补”。这就像拼拼图——如果缺了几块边角料,你可能拼到一半才发现整个区域对不上。
本章的目的是帮你在开始正式阅读前,一次性检查完所有前置知识。每一项我都会告诉你:这项知识在后续哪个 Part 会用到、需要达到什么程度、怎么快速自检、如果不达标该如何补。
结论先行:如果你能看懂简单的 C 代码(for 循环、指针、结构体)、知道神经网络是”层层叠加的函数”、会用终端敲命令——你就可以开始读 Part 2 了。Part 3 及之后的内容对基础要求逐步提高,但你完全可以边读边补,不需要把所有前置都学完才开始。
1. Part 2 前置(几乎为零)
Part 2 讲的是移动端推理引擎的通用概念——计算图是什么、内存管理是什么、算子是什么。这些内容的写作面向零经验读者,所以前置要求几乎为零。
你需要的唯一前置:能读懂中文技术文章。
更具体地说,Part 2 会出现一些英文术语(如 Graph、Tensor、Operator),但每个术语首次出现时都会给出中文解释和日常类比。你不需要事先知道这些词的含义。
可能有帮助但非必需的背景:
如果你用过 PyTorch 或 TensorFlow 写过哪怕一个 “Hello World” 级别的模型(比如 MNIST 手写数字识别),你会对”模型由层组成、每层做一种计算”这个概念有直观体验。但如果没有,Part 2 会从零开始解释,不会跳过。
如果你了解基本的数据结构概念(数组、图/tree/DAG),读 Part 2 会更轻松。但同样,这些概念会在用到时解释。
自检问题:
- 你能读懂一篇中文的技术博客吗?(比如知乎上讲 CPU 缓存的科普文章)
- 你知道”函数”是什么意思吗?(输入一些东西,按规则处理,输出结果)
如果两个都能答”是”,你就可以直接开始 Part 2。
2. Part 3 前置:C++ 基础、神经网络基础、命令行基础
Part 3 开始深入各个框架的具体实现。这意味着你会看到源码片段、会需要自己编译项目、会需要理解神经网络的基本组件。
2.1 C++ 基础
为什么需要:六个推理引擎中有五个是用 C++ 写的(ncnn、MNN、TNN、Paddle-Lite、MACE),ONNX Runtime 的核心也是 C++。阅读它们的源码需要看懂 C++ 代码。
需要达到的程度:能读懂 C++ 代码,不需要能写出优雅的 C++。具体来说——
能看懂的语法特性:
- 基本数据类型(int、float、指针、引用)
- 类(class)的定义、构造函数、析构函数、成员函数
- 继承和虚函数(virtual)——这在框架代码中非常常见,算子基类 + 具体算子子类的结构到处都是
- 模板(template)的基本用法——能看懂
vector<int>和简单的函数模板就够了 - 命名空间(namespace)
- 智能指针(shared_ptr、unique_ptr)——知道它们自动管理内存就行
- 枚举(enum)和宏定义(#define)
不需要掌握的:
- 高级模板元编程(SFINAE、variadic templates)
- C++17/20 的新特性(结构化绑定、concepts)
- STL 算法库的全部内容
- 异常处理的高级用法
自检问题:
问题 1:下面这段代码做了什么?
class Layer {
public:
virtual int forward(const Mat& input, Mat& output) = 0;
virtual ~Layer() {}
};
class Convolution : public Layer {
public:
int forward(const Mat& input, Mat& output) override;
private:
Mat weight;
Mat bias;
};
如果你能说出”定义了一个抽象基类 Layer,它有一个纯虚函数 forward;Convolution 继承 Layer 并实现 forward,有 weight 和 bias 两个成员变量”——你的 C++ 基础足够了。
问题 2:const Mat& 中的 const 和 & 分别是什么意思?
如果你能说出”const 表示这个参数不会被修改,& 表示传引用(不拷贝数据)”——没问题。
问题 3:virtual 和 override 的作用是什么?
如果你能说出”virtual 表示这个函数可以被子类重写,override 显式标记子类正在重写父类的虚函数”——完全够用。
如果不达标,学习建议:
推荐路径(约 1 周):找一本 C++ 入门书(如《C++ Primer》前 10 章)或者看一套视频教程,重点学习类和继承这两章。不需要做大量练习题——你的目标是”能读懂别人的代码”而非”能从零写出复杂程序”。
快速补救路径(约 2 天):如果你会 C 或 Java,只需要补 C++ 特有的部分——类(相当于 Java 的 class,但有析构函数和多继承)、引用(Java 没有)、模板(相当于 Java 泛型但更强大)。网上搜”C++ for Java/C programmers”能找到很多对比教程。
2.2 神经网络基础
为什么需要:推理引擎跑的就是神经网络模型。不理解神经网络的基本组件,就不理解推理引擎为什么需要做那些事情。
需要达到的程度:理解概念即可,不需要能手推反向传播的数学公式(那是训练框架的事,和推理引擎关系不大)。
需要理解的概念:
-
张量(Tensor):多维数组。一张 RGB 图片是一个 3 维张量(高度 x 宽度 x 3 通道)。一批图片是 4 维张量(批次 x 通道 x 高度 x 宽度)。推理引擎里所有数据都以张量形式流动。
-
层 / 算子(Layer / Operator):对张量做一种特定计算的函数。卷积层(Convolution)在图片上滑动一个小窗口做加权求和;全连接层(FullyConnected / Dense)做矩阵乘法;ReLU 把所有负数变成零。推理引擎需要为每种算子实现一个高效版本。
-
计算图(Computation Graph):把所有层按依赖关系连接起来的有向图。数据从输入层流入,经过层层处理,最终到达输出层。推理引擎的核心工作之一就是高效执行这个图。
-
卷积(Convolution):最重要的一种算子。你不需要手算卷积,但需要知道它”用一个小滤波器在输入上滑动,每个位置做点乘求和”这个基本操作,以及它的几个参数(kernel size、stride、padding)影响输出尺寸。
-
模型文件:训练完成后,所有层的权重参数(数字)被保存到一个文件里。推理引擎加载这个文件,就知道每一层该怎么计算了。不同引擎使用不同的模型文件格式。
不需要理解的:
- 反向传播的数学推导
- 各种优化器(SGD、Adam)的工作原理
- 损失函数的设计
- 训练技巧(学习率调度、正则化)
自检问题:
问题 1:一张 224x224 的 RGB 图片,作为张量它的形状(shape)是什么?
如果你能说出 “3x224x224”(CHW 格式)或 “224x224x3”(HWC 格式),并且知道 3 代表三个颜色通道——没问题。
问题 2:卷积层做了什么事?用一句话描述。
如果你能说出类似”用一个小矩阵(卷积核)在输入图片上滑动,每个位置做逐元素相乘再求和,得到输出的一个像素值”——足够了。
问题 3:ReLU 做了什么?
如果你能说出”把负数变成零,正数不变”——完美。
如果不达标,学习建议:
推荐路径(约 3 天):看 3Blue1Brown 的”Neural Networks”系列视频(有中文字幕),然后用 PyTorch 跑一个 MNIST 手写数字识别的教程。不需要理解每一行代码——目标是对”模型由层组成、层有参数、推理是前向传播”建立直觉。
快速补救路径(约 4 小时):如果你时间很紧,直接搜”卷积神经网络 入门 可视化”,看那些有动图展示卷积滑动过程的文章。配合 netron.app 网站打开一个 .onnx 模型文件看看计算图长什么样——这比读论文直观得多。
2.3 命令行基础
为什么需要:编译推理引擎、运行 benchmark、调试性能——这些全在命令行里完成。没有任何一个推理引擎提供 GUI 操作界面。
需要达到的程度:能在终端里完成日常开发工作。
需要会的操作:
- 目录导航:cd、ls、pwd
- 文件操作:cp、mv、rm、mkdir
- 文本查看:cat(短文件)、less(长文件)、grep(搜索内容)
- 进程管理:Ctrl+C 终止运行中的程序
- 环境变量:知道 PATH 是什么、会 export 设置变量
- 管道和重定向:
命令1 | 命令2、> output.txt - 基本的 shell 脚本阅读能力(能看懂 for 循环和 if 判断)
不需要会的:
- 高级 shell 编程(awk、sed 的复杂用法)
- 系统管理(用户权限、systemd、网络配置)
- 编辑器之战(vim 还是 emacs 无所谓,能编辑文件就行)
自检问题:
问题 1:如何在当前目录下递归搜索所有 .cpp 文件中包含 “forward” 的行?
如果你能写出 grep -r "forward" --include="*.cpp" . 或类似命令——没问题。
问题 2:cmake .. -DCMAKE_BUILD_TYPE=Release && make -j$(nproc) 这条命令做了什么?
如果你能说出”用 cmake 生成 Release 模式的构建文件,然后用所有 CPU 核心并行编译”——足够了。
问题 3:如何查看一个程序的实时输出同时把输出保存到文件?
如果你能说出 ./program | tee output.log 或类似方案——没问题。
如果不达标,学习建议:
推荐路径(约 2 天):找一个”Linux 命令行入门”教程(推荐 The Missing Semester of Your CS Education 课程的前两节),跟着敲一遍。命令行技能的关键在于肌肉记忆——你需要实际敲,不能光看。
快速补救路径(约半天):如果你用过 Windows 的 cmd 或 PowerShell,命令行的概念你已经懂了,只需要学 Linux/macOS 对应的命令名称。搜”Windows to Linux command cheat sheet”对照学习。
3. Part 4 前置:计算机体系结构基础
Part 4 深入硬件适配——为什么 ARM NEON 要用 128 位寄存器、GPU 的 warp 调度是什么、DSP 的 VLIW 架构有何特别。理解这些内容需要一些计算机体系结构的基础知识。
需要达到的程度:理解硬件如何执行指令的基本图景,不需要能设计 CPU。
需要理解的概念:
-
CPU 如何工作的基本模型:取指令→解码→执行→写回。知道”指令”是 CPU 能执行的最小操作单元(加法、乘法、从内存加载数据)。
-
寄存器:CPU 内部的超高速存储单元,通常只有几十个,每个能存一个数字(32 位或 64 位)。计算必须先把数据加载到寄存器,算完再写回内存。
-
缓存层次(L1/L2/L3):CPU 和主内存之间的高速缓冲。L1 最快但最小(几十 KB),L3 最大但相对慢(几 MB)。推理引擎的很多优化都是为了让数据尽量留在缓存里——访问内存比访问 L1 缓存慢 100 倍。
-
SIMD(单指令多数据):一条指令同时对多个数据做相同操作。比如 ARM NEON 的 128 位寄存器可以同时处理 4 个 32 位浮点数。推理引擎大量使用 SIMD 来加速矩阵运算。
-
并行性的几个层次:指令级并行(一个核心同时执行多条指令)、数据级并行(SIMD)、线程级并行(多个核心各跑一个线程)。
-
GPU vs CPU 的基本差异:CPU 有少量强大的核心(适合复杂逻辑),GPU 有大量简单的核心(适合相同操作应用到大量数据)。
不需要理解的:
- 流水线冲突的具体解决方案(forwarding、stalling)
- 分支预测算法的具体实现
- 缓存一致性协议(MESI)
- 乱序执行的微架构细节
- FPGA 设计
自检问题:
问题 1:为什么访问数组时”顺序遍历”比”随机跳跃访问”快得多?
如果你能说出”因为顺序访问利用了缓存的空间局部性——加载一条缓存行时相邻数据也被一起加载进来”——很好。如果你能进一步提到”预取器也更容易预测顺序访问模式”——更好,但不必需。
问题 2:ARM NEON 的 128 位寄存器一次能处理几个 float32?几个 int8?
如果你能算出”128/32=4 个 float32,128/8=16 个 int8”——没问题。
问题 3:为什么 GPU 做矩阵乘法比 CPU 快很多?
如果你能说出类似”矩阵乘法是高度并行的计算——每个输出元素可以独立计算,而 GPU 有成千上万个核心可以同时算不同的输出元素”——足够了。
如果不达标,学习建议:
推荐路径(约 1 周):看 CMU 的 Computer Systems: A Programmer’s Perspective(CSAPP)中关于存储器层次和处理器体系结构的章节(第 5-6 章)。这本书的好处是从程序员视角讲硬件,不会陷入电路设计的细节。
快速补救路径(约 2 天):搜索”程序员需要知道的 CPU 缓存知识”和”SIMD 编程入门”两篇主题的文章。看完后再搜”ARM NEON intrinsics 入门”了解一下 NEON 编程的基本样子。你不需要能写 NEON 代码,但需要能看懂 vld1q_f32(加载 4 个 float)、vmulq_f32(4 个 float 相乘)这种 intrinsic 函数的含义。
补充建议:CSAPP 的知识在这里是”够用就行”的标准。如果你觉得 CSAPP 太厚(确实很厚),可以只看第 5 章”优化程序性能”和第 6 章”存储器层次结构”中的核心概念部分,跳过习题和扩展阅读。
4. Part 5 前置:Python / CMake / Git
Part 5 涉及高级优化主题,包括模型转换脚本(Python)、构建系统配置(CMake)、和源码管理(Git)。
4.1 Python
为什么需要:模型转换工具、量化脚本、benchmark 自动化脚本通常用 Python 写。ONNX 模型的操作(加载、修改、验证)几乎全在 Python 里完成。
需要达到的程度:能读懂 Python 脚本、能修改参数运行,不需要能从零写复杂程序。
需要会的:
- 基本语法(变量、函数、类、导入模块)
- NumPy 的基本操作(创建数组、reshape、切片、矩阵运算)
- 能读懂 argparse(命令行参数解析)
- 能安装 pip 包
不需要会的:
- Web 框架(Flask/Django)
- 异步编程(asyncio)
- 高级 Python 特性(装饰器、metaclass、descriptor)
自检问题:
问题 1:import numpy as np; a = np.zeros((3, 224, 224), dtype=np.float32) 创建了什么?
如果你能说出”一个形状为 3x224x224 的全零 float32 数组(代表一张黑色 RGB 图片)”——没问题。
问题 2:能读懂一个简单的模型转换脚本吗?比如加载 ONNX 模型、修改某个节点的属性、保存。
如果看到 import onnx; model = onnx.load("model.onnx") 你不会困惑——够了。
如果不达标,学习建议:Python 的入门非常快(约 2-3 天对有编程基础的人)。推荐直接看 Python 官方教程的前 9 章,然后做一个 NumPy 的 10 分钟入门教程。
4.2 CMake
为什么需要:六个推理引擎全部使用 CMake 作为构建系统。编译它们、配置编译选项(启用/禁用某个后端、设置目标平台)、添加新文件——都需要修改 CMakeLists.txt。
需要达到的程度:能读懂 CMakeLists.txt 的结构、能修改编译选项,不需要能从零搭建复杂的 CMake 项目。
需要理解的概念:
- CMakeLists.txt 的基本结构:
cmake_minimum_required、project、add_library、add_executable、target_link_libraries - 变量和选项:
set(VAR value)、option(USE_VULKAN "Enable Vulkan" OFF) - 条件编译:
if(USE_VULKAN) ... endif() - 查找依赖:
find_package - 构建类型:Debug vs Release
- 交叉编译的概念:在 x86 电脑上编译出 ARM 手机能运行的程序
不需要掌握的:
- CMake 的现代目标属性(target properties)体系的全部细节
- Generator expressions 的复杂用法
- CPack 打包
- CTest 的高级功能
自检问题:
问题 1:下面这段 CMake 做了什么?
option(NCNN_VULKAN "build with vulkan" OFF)
if(NCNN_VULKAN)
find_package(Vulkan REQUIRED)
target_link_libraries(ncnn PRIVATE Vulkan::Vulkan)
endif()
如果你能说出”定义了一个选项 NCNN_VULKAN 默认关闭,如果开启则查找 Vulkan 库并链接到 ncnn”——没问题。
问题 2:如何编译 ncnn 并启用 Vulkan 支持?
如果你能写出 cmake .. -DNCNN_VULKAN=ON && make 或类似命令——足够了。
如果不达标,学习建议:CMake 的官方教程(Tutorial)写得不错,跟着做前 3 步就够用了(约半天)。另外推荐搜”Modern CMake for C++”看一下社区的最佳实践总结。但说实话,大部分时候你只需要模仿项目中已有的 CMakeLists.txt 的写法就行——推理引擎的 CMake 配置通常有很好的模式可循。
4.3 Git
为什么需要:阅读源码需要 clone 仓库、切换版本(checkout tag)、查看提交历史(理解某个设计是如何演进的)。贡献代码需要 fork、branch、commit、push、提 PR。
需要达到的程度:日常开发级别的 Git 操作。
需要会的操作:
- clone、pull、fetch
- branch、checkout、merge
- add、commit、push
- log、diff、blame(看某行代码是谁在什么时候改的——对理解设计演进很有用)
- fork + PR 的工作流
不需要会的:
- rebase 的复杂场景(interactive rebase)
- submodule 的高级操作
- git internals(对象模型、packfile)
- 复杂的合并冲突解决
自检问题:
问题 1:如何查看 ncnn 仓库中 src/layer/convolution.cpp 文件的最近 5 次修改?
如果你能写出 git log -5 src/layer/convolution.cpp 或类似命令——没问题。
问题 2:你 fork 了 ncnn,在自己的分支上改了代码,如何把原仓库的最新改动同步过来?
如果你知道”添加 upstream remote、fetch upstream、merge 或 rebase”这个流程——足够了。
如果不达标,学习建议:推荐 Pro Git 书(免费在线阅读)的前 3 章(约 1 天)。Git 的概念不多,核心就是:工作区→暂存区→本地仓库→远程仓库,理解这个四层模型后其他操作都好理解。
5. Part 6 前置:Part 1-5
Part 6 是综合实战部分——技术选型、性能调优、开源贡献实践。它的前置就是前面所有部分的知识。
这不是说你必须”精通”Part 1-5 的所有内容才能读 Part 6。更准确的说法是:Part 6 会引用和综合前面章节的概念,你需要对这些概念有”工作级别”的理解——遇到某个术语时能回忆起它大概是什么意思,需要细节时知道回哪个章节去查。
Part 6 的具体内容和对应前置:
技术选型指南——需要理解各框架的设计定位和适用场景(Part 3)以及不同硬件的特性(Part 4)。你需要能回答”在这个场景下为什么选 A 不选 B”这类问题。
性能调优实践——需要理解计算图优化(Part 2)、硬件特性(Part 4)、量化和编译优化(Part 5)。你需要知道性能瓶颈可能出在哪里(计算?内存?调度?)以及对应的优化方向。
开源贡献实践——需要理解至少一个框架的代码结构和设计哲学(Part 3)、Git 工作流(Part 5 前置)、以及项目的 contributing guidelines。
自检方法:在开始读 Part 6 之前,尝试回答以下问题。不需要回答得很精确,但如果完全没有思路,说明需要回去复习对应章节。
- 如果一个 App 需要在高通和联发科芯片上都跑一个人脸检测模型,你会优先考虑哪几个框架?为什么?
- 一个模型在手机上推理太慢,可能的瓶颈有哪些?你会按什么顺序排查?
- 给 ncnn 写一个新算子的 PR,需要改哪些文件?PR 的代码风格需要注意什么?
如果对这些问题有基本思路(哪怕不完整),你就可以读 Part 6。
6. 本章小结
回顾本章的核心要点:
- Part 2 几乎零前置——能读中文技术文章就行
- Part 3 需要三项基础:C++(能读懂类继承和虚函数)、神经网络(知道张量、层、卷积是什么)、命令行(能编译和运行程序)
- Part 4 需要计算机体系结构基础(缓存层次、SIMD、GPU vs CPU 的区别)
- Part 5 需要 Python(能读脚本)、CMake(能配置编译选项)、Git(能 clone/branch/PR)
- Part 6 需要 Part 1-5 的综合理解
- 所有前置都是”能读懂”级别,不是”能精通”级别
- 可以边读边补——遇到看不懂的地方再针对性补充,不需要把所有前置学完才开始
关键原则:不要让前置知识成为阻碍你开始的借口。先开始读,遇到障碍再补,这是最高效的学习策略。
下一部分(Part 2)我们正式进入技术内容——从所有推理引擎共享的通用架构概念开始。
导读目录:README.md 上一章:Ch02: 阅读路线图——三条路径任你选 下一章:Ch04: 神经网络推理的本质——从训练到部署