第三编 Section End
搭建一个工厂缺陷检测系统:神经网络与深度学习如何走进真实视觉工程
第三编到这里,读者已经看到,机器学习之所以会进入神经网络与深度学习阶段,根本原因在于第二编暴露出的表示瓶颈越来越难以回避。特征工程可以继续做,核方法也可以继续试,但只要表示仍然主要依赖人工设计,工程团队就会反复遇到同一个问题:当任务结构越来越复杂、输入维度越来越高、局部模式越来越多时,程序员到底要手工设计多少层特征,才能勉强把问题变成一个可分的形式?
这一节的目的,就是把这条工程上的转折线写得更具体一些。这里选择的是一个真实而典型的视觉任务:为工厂产线建立缺陷检测系统。这个系统的目标是,给定一张产品图片,判断它是正常品还是缺陷品;若是缺陷品,还希望识别缺陷类别,例如划痕、裂纹、污点、尺寸异常或装配偏移。这个案例非常适合作为第三编结尾的工程实例,因为它能自然把第三编的几条主线都串起来:为什么手工特征会很快不够用,为什么多层网络会成为新的表示工具,反向传播和梯度下降在工程里究竟意味着什么,卷积网络为什么会在视觉任务上形成真正的突破,以及训练技巧为什么会成为系统能否真的训起来的关键,不会只是附属细节。
我们可以把自己放进一个真实团队里来想象这个需求。生产部门给出的表述通常不会是“请建立一个多层复合函数系统”,而会更直接:产线上人工质检压力太大,漏检会导致次品流出,误检又会增加返工成本,希望系统能在拍到产品图像后快速给出缺陷判断,并在必要时帮助人工复核。于是,一个典型的第三编问题就出现了:当输入不再是整理好的表格特征,而是高维图像时,机器到底该怎样自己学出有用表示?
1. 先把任务形式和误差代价说清楚
程序员接到这个需求后,第一步仍然是先把任务定义清楚,搭网络要放在后面。因为“缺陷检测”在业务语言里听起来像一个词,但在建模层面可能对应好几种不同问题。
最简单的形式是二分类,也就是只判断“正常”还是“缺陷”。再复杂一点,则是多分类,即不仅要判断是否缺陷,还要区分缺陷类型。若继续扩展,甚至可能变成目标检测或分割问题,因为有时业务方不仅想知道有没有问题,还想知道缺陷出现在哪个区域、占多大面积。
这一步在工程里非常关键,因为不同任务形式对应不同输出空间、不同损失函数和不同验收逻辑。设输入图像记为 $x$,若做二分类,则标签可以写成
其中 $y=1$ 表示缺陷,$y=0$ 表示正常。若做多分类,则标签可写成
其中 $C$ 表示类别数。
但比“标签怎么写”更重要的,是误差代价怎么理解。工厂场景中,漏检和误检通常代价并不对称。漏检意味着缺陷品流出,可能直接造成客户投诉、召回成本或品牌损失;误检则会提高人工复查和返工成本。程序员如果在项目一开始没有把这个代价结构问清楚,那么后面的训练指标和阈值选择很容易完全偏离真实目标。
也就是说,第三编的工程起点仍然是在做数学建模,只不过这里的建模重点已经不再是“特征如何组织”,而是“高维输入的输出形式和错误代价究竟如何定义”。这正是深度学习项目常常给人一种“模型很复杂,但问题定义反而更重要”的原因。
2. 数据准备:图像不是表格,标注也不是附属工作
任务形式明确之后,下一步就是准备数据。对于缺陷检测系统,最基本的数据来源通常是产线上不同时间、不同工位、不同光照和不同设备条件下拍摄的产品图像。若图像采集条件单一,模型很可能只学会某一种拍摄环境下的规律,而一旦上线环境略有变化,就会迅速失效。
于是,程序员要做的第一件事,是先建立采集规范和标注规范。采集规范决定图像分辨率、视角、光照、背景和触发时机;标注规范则决定哪些缺陷算作缺陷、缺陷类别如何划分、边缘样本怎么处理、多人标注不一致时如何仲裁。网络训练通常要等这两件事稳定之后再开始。
从数学上看,这一步仍然是在构造样本对
但和前两编不同的是,这里的 $x_i$ 不再是低维结构化向量,而是高维张量对象,例如
其中 $H$ 表示图像高度,$W$ 表示图像宽度,$C$ 表示通道数。也就是说,输入空间已经从前面几编里的向量空间扩展成了具有明显空间结构的对象空间。
这一步会很快让程序员感觉到,第二编那种“先人工设计一批特征,再喂给模型”的工作方式开始变得非常吃力。因为对于图像而言,边缘、纹理、局部形状、区域组合关系很难靠手工统计量完整表达。哪怕程序员能够设计出一些简单的边缘检测或纹理特征,它们也很难适应复杂缺陷形态、背景变化和照明变化。
与此同时,数据准备还会碰到几个很典型的工程问题。第一是类别不平衡。产线上正常品通常远多于缺陷品,而某些稀有缺陷更可能少得几乎看不到。第二是样本相关性。若同一批次产品连续拍摄很多相似图像,很容易让训练集和测试集过于接近。第三是标注噪声。人工质检本身就可能存在边界模糊和主观差异,这会直接影响模型学习。
这说明,第三编中的“深层表示学习”并不是一开始就从网络结构里冒出来的。相反,它首先是由输入对象的复杂性逼出来的。正是因为图像数据不再适合靠人工做完表示,团队才会越来越迫切地需要一种能自己学习中间表示的模型。
3. 从传统视觉方法到神经网络,程序员为什么会真的换路
在很多工业视觉项目中,团队往往不会立刻上来就使用复杂深度网络,而是会先尝试一些传统方法,例如手工提取边缘、纹理、形状统计量,再接一个线性分类器或浅层模型。这么做并不奇怪,因为工程团队总想先用最容易解释和实现的方案验证问题是否可学。
但程序员通常很快就会遇到几个熟悉的困难。第一,某些缺陷非常局部,例如细小划痕、微裂纹或小面积污点,人工设计的全局特征很容易把它们淹没掉。第二,缺陷外观变化很大,同一类缺陷在不同产品和光照下可能表现完全不同,手工规则很难覆盖。第三,背景和正常纹理本身就很复杂,很多“看起来像缺陷”的局部模式其实只是正常工艺差异。
于是,程序员会越来越清楚地感受到:真正困难的,是中间表示到底该如何形成,而不只是最后的分类器。也就是说,问题已经转向“机器能不能自己把图像逐层变成对缺陷有意义的表示”,而不再停留在“最后用什么边界分开样本”。这正是第三编开始发挥作用的地方。
若把一个多层神经网络记为
其中 $\theta$ 表示全部参数,$f^{(\ell)}$ 表示第 $\ell$ 层的变换,那么它和前两编方法最大的不同就在于:中间表示不再主要靠人工设计,而是由参数化层级结构在训练过程中自己学出来。
从程序员视角看,这条路真正有吸引力的地方在于,团队不必再为每一种缺陷都手写一套专门特征,而是可以把大量图像直接喂进网络,让网络自己在误差反馈下逐层调整中间表示。这并不意味着“特征工程彻底消失”,而是意味着表示学习的重心第一次明显从程序员转移到了模型本身。
4. 一个基础神经网络到底是怎么被训练起来的
当团队决定尝试神经网络后,真正的工程核心就变成了训练。程序员最先接触到的,是一条很具体的工作链:把图像转成数值张量,定义网络结构,设定损失函数,前向传播得到预测,再通过反向传播更新参数。和这条工作链相比,那些“深度学习的伟大叙事”反而显得没那么重要。
设网络输出类别概率向量
其中 $\hat y_c$ 表示样本属于第 $c$ 类的预测概率。若真实标签采用 one-hot 表示,即
那么最常见的损失函数就是交叉熵:
这个式子在工程里的含义非常直接:如果真实类别的预测概率被模型压得很低,那么损失就会很大;反之,如果真实类别被赋予很高概率,损失就会下降。
从程序员视角看,训练神经网络通常是按批次进行的。也就是说,数据不会一次性全部送进网络,而是每次取一小批图像做前向传播,得到一批预测值,再计算这一批样本的平均损失。随后,程序员让自动求导系统根据链式法则把梯度一路传回各层参数,并按某种优化规则更新参数。
如果把参数记为 $\theta_t$,学习率记为 $\eta>0$,那么最基本的梯度下降更新可以写为
这里,$\nabla_\theta L(\theta_t)$ 表示当前参数下损失函数对参数的梯度。这个公式在书里看起来非常简洁,但在工程里它对应的是一整条训练循环:取一批图像、做前向传播、算损失、反向传播、更新参数、记录训练曲线、继续下一批。
程序员真正关心的,是这条循环跑起来之后发生了什么。例如,训练损失是否稳定下降,验证集准确率是否同步上升,某些类别是否始终学不动,梯度是否出现异常震荡,学习率是否太大或太小。也就是说,第三编里的反向传播和梯度下降,在工程里会成为程序员每天盯着训练日志和曲线图时真正依赖的核心机制,而不只是“理论背景”。
5. 为什么一旦进入图像任务,团队通常会很快升级到卷积网络
如果程序员真的用一个普通全连接网络去做工业图像分类,很快就会发现它在参数量、局部模式建模和空间结构利用上都不太理想。原因非常简单:图像并非任意排列的高维向量,它是具有明确空间邻近关系的对象。相邻像素通常比远距离像素更相关,边缘和纹理往往由局部区域构成,同一种缺陷出现在图像左上角还是右下角,本质上并不应该改变它的类别。
这就是卷积网络会在视觉任务中迅速成为主流的原因。设输入特征图记为 $x$,卷积核记为 $w$,则二维卷积的基本形式可以写为
其中 $(u,v)$ 表示输出位置坐标,$(i,j)$ 表示卷积核内部坐标。这个公式的工程意义非常清楚:同一个小型权重模板会在整张图像上滑动,并在每个局部区域提取相似模式。
卷积网络相对于全连接网络的优势,程序员通常会非常直观地感受到。第一,局部感受野使模型更关注局部缺陷,避免一开始就把整张图打平。第二,参数共享大幅减少了参数数量,也让模型更容易识别“同一类局部模式在不同位置重复出现”的现象。第三,池化和逐层下采样会让表示逐渐从细粒度局部纹理,走向更高层的形状和部件结构。
从程序员角度看,这意味着网络不再需要自己在毫无先验的前提下重新发现“图像具有局部结构”这件事,相关先验已经被直接写进了模型结构里。因此,卷积网络并不只是“参数更多”,它更是在把视觉任务的结构先验变成表示学习的一部分。
6. 训练为什么常常要先追求稳定
当团队第一次真正训练卷积网络时,往往会发现:即使模型结构本身已经合理,训练也未必自动顺利。第三编里关于深度学习复兴条件的那些内容,正是在这里以非常工程化的方式出现。
最典型的问题之一是训练不稳定。有时损失一开始就不下降,有时训练集表现上去很快但验证集完全不动,有时梯度突然爆掉,有时网络虽然收敛但学得很慢。程序员在这个阶段最直接的感受通常是:问题常常不在表达能力本身,而在于模型还没有被顺利训练出来。
于是,初始化、激活函数、归一化和正则化就都会进入日常工作流。比如,合理初始化可以避免网络一开始就把信号压得过小或放得过大;ReLU 会让深层网络中的梯度传播更顺畅;Batch Normalization 可以让中间层分布更稳定;Dropout 则有助于缓解过拟合。
如果把某一层的线性变换记为
再经过激活函数得到
那么程序员在训练时真正盯着看的问题就是:这些层级变换后的数值尺度是否稳定,激活是否过度饱和,梯度是否还能有效回传。
这也解释了为什么第三编不能只讲“多层网络 + 反向传播”就结束。因为从工程角度看,深度学习真正的复兴,靠的是训练机制终于足够成熟,使得这些数学结构能够稳定地在现实项目里跑起来,并不能简单归结为某个单一数学公式的胜利。
7. 程序员如何判断网络到底学到了什么
深度学习工程里一个非常重要的问题是:模型效果不好时,问题究竟出在数据、结构、训练还是标签本身?这时,单看一个总准确率通常远远不够。程序员往往会开始做错误分析和中间表示分析。
最直接的做法之一,是把错误样本分出来看。团队会检查:哪些缺陷最容易被漏掉,哪些正常样本最容易被误判,错误是否集中在某个批次、某种光照、某类产品或某种缺陷边界情形。如果错误高度集中在某些视觉模式上,那么很可能说明当前训练数据还没有覆盖够,或标签本身就存在模糊。
另一类常见做法,则是观察中间层特征。虽然这些中间表示不总是完全可解释,但程序员往往仍能通过可视化、激活分布或简单投影观察出一些现象:前几层更像边缘和纹理探测器,中间层开始对局部形状更敏感,后层则更接近类别相关表示。也就是说,第三编中的“深度表示学习”在工程里可以通过模型中间行为逐步感受到,而不只是一句宏大的理论判断。
这一步很重要,因为它帮助团队建立一个与第二编不同的新直觉:表示不再主要由程序员手工写出来,而是通过训练自动形成。程序员的工作重心,也从“手工发明更多特征”逐渐转移为“判断当前网络学出的表示是否足够好”。
8. 训练在这一编里为什么变成了“数据、结构、优化”三者联动
如果把第三编这个项目放到真实团队里看,程序员的训练流程通常不再像第二编那样主要围绕“换特征、换边界、再试一次”,而是变成数据、结构和优化三者同时联动。
第一条线是数据线。团队不断补充边界样本、稀有缺陷样本和新光照条件下的图像。第二条线是结构线。程序员会尝试不同网络深度、不同卷积块组织方式和不同输入分辨率。第三条线是优化线。团队会不断调学习率、批大小、初始化方式、归一化策略和正则化强度。
这三条线会彼此影响得非常明显。比如,若训练集和验证集都学不动,问题可能出在网络结构或优化;若训练集很快学会但验证集始终不好,问题可能更像过拟合或数据覆盖不足;若某些稀有缺陷始终识别不好,问题又可能出在类别不平衡和标注数量不足。
因此,从程序员角度看,第三编最核心的变化,在于训练过程第一次真正进入了“表示、结构和优化共同决定结果”的阶段,而不只是“模型更深了”这么简单。也正因为如此,深度学习工程往往给人一种强烈印象:模型本身固然重要,但真正把系统做成,靠的是对整个训练过程的联合调试。
9. 工程上如何验收这个系统
到了上线前,团队不会只看一个总准确率就决定系统可不可用。因为在缺陷检测任务里,误差类型的代价往往强烈不对称。
首先,团队会单独看漏检率。因为缺陷品漏掉通常比误检更危险。其次,会看不同缺陷类型上的表现是否均衡,避免系统只对最常见缺陷有效。再次,会看在不同生产批次、不同拍摄设备和不同时间段上的稳定性,防止模型其实只学会了某种固定环境。
程序员还会做一类很重要的验收:人为复核链路测试。也就是说,系统并不是简单替代人,而往往是先作为筛查工具或辅助工具上线。这时,团队要确认模型输出怎样进入实际流程,例如高置信度正常样本是否可自动放行,边界样本是否应送人工复核,哪些类别必须保守处理。
从数学上看,这一步仍然是在问“风险究竟如何被近似衡量”,但到了第三编,风险已经和表示质量、训练稳定性、类别不平衡和业务代价同时绑定在一起。也就是说,工程验收在这里不只是看“分类对不对”,还在看“学到的表示是否足够稳、训练是否足够可信、输出是否能进入真实流程”。
10. 从这个案例回头看第三编,数学知识是怎样被真正用起来的
如果把整个项目重新回看一遍,就会发现第三编中的核心对象已经全部进入了工程主流程。
首先,多层复合函数不再只是一个抽象表达,而是真正决定系统如何逐层把原始图像变成高层表示的结构形式。其次,反向传播不再只是求导技巧,而是程序员每天都在依赖的训练主干,因为没有它,误差根本无法系统回传到每一层参数。再次,梯度下降与其各种变体不再只是优化方法名字,而是训练日志中最真实的“损失是否下降、验证集是否变好”的动力来源。
然后,卷积与池化在这里也不再是模型列表上的术语,而是视觉任务结构先验进入系统的方式。它们让网络不必再靠程序员手工发明每一种局部纹理特征,而可以自己在局部区域里提取、组合并逐层抽象。最后,深度表示学习也不再只是理论口号,而是整个系统之所以能够真正摆脱手工特征瓶颈的核心原因。
因此,这个案例最值得带走的,是一种更深的认识:第三编中的神经网络与深度学习之所以会成为主流,并不只是因为模型名字变得更复杂,更因为真实工程终于有了一种可以在高维复杂输入上自动学习表示的统一机制。
11. 这个系统为什么还会继续走向第四编
尽管到这里为止,团队已经通过深度网络和卷积结构把视觉缺陷检测系统做得远比第二编阶段更强,但新的边界也会逐渐出现。最明显的一点是:当前任务主要处理的是单张静态图像,而现实中很多更复杂的问题属于序列、上下文和生成问题,并不能被看成孤立图像分类。
例如,若团队下一步想处理产线上连续视频中的缺陷演化、设备时序信号中的异常模式,或根据图像和文字说明联合生成检测报告,那么问题就不再只是“从静态输入里学表示”,而会进一步进入“如何表示时间”“如何生成输出”的阶段。
这正是第四编必然出现的原因。第四编对应的,是深度表示学习在真实工程中继续往前走时自然会遇到的下一层问题:既然机器已经能在静态图像里学表示,那么它能不能进一步学会表示顺序、上下文和生成过程?