第3章 计算机视觉:让模型理解图像

本章技术主题: 图像分类、卷积特征与迁移学习。
本章技术实验: 训练植物外观分类器,并比较分类、检测和分割的不同输出。
家庭项目: “打造一个AI阳台植物园”第二阶段——视觉学习;“建立家庭物品整理与循环利用助手”作为可见属性迁移。
跨章位置: 接收第2章的植物记录,第12章再把视觉候选与传感器、规则和语言说明连接起来。
技术主线: 图像像素 → 卷积特征 → 预训练表示 → 分类概率 → 泛化测试 → 人工判断。

学习目标

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

  1. 把植物状态识别任务转化为图像分类问题,并建立相互独立的训练、验证和测试数据。
  2. 用直观语言说明像素、卷积、特征图和迁移学习在图像分类中的作用。
  3. 运行一个完整的Keras迁移学习程序,读懂图像进入预训练网络再到分类输出的主要路径。
  4. 根据任务输出比较图像分类、目标检测、图像分割和多模态视觉,并通过陌生条件测试判断模型的泛化边界。

项目导入

本章继续“打造一个AI阳台植物园”这一连续项目,进入第二阶段:视觉学习。实践任务是制作植物视觉识别原型:输入一幅叶片或植株照片,模型在“外观正常”“叶片发黄”“叶片有斑点”三个课堂标签中给出候选类别和分数,再由学生查看原图并确认。

本章沿用阳台植物场景,但输入是图像,输出是外观类别;第2章处理独立的表格样本,第5章保留连续记录的时间顺序,第12章再连接多源模型。学生要比较的是模型任务,不是反复制作同一个应用界面。

这些标签只描述照片中可见的外观,不诊断病虫害,也不直接给出浇水、施肥或用药建议。相似外观可能由多种原因造成,一张图像也可能缺少根系、土壤和生长过程信息。模型的用途是帮助整理和筛选观察记录,而不是替代种植知识和现场判断。

本章的“应用中的AI”是植物图像分类模型;它接收像素组成的图像并输出三类分数。“开发与学习AI”协助学生制定采样方案、生成和解释完整训练代码、阅读训练日志以及归纳失败模式。图片授权、标签规则、测试样本隔离和采用结论仍由学生负责。若开发与学习AI声称仅凭一张叶片照片就能确定病因,学生应指出任务边界并拒绝这种扩大解释。

本章使用公开授权或教师提供的脱敏植物图片。不要未经同意拍摄他人家庭环境,也不要把网络搜索结果直接当成可自由训练的数据。图片来源和授权说明应与数据一同保存。

本章成果

考点提示

请重点理解:图像进入模型后首先是像素数值;卷积通过局部计算形成特征图;迁移学习复用预训练模型学到的通用视觉特征;分类、检测和分割的输出不同;测试时应隔离植物个体,而不只是随机拆分几乎相同的照片。

第一节 用图像分类识别植物状态

一、先确定模型到底输出什么

图像分类回答“整幅图最接近哪个类别”。本项目的三个标签描述可见外观:外观正常、叶片发黄、叶片有斑点。类别定义必须能够被人执行。例如“叶片发黄”可以规定为主要叶片区域存在明显黄色变化;“叶片有斑点”表示叶面出现可见的点状或片状色差;无法看清叶片、同时符合多类或不属于这三类的图片标记为“暂不进入训练”。

不要用“健康”“患病”作为未经验证的事实标签。外观正常的照片不能证明植物在生物学上完全健康,有斑点也不能确定病因。标签越符合图像中真正可见的信息,模型任务越清楚,后续判断越诚实。

输入是一幅主要包含一株植物或一片叶的照片,输出是三个类别分数。模型不知道植物品种、拍摄日期和此前变化,除非这些信息作为另一个经过设计的输入交给系统。第一版只做单幅图像分类,不加入环境记录,也不连接传感器或控制设备。

二、按照植物个体划分数据

每类可先准备60至100幅授权图片。数量并非越多越好,关键是图片是否覆盖不同植株、角度、距离、背景和光照。对同一片叶连拍的十张照片高度相似,不能等同于十个独立植物个体。

数据目录按训练、验证和测试划分,每份下面再按类别建文件夹:

plant_images/
├─ train/
│  ├─ normal/
│  ├─ yellow/
│  └─ spotted/
├─ validation/
│  ├─ normal/
│  ├─ yellow/
│  └─ spotted/
├─ test/
│  ├─ normal/
│  ├─ yellow/
│  └─ spotted/
└─ demo/
   └─ test_leaf.jpg

同一株植物的照片必须全部进入同一份数据。如果同一片叶的正面照片在训练集、侧面照片在测试集,模型可能靠叶片纹理和背景记忆取得高分,测试便不能说明它认识了新植株。先按植物个体分组,再把不同个体分配到训练、验证和测试,是视觉项目中非常重要的防泄漏措施。

训练集用于学习参数,验证集用于选择训练轮数和方案,测试集留到方案确定后使用。建议按约70%、15%、15%划分植物个体,同时检查每一类在三份数据中都有足够样本。若有一类图片极少,整体正确率会掩盖该类的失败,应优先补充该类数据或缩小任务。

三、先观察图片,再观察分数

在训练前随机展示每类若干图片,让小组检查标签、清晰度和背景。若“发黄”照片全在室外,“正常”照片全在白墙前,模型可能学习场景而不是叶片外观。若斑点只出现在某一种植物上,模型可能学习品种差异。发现这类线索时应先改善数据,而不是期待模型自动忽略。

训练完成后的第一次测试使用未进入训练的新植物照片。除最高分类别外,还要记录三个分数、真实标签、植物个体、光照和背景。分数接近说明当前类别之间难以区分;即使最高分很高,陌生植物或严重模糊图像也不应自动接受。

应用中的AI负责输出候选类别。普通程序可以根据阈值把低分或分数接近的样本转为“需要人工确认”。学生查看原图和数据来源后作最终标记。这个分工避免把模型的强制选择误写成植物事实。

第二节 从像素、卷积到迁移学习

一、模型首先看到的是像素数值

彩色数字图像可以理解为一个有高度、宽度和颜色通道的数值数组。常见图像每个位置有红、绿、蓝三个通道,数值表示该位置的颜色强度。把照片缩放到160×160后,模型接收到的是160×160×3个数值,而不是“叶片”“斑点”这样的词语。

缩放会统一输入形状,也可能损失细小信息。若斑点在原图中只有几个像素,过度缩小后可能消失。反过来,盲目使用超大图像会增加计算量,并不保证小数据项目更可靠。选择尺寸应结合目标细节和可用资源,通过测试决定。

图像增强在训练时对图片做适度翻转、旋转或缩放,帮助模型看到更多合理变化。增强不是创造新的植物事实,变化不能破坏标签。例如把图片旋转少量角度通常不会改变外观类别,而改变颜色可能直接破坏“发黄”这一标签,因此不能随意使用强颜色变换。

二、卷积怎样形成特征图

卷积层使用一组可学习的小窗口在图像上滑动,对每个局部区域进行相同计算。浅层卷积可能对边缘、颜色变化和简单纹理产生响应;更深层把这些局部模式组合成较复杂的叶脉、斑点边界或形状线索。每个卷积核在整幅图上形成一张响应结果,这类中间结果称为特征图。

卷积的关键不是“模型像人一样看叶子”,而是共享局部计算参数并逐层组合空间模式。特征图也不是人预先写好的规则,它由训练数据和损失共同塑造。可视化某张特征图可以看到哪些区域产生较强响应,但不能仅凭亮区就断言模型理解了植物病理。

从零开始训练卷积网络通常需要较多图像。迁移学习先使用一个在大规模通用图像上预训练的视觉网络作为特征提取器,再为本项目增加新的分类层。预训练网络已经形成边缘、纹理和形状等通用表示,学生的数据主要用于学习这些表示怎样组合成三个新类别。

三、运行迁移学习的完整核心程序

下面程序读取前述目录,使用MobileNetV2作为预训练特征提取器,冻结其参数,只训练新的三分类层。首次运行需要取得预训练权重;离线课程资源应提前提供经过核验的权重文件和来源说明。

from pathlib import Path

import numpy as np
import tensorflow as tf
from tensorflow import keras

keras.utils.set_random_seed(7)

data_root = Path("plant_images")
image_size = (160, 160)
batch_size = 16

train_ds = keras.utils.image_dataset_from_directory(
    data_root / "train",
    image_size=image_size,
    batch_size=batch_size,
    label_mode="int",
    shuffle=True,
    seed=7,
)
validation_ds = keras.utils.image_dataset_from_directory(
    data_root / "validation",
    image_size=image_size,
    batch_size=batch_size,
    label_mode="int",
    shuffle=False,
)
test_ds = keras.utils.image_dataset_from_directory(
    data_root / "test",
    image_size=image_size,
    batch_size=batch_size,
    label_mode="int",
    shuffle=False,
)

class_names = train_ds.class_names
prefetch = tf.data.AUTOTUNE
train_ds = train_ds.prefetch(prefetch)
validation_ds = validation_ds.prefetch(prefetch)
test_ds = test_ds.prefetch(prefetch)

augmentation = keras.Sequential([
    keras.layers.RandomFlip("horizontal"),
    keras.layers.RandomRotation(0.05),
    keras.layers.RandomZoom(0.10),
], name="augmentation")

base_model = keras.applications.MobileNetV2(
    input_shape=(160, 160, 3),
    include_top=False,
    weights="imagenet",
)
base_model.trainable = False

inputs = keras.Input(shape=(160, 160, 3))
x = augmentation(inputs)
x = keras.applications.mobilenet_v2.preprocess_input(x)
x = base_model(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dropout(0.20)(x)
outputs = keras.layers.Dense(len(class_names), activation="softmax")(x)
model = keras.Model(inputs, outputs)

model.compile(
    optimizer=keras.optimizers.Adam(learning_rate=0.001),
    loss="sparse_categorical_crossentropy",
    metrics=["accuracy"],
)
stop = keras.callbacks.EarlyStopping(
    monitor="val_loss", patience=5, restore_best_weights=True
)
model.fit(
    train_ds,
    validation_data=validation_ds,
    epochs=30,
    callbacks=[stop],
)

test_loss, test_accuracy = model.evaluate(test_ds, verbose=0)
print("类别顺序:", class_names)
print("测试损失:", round(float(test_loss), 4))
print("测试正确率:", round(float(test_accuracy), 4))

demo_path = data_root / "demo" / "test_leaf.jpg"
demo_image = keras.utils.load_img(demo_path, target_size=image_size)
demo_array = keras.utils.img_to_array(demo_image)
demo_array = np.expand_dims(demo_array, axis=0)
scores = model.predict(demo_array, verbose=0)[0]
best_index = int(np.argmax(scores))
print("候选类别:", class_names[best_index])
print("类别分数:", {name: round(float(score), 3)
                  for name, score in zip(class_names, scores)})

这段程序的关键路径是:目录中的图片被调整为统一大小;训练图片经过合理增强;预处理函数把像素转换为预训练网络所需的数值范围;冻结的基础网络输出视觉特征;全局平均池化汇总特征;新分类层输出三个分数。

冻结基础网络意味着第一阶段不改动预训练参数,能减少小数据下过拟合和训练负担。若数据较多且第一阶段稳定,可在教师指导下解冻少量高层参数,用很小的学习率微调;这属于提高任务,必须重新用验证集和测试集检查,不能因为训练正确率提高就认定更好。

完整Notebook应在代码前展示数据样例和类别数量,在训练后绘制训练与验证曲线、混淆矩阵,并逐类报告测试结果。学生不以默写程序为目标,但要能够沿变量追踪一张图片怎样变成类别分数,并让开发与学习AI只解释指定的一段,例如询问“基础网络冻结后哪些参数仍在训练”。

第三节 比较分类、检测、分割并测试视觉泛化

一、先根据需要选择视觉任务

同一张植物照片可以对应不同任务,关键差别在输出。

任务 主要问题 输出 适合本项目的用途
图像分类 整幅图最接近哪一类 类别和分数 一张图主要包含一株植物或一片叶
目标检测 图中有哪些对象,它们在哪里 类别、分数和矩形框 一张图有多片叶或多个目标
图像分割 每个像素属于哪个区域 与图像对应的像素掩膜 估计斑点或发黄区域所占比例
多模态视觉 图像与文字问题共同表达什么 文字或结构化回答 结合图片回答“哪些可见现象支持该描述”

分类成本较低,但无法指出斑点位置;检测给出框,却不能精确描出不规则区域;分割最细致,通常需要逐像素标注,数据制作成本更高。多模态视觉模型可以把图像与自然语言问题一起处理,适合描述和比较,但输出文字可能加入图中没有的内容。技术选择应由任务输出和错误后果决定,而不是名称越复杂越好。

生活项目“建立家庭物品整理与循环利用助手”也可以迁移这套视觉方法:模型只根据照片中的颜色、形状、材质外观和可见破损,输出候选类别或候选标记,供人整理记录。它看不到物品内部、残留物和当地回收政策,不能仅凭外观决定物品是否可回收或最终去向。查询地区规则、核对版本并引用来源属于后续的语义检索与系统整合,本章只学习从像素到可见属性判断的视觉机制。

文字识别只是视觉任务中的一种。当目标是准确转录固定版式文字时,专用文字识别方法便于逐字符检查和稳定批处理;视觉语言模型可以直接结合版面与图像语义回答问题,却可能改写、遗漏或补充文字。本书不把文字识别单列成章,在需要处理图片文字时,把它作为视觉或多模态系统的一项可选能力,并根据准确性、成本和核验方式选择。

二、设计真正能暴露问题的测试

测试集应覆盖训练条件之外但仍属于任务范围的变化:更换桌面和墙面背景;在自然光、室内光和轻微背光下拍摄;改变远近、角度和遮挡;使用训练中没有出现过的同类植物个体。每次测试尽量只改变一个因素,才能判断错误可能来自哪里。

还要加入陌生输入:塑料仿真叶、印刷图片、不是植物的绿色物体、同时包含多株植物的远景,以及三个类别都不适合的叶片状态。分类模型仍会在已有类别中选一个最高分,甚至可能很高。因此“最高分大于阈值”只能作为接受条件之一,不能替代任务范围检查。

按类别查看结果比整体正确率更重要。混淆矩阵可以统计真实类别与预测类别的组合,帮助发现“发黄经常被判为正常”还是“正常经常被判为发黄”。若漏掉斑点类的后果更受关注,就应单独统计该类召回情况,并通过补充多样样本、检查标签或缩小用途来改善。

三、从视觉结果形成采用判断

一次合理的判断应包含四层证据。第一层是数据:来源是否有授权,类别是否清楚,植物个体是否正确隔离。第二层是模型:是否超过简单基线,验证与测试是否接近。第三层是泛化:背景、光照、角度和新个体变化后,错误是否明显增加。第四层是用途:错误发生后由谁确认,是否会触发不安全行动。

应用中的AI只承担“从图像到候选类别”的部分;若系统还要生成说明,生成内容必须引用可见证据并标明不确定项。普通程序负责保存原图、分数和人工确认结果。开发与学习AI可以根据带编号的失败记录归纳模式、建议下一轮只改变一个变量,也可以帮助检查程序修改范围,但不能把分类标签扩写为病因和处置结论。

本章原型适合用于课堂观察记录的初步整理。它不适合在没有品种信息、时间变化和专业复核的情况下判断植物健康,更不应自动控制任何设备。若测试发现模型主要依赖背景、某一类召回过低或陌生输入经常高分,应选择继续收集证据、限制使用范围或停止使用,而不是用更流畅的界面掩盖问题。

本章小结

计算机视觉模型首先接收像素数组。卷积在局部区域共享计算并逐层形成特征图,迁移学习复用预训练网络的通用视觉表示,再用较少任务数据学习新分类。图像分类、目标检测、图像分割和多模态视觉的主要区别在输出,选择技术前必须先明确任务。可靠测试要按植物个体隔离数据,控制背景、光照和角度变化,检查陌生输入和逐类错误。模型提供候选,人依据来源、范围和后果作最终判断。

关键术语

目标测试

  1. 彩色图像输入模型后,首先表现为什么? A. 植物名称 B. 像素数值数组 C. 病因结论 D. 养护建议
  2. 迁移学习在本章中的主要作用是什么? A. 自动产生正确标签 B. 复用预训练网络的通用视觉特征 C. 取消测试集 D. 保证陌生输入不会出错
  3. 若要精确标出叶面斑点区域,哪类任务最合适? A. 图像分类 B. 文本生成 C. 图像分割 D. 表格回归
  4. 下列哪一种划分最能减少图像数据泄漏? A. 同一片叶的照片随机分到三份数据 B. 按文件名最后一位划分 C. 同一植物个体全部进入同一份数据 D. 全部图片同时用于训练和测试
  5. 判断并改错:“迁移学习冻结了基础网络,因此整个模型没有任何参数需要训练。”
  6. 判断并改错:“三分类模型对陌生塑料叶给出0.95分,说明它已经可靠识别了真实植物状态。”
  7. 用“局部计算、特征图、逐层组合”三个要点说明卷积如何处理图像。
  8. 对同一幅包含多片叶的植物照片,分别说明分类、检测和分割会输出什么。
  9. 设计一组检查背景偏差的视觉测试,说明哪些条件保持不变、改变什么、记录什么。
  10. 某模型整体正确率为86%,但“叶片有斑点”类别10张测试图只识别出4张,陌生输入又经常得到高分。请给出是否采用的判断,并写出至少三项后续措施。