7.1. 什么是神经网络¶
神经网络是一种行为从样本中学习而非靠手工编码得到的算法。同样的网络架构,喂入一百万张人脸图像,就学会检测人脸;同样的架构,喂入一百万张手部图像,就学会检测手部;同样的架构,喂入一个涵盖多种目标类别的带标签集合,就学会同时检测所有这些类别。在不同目标之间变化的只有权重,而权重由一个离线训练过程产生——该过程将网络的预测与带标签的样本进行比对,并不断调整权重直到二者相符。
7.1.1. 工作机制¶
神经网络是一系列层的堆叠。每一层将上一层的输出乘以一个权重矩阵,加上一个偏置向量,再对结果施加一个非线性函数。一层的输出就是下一层的输入。一张采集到的图像从堆叠的顶部进入,向下流经数十乃至数百层,最终从底部输出为一个张量,其各项描述了图像中包含的内容。
每一层权重所做的事取决于网络是基于什么训练的。视觉网络中靠前层的权重矩阵可能对一小段水平边缘产生响应;稍深一层的可能对一个角点产生响应;更深一层的可能对圆形的眼睛形状产生响应;最深的层可能对整张人脸的排布产生响应。这一切都不是手工编写的。训练过程在数百万个带标签样本上反复迭代,沿着损失函数将权重逐步向下推动,于是“边缘—角点—眼睛—人脸”这一层级结构便从数据中自然涌现出来。
一个以层堆叠形式呈现的小型分类网络。输入张量以采集图像的形状从顶部进入,向下流经各层,每一层都对张量的维度进行变换。底部的输出张量每个类别对应一项。检测网络和关键点网络具有相同的层堆叠形式;改变的只是输出张量的解释方式。¶
网络的架构——各层如何排布、哪些运算将它们连接起来——决定了网络能够做什么。权重则是网络已经学到的东西。摄像头在其中承担的部分,是加载训练所产生的权重文件,并运行与训练器相同的算术运算,只不过作用对象是采集到的帧,而非训练集。
7.1.2. 输入什么,输出什么¶
网络的两端都是张量——多维数字数组,正是 numpy 章节刚刚介绍过的那类对象。视觉网络的输入张量是采集到的图像被重新排布成网络所期望的布局:通常是一个 (B, H, W, C) 四元组形状,其中 B 是批次维度(在摄像头上始终为 1,因为每次处理一帧),H 和 W 是网络期望的像素高度和宽度,C 是通道数(RGB 网络为 3,灰度网络为 1)。
输出张量取决于网络的用途:
分类网络产生一个一维的置信度分数张量,每个类别一个分数。最大分数所在的索引即为预测类别。大多数摄像头出厂自带的、由 MobileNet 衍生而来的人物检测器就是这种形式:两个分数,一个对应“person”,一个对应“not person”。
检测网络产生一个二维张量,其各项描述了一组边界框以及类别概率。YOLOv8 就是这种形式:一个
(84, N)张量,其中 84 行里有 4 行是框回归值,其余 80 行是各类别概率,并在N个锚点位置上重复。关键点网络产生一个张量,其各项是命名地标的像素位置。MediaPipe 人脸关键点模型就是这种形式:每检测到一张人脸有 468 个关键点。
分割网络产生一个二维张量,其各项是逐像素的类别标签——维度与输入相同,每个位置都带有一个类别索引。
回归网络产生单个数字或一个短数字向量——例如深度估计值、角度或温度。
每种形式在摄像头上都有自己的后处理器,将原始输出张量转换回应用程序其余部分所使用的结果形式——边界框、关键点列表、类别标签、数值估计值。后处理器是应用侧的代码,它了解网络输出的布局;网络本身只是产生张量的算术运算。
7.1.3. 为什么这能在摄像头上运行¶
有两项算术技术使其在微控制器级别的器件上变得可行。第一项是量化。训练在 32 位浮点运算中进行;而对大多数网络而言,推理可以在 8 位整数运算中进行,几乎不损失精度。8 位权重只占四分之一的存储空间,运行速度比 32 位浮点数快好几倍。摄像头出厂自带的每个模型都已在离线状态下完成量化。
第二项是硬件加速。微控制器的 CPU 需要一条指令一条指令地缓慢处理的同一批算术运算,神经网络加速器可以一次性运行数百个操作。较新的摄像头(AE3 和 N6)配备了专用的神经处理单元(NPU)——一个集成在 SoC 上的张量加速器——它能把一个在 CPU 上需要一秒才能跑完的模型变成只需几十毫秒即可运行。推理引擎章节将介绍摄像头在其中承担的部分是什么样子。
7.1.4. 本章涵盖的内容¶
训练不是摄像头的工作。一个训练好的模型以 .tflite 文件的形式来到摄像头上;摄像头加载它,将每一帧采集到的图像送入其中运行,并把得到的张量解码为应用程序可以据以行动的结果。后续内容都是关于这些步骤的:
加载并检视一个模型;
存放模型文件的闪存分区;
一次推理调用的四个阶段;
实际执行算术运算的引擎;
以及将输出张量转换回边界框、关键点或类别列表的后处理器。
图像章节中的各个检测器,每一个都是针对某个特定目标设计的。本章其余部分介绍的检测器则是从数据中训练出来的,由同一个引擎运行脚本所加载的任意模型。随之而来的工作流变化——针对特定目标的算法被针对特定目标的权重文件所取代——正是接下来要展开阐述的内容。