第7章 训练一个专业视觉检查器

工作阶段: 让模型从图像样本中学习可见差异,并进入人工复核流程。
公共核心项目: 与AI协同开发“商品包装状态视觉复核器”。
核心技术: 像素张量、卷积神经网络、迁移学习选择、对象与批次划分、规则基线、混淆矩阵、双阈值、陌生输入接管。
输入输出链: 包装图 → 尺寸/亮度规则门 → 封签规则基线/Keras CNN → 待复核分数 → 候选或人工接管 → 人工看原图确认。
应用中的AI: 判断登记包装教学图属于“合格候选”还是“待复核候选”的本地Keras卷积分类模型。
协助开发的AI: 帮助学生审查采集表和标签、生成与解释完整Notebook和工程代码、分析错例并完成限定修改的大语言模型。
人的责任: 确认只基于可见证据的标签,处理图像授权,冻结对象与批次,审查漏检与陌生样本,批准用途。
主要交付物: 248张可离线携带教学图、清单与生成器、规则基线、小型CNN、完整Notebook、推理入口、真实报告、模型卡和一键验收。
明确边界: 项目只形成外观候选,不证明内部质量、真伪、食品药品安全或法规合格;所有结果必须由人查看原图。
跨章关系: 延续第2章的数据来源和隔离,第5章的基线比较与固定测试;第9章可研究端侧部署,但不能扩大模型权限。

学习目标

完成本章学习后,你将能够:

  1. 根据工作输出区分图像分类、目标检测和图像分割;
  2. 把包装标签限制在可见现象,并按对象和批次隔离训练、验证与测试;
  3. 实现一个透明封签规则基线,说明它能发现什么、会漏掉什么;
  4. 在AI协助下运行完整Keras CNN,解释卷积、池化、Sigmoid与阈值;
  5. 用混淆矩阵、待复核漏检、候选覆盖和陌生样本接管比较系统;
  6. 完成阈值实验、四类固定测试、模型卡和另一组复现交付。

项目导入

包装检查可能涉及封签、破损、污渍、压角、文字、条码、重量和内部质量。把这些全部交给“看图AI”会让任务失控。本章只处理一项清楚的工作:输入一张登记范围内的单包装教学图,系统检查尺寸和亮度,再给出“合格候选”“待复核候选”或“人工复核”。最终人员必须查看原图。

“合格候选”不是“商品合格”。图像只能呈现某个角度的外观,无法证明内部状态、材料性能和真伪。项目标签也只覆盖程序生成的四种可见问题:缺封签、撕裂、压角和污渍。真实商品一旦换结构、背景或拍摄设备,就可能超出训练范围。

项目把视觉模型放进完整工程,而不是只展示一条model.fit。学生先审查248张教学图的对象、批次和划分,运行只看绿色封签的透明基线,再训练小型Keras CNN,在未见批次B09—B10的48张图上比较。运行时使用0.35和0.65双阈值,把中间分数转人工;8张不同尺寸的陌生包装在模型前接管。

包装图像
   │
   ├─ 不是32×32或亮度异常 ─→ 模型不运行 ─→ 人工重新采集/复核
   │
   └─ 输入门通过
        ├─ 绿色封签规则基线 ───────────┐
        └─ 卷积→池化→卷积→整图表示→Sigmoid分数
                                      │
            ≤0.35 合格候选 0.35—0.65 人工 ≥0.65 待复核候选
                                      ↓
                              人员查看原图确认

建议用7—9学时。先打开数据目录观察同一对象的两个视角和四种缺陷,再运行参考模型;随后在Notebook中逐格建立数据、规则和CNN;第三阶段修改一个阈值或一个输入规则,运行全部验收并填写模型卡。学生不从空白写长网络,却必须找到像素进入模型、分数生成、规则接管和人工决定的位置。

本章成果

AI协同开发路线

先向协助开发的AI提交可见标准,而不是“帮我判断商品合格”。可这样提问:“输入是单包装外观图,只允许标注缺封签、撕裂、压角、污渍和未见明显问题。请追问图像授权、同一对象连拍、批次、拍摄条件和高代价漏检,不要先选择大模型。”学生与专业责任人确认答案,把“内部质量、真伪、法规合格”写入禁止用途。

随后要求AI先给工程树:生成/采集、清单校验、对象与批次划分、规则基线、Keras训练、统一推理、冻结评价、Notebook、测试、模型卡。为保证课堂离线可复现,本样板不用联网下载大型预训练权重,选择32×32教学图和三层小型CNN。真实图像更多、变化更复杂时可评估迁移学习,但必须重新测试,不能仅因模型更大就采用。

AI生成代码后,学生逐项核对:同一object_id和batch_id是否跨集合;规则是否只解决封签;陌生尺寸是否在模型前转人工;CNN输出顺序是否一致;冻结图是否在最终评价前被读取。修改时限定范围,例如“只在B07—B08验证批次把intact_max由0.35改为0.25,保持模型和review_min不变,不打开B09—B10”。学生先预测人工量变化,冻结方案后才运行最终评价与acceptance.py --require-model。

第一节 根据专业输出选择分类、检测或分割

一、先问工作需要什么输出

图像分类为整幅图输出类别或分数。本项目每张图只放一个主要包装,工作只需要候选状态,因此选择分类。分类模型不会给出撕裂坐标,即使卷积内部对某一区域响应较强,也不能把整图分数解释成缺陷位置。

目标检测适合一张图有多个包装,并要求指出哪一个需要复核,输出类别和矩形位置。图像分割适合测量裂缝、病斑、涂层等像素区域。三者需要不同标注和指标,不是“模型越高级越好”。如果业务只需要整图候选,检测框和像素掩膜会增加不必要的标注成本。

多模态大模型可以描述包装图,适合帮助人员发现可能遗漏的现象,却也可能生成不在图中的解释。稳定执行固定分类标准时,需要明确数据、标签和冻结测试。本项目把大语言模型放在协助开发位置,运行时AI是本地CNN。

二、把标签限制在可见证据

intact表示当前教学图中没有看到四类已定义问题;review表示出现缺封签、撕裂、压角或污渍候选。使用“候选”是为了提醒:摄像头只看到一个视角,图片无异常不等于真实商品合格。

真实项目需要两名标注者先独立判断一小批图,记录分歧。若一个人把轻微压角标为合格,另一人标为待复核,先修订标准和示例,不要让模型在矛盾标签上学习。标签由质量或业务责任人批准,不能让AI从文件夹名称推断企业标准。

图像质量问题与包装状态也要区分。过暗、过亮、损坏文件和陌生尺寸不是包装缺陷类别,应由输入门转人工重新采集。把“照片模糊”标成review会让模型混淆物体状态与拍摄质量。

三、建立人工与规则基线

人工基线是按清单查看原图并记录判断与用时。工程中的计算基线更简单:只检查顶部区域是否存在足够比例的绿色封签。如果存在就给出intact候选,不存在给出review候选;尺寸和亮度不合格先转人工。

def rule_baseline(path):
    gate = scope_gate(path)
    if not gate["accepted"]:
        return {"route": "manual_review", "label": None}
    image = read_rgb(path)
    region = image[2:13, 9:23]
    green = ((region[:, :, 1] > region[:, :, 0] * 1.25) &
             (region[:, :, 1] > region[:, :, 2] * 1.25))
    ratio = float(green.mean())
    return {"route": "rule_candidate",
            "label": "intact" if ratio >= 0.08 else "review"}

规则能够稳定发现缺封签,却会把仍有封签的撕裂、压角和污渍当作合格。这一限制在运行前就写入系统,而不是等模型赢了再贬低基线。复杂模型只有在相同测试批次减少漏检,并且陌生与故障安全接管时才值得保留。

专业迁移卡:数字媒体与设计
数字媒体方向可把输入改为授权镜头截图,分类为“可进入初剪候选/需复核”,规则先检查分辨率、方向和黑帧;设计方向可检查作品是否属于已登记版式类别。素材授权、事实和发布仍由人确认,不能用分类分数自动发布。

第二节 采集、标注并按对象或批次划分图像数据

一、让样本覆盖真实变化

配套data/generate_images.py以固定种子20260807生成图像。每张32×32 RGB图有背景、包装矩形和轻微噪声;合格图有完整封签,待复核图从缺封签、撕裂、压角和污渍中选择。生成数据让学生不依赖外部下载即可跑通,但视觉模式远比真实商品简单。

每个包装对象生成两个轻微视角,字段包括image_id、相对路径、标签、object_id、batch_id、split、范围和缺陷类型。B01—B06共144图训练,B07—B08共48图验证,B09—B10共48图冻结测试。8张圆形、48×32图片作为未登记结构,不进入训练。

真实采集要覆盖光照、背景、距离、设备和生产批次,同时避免类别与背景绑定。如果合格图都在白桌面、问题图都在深色输送带,模型可能只学背景。图像可能含姓名、地址、订单或人物,采集前要确定授权,裁去无关个人信息,并保存原图与脱敏记录的对应关系。

课堂数据审查不能只数图片。每组先随机打开12张图,对照清单核查路径、对象、批次、标签和缺陷是否一致;再按标签和缺陷类型统计数量,确认“待复核”不是只有一种明显黑线。最后选择同一对象的两个视角,讨论哪些变化来自拍摄、哪些现象决定标签。若清单写“污渍”而图中没有可见依据,应先退回数据,不让模型替标注人员消化矛盾。

使用真实商品图片时还要形成权利清单:拍摄者是否同意教学和训练使用,包装上的品牌、人物和订单信息能否保存,数字资源能否公开下载,何时删除原图。获得查看权限不自动等于获得训练或发布权限。协助开发的AI可帮助列检查项,却不能判断一份授权是否覆盖当前用途。

二、按对象或批次隔离集合

同一对象两个视角高度相似。如果一个进入训练、另一个进入测试,模型可能记住纹理,测试分数会过高。同一批次还可能共享材料和背景,因此本项目同时要求对象与批次都不跨集合。load_manifest建立每个object_id和batch_id对应的切分集合,只要出现多个训练/验证/测试值就停止。

groups = {}
for row in rows:
    for kind in ("object_id", "batch_id"):
        groups.setdefault((kind, row[kind]), set()).add(row["split"])
leaked = [key for key, splits in groups.items()
          if len(splits - {"unknown"}) > 1]
if leaked:
    raise ValueError(f"对象或批次跨集合泄漏: {leaked[:3]}")

测试批次在建模前由教师或数据责任人封存。开发阶段使用不含B09—B10路径和标签的development_manifest.csv;学生不能查看冻结类别分布、运行基线或逐张调模型。模型与双阈值冻结后,最终评价单元才一次性打开完整清单。若结果反过来指导修改,新版本必须建立新的未见批次。

三、增强不能改变标签事实

真实项目常用轻微旋转、平移、缩放和亮度变化扩展训练,但每项变化都必须不改变标签。例如条码方向若是标准,水平翻转就可能制造不存在的包装;过强颜色变换会掩盖污渍或封签颜色。

本样板生成器已经提供少量位置与像素扰动,训练代码不再叠加随机增强,以便复现。学生若增加RandomRotation,应在训练/验证批次先可视化增强图、确认标签并比较验证结果;方案冻结后才进行一次新批次评价。增强不是“免费数据”,也不能补足真实产品缺失。

第三节 从零训练小型CNN并比较迁移学习选择

一、从像素到卷积与迁移选择

一张32×32彩色图进入程序后成为32×32×3张量。卷积核在局部区域共享参数,逐层组合边缘、颜色和纹理;池化缩小空间尺寸;全局平均把整幅特征图汇总;Sigmoid输出待复核相对分数。

迁移学习是复用大型预训练视觉网络,适合真实数据有限、图像更复杂的场景,但需要权重来源、较大计算和离线分发。本章为了让完整工程小、快、无网络,实际选择从零训练三层小型CNN。课堂比较两条路线的条件:先用样板学透CNN机制;真实迁移项目再引入经核验预训练权重,并重新完成基线、测试和模型卡。本章没有加载预训练权重,因此不能把本次结果称为迁移学习实验。

二、完整训练与推理链

train.py读取清单并把像素缩放到0—1。完整模型主链如下:

inputs = keras.Input(shape=(32, 32, 3), name="package_rgb")
x = keras.layers.Conv2D(8, 3, activation="relu",
                        padding="same")(inputs)
x = keras.layers.MaxPooling2D()(x)
x = keras.layers.Conv2D(16, 3, activation="relu",
                        padding="same")(x)
x = keras.layers.MaxPooling2D()(x)
x = keras.layers.Conv2D(24, 3, activation="relu",
                        padding="same")(x)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dense(16, activation="relu")(x)
outputs = keras.layers.Dense(
    1, activation="sigmoid", name="review_score"
)(x)
model = keras.Model(inputs, outputs,
                    name="package_visual_reviewer")
model.compile(
    optimizer=keras.optimizers.Adam(0.003),
    loss="binary_crossentropy", metrics=["accuracy"],
)
model.fit(
    x_train, y_train,
    validation_data=(x_val, y_val),
    epochs=45, batch_size=24,
    callbacks=[keras.callbacks.EarlyStopping(
        monitor="val_loss", patience=8,
        restore_best_weights=True,
    )],
)

训练模型使用0.5把分数变成二分类,只用于和规则公平比较。运行系统更保守:分数≤0.35才进入合格候选,≥0.65进入待复核候选,中间转人工。尺寸不是32×32、亮度异常或模型缺失时,模型不运行。

if score <= thresholds["intact_max"]:
    route, label = "intact_candidate", "intact"
elif score >= thresholds["review_min"]:
    route, label = "review_candidate", "review"
else:
    route, label = "manual_review", None

完整Notebook位于resources/ch07/project/notebooks/ch07_package_vision.ipynb,权威工程为vision.py、train.py、evaluate.py和review_image.py。Notebook让学生逐格观察,.py保证测试、命令行和其他应用共用同一推理逻辑。

三、读混淆矩阵和本次真实结果

参考环境为Windows、Python 3.12.13、TensorFlow 2.21.0、Keras 3.15.1、CPU。模型实际训练42轮后恢复验证损失较好的权重。冻结B09—B10共48张图,合格与待复核各24张。

方法 合格正确 合格误报复核 待复核漏检 待复核正确 准确率
绿色封签规则 24 0 16 8 66.67%
Keras CNN(0.5) 24 0 12 12 75.00%

CNN比规则多识别4张待复核图,准确率高8.33个百分点,但仍漏掉12/24个问题候选,不能自动放行。运行双阈值后,只有25/48张进入明确候选区,候选25/25正确,其余23张转人工。这里的100%不是全部测试准确率,而是较保守阈值筛出的候选正确率,代价是47.92%的人工接管。

报告如实同时保留模型能力与运行覆盖,不能只挑“25/25”宣传。图像由程序生成,结构简单,75%也不能外推到真实包装。最终判断是教学合成范围限用、始终人工确认。

课堂可以选择四张图完成“从原图到决定”的追踪:一张合格、一张缺封签、一张仍有封签但带污渍、一张48×32陌生结构。学生先不用模型按标签合同判断,再运行rule_baseline和review_image.py,把输入门、规则结果、CNN分数、路由和人工决定填入同一张观察表。缺封签图能说明规则的价值,污渍图能说明规则边界,陌生图能说明模型前接管,合格图则用于检查系统没有把所有对象都推给人工。

如果模型建议与学生判断不同,先回到原图和标签标准,不立刻改代码。检查图像是否损坏、清单是否错标、分数是否处于不确定区,再判断问题属于数据、模型还是运行阈值。协助开发的AI只能依据这些证据提出修改候选。每组还应把自己的解释交给另一组复核,确保别人能从文件和结果得出相同结论,而不是依赖口头补充。

四、读训练现象和错例

训练准确率上升不保证批次泛化。学生先列出12个漏检的defect_type和分数,检查它们是否集中在压角、污渍或不同位置。规则漏掉带封签的所有其他缺陷符合其设计;CNN错例可能来自小目标在全局平均中被弱化、训练样本不足或批次颜色变化。

开发阶段只向协助开发的AI提交验证错例编号、真实标签、分数、缺陷类型和图片,不只问“怎么提高准确率”。冻结测试结果只用于交付判断;若它暴露了新问题,可形成下一版假设,但必须用新批次验证。若建议删除错例、把冻结图加入训练或改阈值掩盖0.5分类错误,应拒绝。

图示规划: 绘制“像素—卷积局部特征—池化—整图表示—待复核分数—双阈值—人工确认”,旁边用小图对比分类、检测和分割的输出。

第四节 在光照、背景和陌生样本中测试泛化

一、只改变合格候选阈值

固定模型、B07—B08验证批次与review_min=0.65,只把intact_max从0.35改为0.25。修改前预测:合格候选减少,人工复核增加,误把问题图当合格的风险可能降低。用验证批次记录三种路由数量、候选正确率、待复核漏检和人工量,选定后冻结阈值;B09—B10只在最终评价运行一次。

阈值不会让模型学到新缺陷,也不能证明输入属于训练范围。若模型在0.5比较仍漏检,降低合格阈值只是更保守地转人工,不应写成模型能力提高。学生让AI只修改元数据或命令参数,审查差异并运行全部回归。

二、正常、边界、陌生和故障测试

正常测试是B09—B10中32×32、亮度正常、对象未见的图,检查规则、CNN分数和候选路由。边界测试使用分数接近0.35或0.65、允许范围内的轻微位置与亮度变化,预期进入人工或按阈值稳定分流。

陌生测试使用8张48×32圆形包装。它们格式正确却结构和尺寸未登记,系统在模型前全部转人工,不能让CNN在两个已知标签中强选。真实陌生样本还包括新包装结构、无关图片、训练未见缺陷和新设备背景。

故障测试包括损坏PNG、模型缺失、清单图像路径不存在、对象/批次跨集合、空清单和极暗图。损坏文件必须给出明确错误;模型故障默认人工;数据泄漏在训练前停止。tests/test_project.py实际包含8项检查,其中独立验证0.35、0.65及两侧人工区的边界路由。

cd resources\ch07\project
python acceptance.py
python acceptance.py --require-model
python review_image.py data\images\B09-O01-V1.png
python review_image.py data\images\U01.png

默认验收检查必需文件、Python语法、测试、Notebook冻结隔离,以及报告中的固定测试、模型、配置与元数据摘要;--require-model加载小模型,将48张冻结图的现场评价与正式参考结果逐项比较,只排除明确不稳定的环境版本字段。训练另用python train.py。

三、填写模型卡并决定用途

模型卡记录任务与类别、数据生成与批次、模型结构、基线、阈值、混淆矩阵、候选覆盖、陌生接管、允许输入、禁止用途和维护责任。本项目的允许用途只能写“教学图上的低风险外观候选”,不能省略合成数据和人工确认。

完整交付位于resources/ch07/project/,含README、任务合同、数据卡、248张图片、生成器、清单、完整Notebook、.py工程、模型、元数据、训练历史、真实报告、模型卡和验收。另一组应能先运行参考模型,再从源数据重训,并解释规则66.67%、CNN75%、双阈值覆盖52.08%三者为何不矛盾。

复现时先运行默认验收,确认数据和文件未损坏;再运行带模型验收,核对参考分数;最后才重新训练。重新训练可能因底层数值实现产生小幅差异,不能要求最后小数完全一致,但批次、样本数、标签顺序和使用结论必须一致。若重新训练跌破规则基线,报告应记录退回,而不是复制旧模型成绩。

采用表示在冻结且与真实用途相符的数据上达到预设门槛;限用表示只形成候选并人工确认;退回表示复杂模型未超过规则、漏检超门或复现失败;停止表示授权或质量责任不具备。本样板即使模型超过基线,也只限用于教学,绝不进入真实自动放行。

专业迁移卡:智能制造、交通与生物
制造方向可把标签改为零件外观“通过候选/复核候选”,按生产批次隔离,漏检门由质量负责人定义。交通方向若要指出路面问题位置,应改为检测而非沿用整图分类。生物方向可筛选显微图像候选,但标签需专业人员确认,模型不得形成诊断或生物安全结论。

本章小结

本章完成了从图像数据、规则基线、小型CNN、双阈值、陌生接管到模型卡的完整项目。任务选择由工作输出决定;标签只描述可见证据;对象和批次不跨集合;复杂模型必须与透明规则在同一冻结批次比较。

真实运行中,规则32/48正确,CNN36/48正确;双阈值只让25张进入候选区并把其余转人工;8张陌生图全部在模型前接管。这样的结果比一个孤立“准确率”更接近工程事实:模型有所增益,却仍有大量漏检和适用范围限制。大语言模型帮助学生开发,人和专业标准决定能否使用。

关键术语

目标测试

  1. 一幅图中有多个包装且要指出问题包装的位置,应优先选择( )。A.分类 B.目标检测 C.文本生成 D.回归
  2. 避免同一包装连拍造成泄漏的关键做法是( )。A.随机分每张图 B.按对象和批次整体划分 C.增加轮数 D.只看准确率
  3. 本项目规则基线最容易漏掉哪类样本?A.缺封签 B.尺寸陌生 C.仍有绿色封签但存在撕裂、压角或污渍 D.损坏文件
  4. 8张48×32陌生包装最合适的处理是( )。A.缩放后强制分类 B.默认合格 C.在模型前转人工 D.加入训练并覆盖测试
  5. 判断并改错:“双阈值候选25/25正确,说明全部48张测试图准确率是100%。”
  6. 判断并改错:“把测试错例加入训练后,继续用原测试集就能证明模型泛化改进。”
  7. 分别写出分类、检测和分割的一种专业输出,并说明本项目为何选择分类。
  8. 设计只把intact_max从0.35改为0.25的实验,写出保持项和要记录的四类结果。
  9. 各设计一条边界、陌生和故障测试,并说明模型是否应该运行。
  10. 某新版本CNN冻结准确率降到60%,低于规则66.67%,却有更漂亮的界面。请作出采用、限用、退回或停止结论,并说明责任人与重新验收范围。

答案编号:A3-7-01—A3-7-10。完整答案和评分要点统一放入书后“目标测试参考答案”。