跳转到内容

在图像标注上训练检测和分割模型

在研究自己的框上微调检测器,把预测的框作为预标签展示给标注者,用修正后的结果重新训练,并让运行适配玻片和细胞。

v2.14.0 新增

本教程在研究自己的边界框上训练目标检测器,并把检测器的框作为预标签展示给后续的标注者来修正。本教程面向已有 image_annotation 方案的研究负责人,他们读过训练你的第一个模型,或者熟悉管理员的训练页面。前半部分用 torchvision-detect 跑完一个完整的循环(图像上的框、一次训练运行、回写、修正,以及第二次运行),那里的每一步都在笔记本电脑的 CPU 上运行过。后半部分把这个循环适配到 YOLO、轮廓、SAM 精修、整图类别、全玻片、显微图像、地图、细胞、文档页面和视觉语言模型,每种情形各占一节。

安装视觉训练器

torchvision-detect 微调 torchvision 的 Faster R-CNN 检测器。该训练器需要 torch 和 torchvision。Potato 的默认安装不含这两个包,因为 torch 的下载有好几个 GB。请在负责训练的机器上安装视觉 extra:

bash
pip install "potato-annotation[train-vision]"

同一个 extra 也覆盖 torchvision-segment、hf-image-classification、sam-finetune、layoutlmv3 和 vlm-sft。缺少所需包的训练器会在训练页面上显示为“not installed”,并附上安装这些包的命令。

对于下面这个小项目,GPU 不是必需的。在 Apple silicon 的 Mac 上,torchvision-detect 和 torchvision-segment 在 CPU 上训练,而不是在 Metal(mps)上,运行日志会用警告“torchvision-detect does not run on Apple's Metal (mps) with this torch; using the CPU”记录这次切换。在 Potato 团队的测量中,默认图像尺寸下一个 Metal 训练步骤花的时间比一整个 CPU epoch 还长。对于更大的图像或更大的研究,请按远程训练的说明在 GPU 服务器上训练。

搭建图像项目

项目需要三样东西:图像、列出这些图像的数据文件,以及配置。把图像放进项目文件夹内名为 media 的文件夹,因为 Potato 默认把 <task_dir>/media/ 作为 URL 路径 /media/ 提供。配置用 media_directory 指明文件夹时,也可以用其他文件夹。

text
shapes/
  config.yaml
  data/images.jsonl
  media/img_001.png
  media/img_002.png
  ...

data/images.jsonl 的每一行是一个条目,带有 id 和图像的 URL 路径:

json
{"id": "img_001", "image_url": "/media/img_001.png"}

该配置定义一个带 bbox 工具和两个标签的 image_annotation 方案,并开启带回写的模型训练。该配置能通过 python -m potato.validate_cli --strict:

yaml
annotation_task_name: "Shape detection (model training)"
task_dir: "."
data_files:
  - "data/images.jsonl"
item_properties:
  id_key: "id"
  text_key: "image_url"
output_annotation_dir: "annotation_output"
 
require_password: false
admin_api_key: "demo-admin-key"
num_annotators_per_item: 1
 
annotation_schemes:
  - annotation_type: "image_annotation"
    name: "objects"
    description: "Draw a box around every circle and every square."
    source_field: "image_url"
    tools: ["bbox"]
    labels:
      - name: "circle"
        color: "#e6194b"
      - name: "square"
        color: "#4363d8"
 
model_training:
  enabled: true
  writeback:
    schemas: ["objects"]
    include_unlabeled: true
    min_confidence: 0.5

四项设置决定了这个循环:

  • source_field: "image_url" 告诉图像组件和训练器,哪个条目字段存放图像。
  • num_annotators_per_item: 1 让每张图像只有一位标注者,这样种子标签覆盖的图像就尽可能多样。
  • writeback.include_unlabeled: true 让每次运行在尚无人标注的图像上做预测。没有该设置时,运行只在留出的 val 和 test 图像上预测,而这些图像已经有人工标注的框。
  • writeback.min_confidence: 0.5 在一张图像的框的平均得分低于 0.5 时,扣下该图像的预测。这个下限作用于整张图像,因此标注者不会只看到一张图像里的一半框。

在项目文件夹内启动服务器:

bash
potato start config.yaml -p 8000

标注种子集

torchvision-detect 在 train 划分里少于 20 张已标注图像时拒绝拟合,而默认划分把 70% 的已标注图像放进 train。请至少标注 30 张图像,目标物体变化大时标注更多。在 http://localhost:8000 上手工画框。没有框的图像会被排除在训练之外,因为检测器分不清没有物体的图像和没有人标完的图像。提交的图像要画出其中的每个物体。

在任何人工标注之前,想先试跑这个循环的话,用户模拟器可以根据 gold 文件画框。每个 gold 条目给出图像 id 和方案的形状,坐标是归一化的:

json
[
  {"id": "img_001", "objects": [
    {"type": "bbox", "label": "circle",
     "coordinates": {"x": 0.158, "y": 0.849, "width": 0.106, "height": 0.146}}
  ]}
]
bash
python -m potato.simulator --server http://localhost:8000 --users 3 \
  --strategy gold_standard --competence good=1.0 --max-annotations 20 \
  --gold-file data/gold.json --sequential --fast-mode --no-export

模拟器会带几个像素的抖动重画每个 gold 框,在 good 水平上,模拟器还会漏掉或标错一些物体,就像认真的标注者偶尔也会那样。在我们对 80 张合成图像的测试中,三位模拟标注者在约一秒内标注了 60 张图像。

训练 torchvision-detect

打开 http://localhost:8000/admin,输入管理员密钥 demo-admin-key,然后打开 http://localhost:8000/admin/training。把 objects 选为方案,把 torchvision-detect 选为训练器,让 Parameters 留空,然后点击 Train now。同一次运行也可以用脚本启动:

bash
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs \
  -d '{"trainer": "torchvision-detect", "schemas": ["objects"]}'

运行从 torchvision 的 COCO 权重出发,并按方案的两个标签给检测器换上新的框头。第一次运行时,torch 会把 COCO 权重下载到 torch 的缓存里。默认值是小型的 fasterrcnn_mobilenet_v3_large_fpn 架构、15 个 epoch、每步 4 张图像,并在连续 4 个 epoch 没有更好的 val mAP 后提前停止。每种形状都按其边界框训练,因此标注者画的是多边形的研究,训练出来的仍是框检测器。每张图像有多位标注者时,运行在 model_training.aggregation 为该图像选定的那一份完整答案上训练,因为两个人的框很少完全吻合。

在我们使用的 Apple M4 Pro CPU 上,37 张 320 乘 240 像素的训练图像约用 106 秒训练完,运行在 5 个 epoch 后停止。运行的文件位于 annotation_output/training/runs/<run_id>/,拟合好的检测器在 artifacts/model/detector.pt。运行的设置和所用设备记录在 artifacts/model_card.json 中。

查看留出得分

每次运行都会在检测器从未训练过的图像上给检测器打分。image_annotation 方案的主得分是 map_50,即 COCO 平均精度均值,预测框与已标注物体的边界框的 IoU 为 0.5 或更高时算作匹配。运行页面还会记录 map_50_95,即 IoU 阈值从 0.5 到 0.95 的平均值。两位标注者对同一个物体画的框很少在 IoU 0.9 时重叠,所以更严格的阈值有一部分衡量的是人们画框的一致性。因此 Potato 以 0.5 阈值为主。衡量边界框上的一致性展示了如何直接检查这种一致性。

检测器保留 val 得分最好的那个 epoch 的检查点,因此 test 划分有图像时,主得分取自 test 划分。Runs 表在 Held-out score 列显示主得分。运行页面列出 n_train、n_boxes、每个划分的大小和覆盖率,以及在 val 和 test 上已标注和已预测的物体数。在我们对 80 张彩色圆形和方形合成图像的测试运行中,检测器在 test 的 map_50 上得到 0.99。合成形状比真实物体容易找得多,所以请在你自己的图像上判断得分。

检查预标签

一次运行成功之后,Potato 会把检测器的框写入每张符合条件的图像的 predictions 字段。GET /admin/training/api/status 在响应的 writeback 字段里报告结果,其中 n_written 是得到预标签的图像数,另外为每种扣下图像的原因给出一个计数,例如低于 min_confidence 的 n_skipped_low_confidence,以及运行已用于训练的图像的 n_skipped_train_split。train 划分的图像永远不会得到预标签,因为模型在自己的训练数据上的置信度说明不了新图像的情况。

用一个新用户名登录 http://localhost:8000。每张通过回写检查的未标注图像打开时,检测器的框已经画好。每个框都存储为 source: ai,并把检测器的得分存为 confidence。标注者可以移动或调整框的大小、更改标签、删除误检,并画出检测器漏掉的物体。标注者编辑这些框或转到下一张图像时,这些框就成为标注者的答案。标注者只是查看图像时,预标签绝不会被保存。模型审核队列按置信度从低到高提供带预标签的图像,审核者的 correct 判定会让该图像进入之后每次运行的 train 划分。

用修正后的框重新训练

每张被修正的图像都会成为下一次运行的人工标签。标注者处理完一批带预标签的图像之后,从训练页面再启动一次 torchvision-detect 运行。新的运行会替换上一次运行对每张图像的预测,新运行用于训练的图像上的预标签会被撤下。

要不用点击就重新训练,请添加 auto_retrain 块。这个片段扩展上面的 model_training 块,每新增 25 条标注就启动一次运行,最多每 10 分钟一次:

yaml
model_training:
  enabled: true
  auto_retrain:
    enabled: true
    trainer: torchvision-detect
    schemas: [objects]
    update_frequency: 25
    min_interval_s: 600
  writeback:
    schemas: [objects]
    include_unlabeled: true
    min_confidence: 0.5

预标签会把标注者锚定在模型的框上。等留出得分足以支持预标签之后,再为标注者开启回写,另见检测被不加思考盖章的预标签,了解检查标注者是否不看就接受框的方法。

用 Ultralytics YOLO 加快检测

ultralytics-detect 从 COCO 预训练检查点(默认是 yolo11n.pt)出发,在同样的框上训练 Ultralytics YOLO 检测器。YOLO 的训练和预测都比 torchvision-detect 快,预标签是同样可编辑的框。Ultralytics 采用 AGPL-3.0 许可,用 Ultralytics 训练并分发的模型适用 AGPL 的条款。Ultralytics 另售用于闭源场景的企业许可证。Potato 的 extra 不含 Ultralytics,因此请自行安装该包:

bash
pip install ultralytics

Potato 会在每次运行之前请你确认许可证。训练页面会显示确认框,API 运行需要在请求体里写 "accept_licence": true,自动重新训练需要 auto_retrain.accept_licence: true。没有确认时,API 会以 HTTP 400、needs_licence_ack: true 和消息“ultralytics-detect is licensed AGPL-3.0”拒绝这次运行。下面的请求确认许可证并启动运行:

bash
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs \
  -d '{"trainer": "ultralytics-detect", "schemas": ["objects"], "accept_licence": true}'

运行期间,训练器会关闭 Ultralytics 的使用分析、Ultralytics HUB、实验跟踪器集成和自动安装包。这些改动只在运行进程的内存中生效,你的 Ultralytics 设置文件保持不变。检查点下载到 ~/.cache/potato/ultralytics。默认值是 640 像素下 50 个 epoch,并在 10 个 epoch 后提前停止。

用轮廓代替框

torchvision-segment 在多边形、椭圆、自由曲线和画笔掩码轮廓上训练 torchvision 的 Mask R-CNN。框会被排除在分割训练之外,因为框并不说明哪些像素属于该物体。请给标注者 polygon 或 brush 之类的轮廓工具。如何标注图像分割掩码介绍各种工具分别适用于什么场合。

每个预测的掩码都会被描成最多 64 个顶点的多边形,因此标注者逐点修正预标签。掩码被分成若干块时,保留最大的一块。默认的 score_threshold 是 0.5,而不是检测器的 0.25。Mask R-CNN 对同一个物体的第二猜测得分在 0.25 到 0.6 之间,每个猜测都会成为标注者还要删除的一个形状。留出得分把预测的多边形与已标注的轮廓作比较。在 Mac 上,torchvision-segment 在 CPU 上训练。使用 torch 2.8 时,Potato 团队测得 256 像素图像的一个训练步骤在 Metal 上用 57 秒,在 CPU 上用 2.3 秒。真实的研究请使用 GPU 服务器。ultralytics-segment 是 YOLO 对应的训练器(yolo11n-seg.pt),同样是 64 顶点的多边形,同样需要确认 AGPL-3.0 许可证。

用 sam-finetune 把框变成轮廓

框画起来快,轮廓画起来慢。sam-finetune 微调 Segment Anything(SAM)的掩码解码器,把一个框变成你的标注者会画出的轮廓。图像编码器保持冻结,每张图像的嵌入只计算一次,因此一个 epoch 的开销很小。训练时,用每个已标注轮廓的边界框作为该轮廓的提示。运行会把每个提示框移动或缩放最多 box_jitter(默认为 5%),让解码器从像检测器给出的框那样松散的框中学习。

sam-finetune 精修框,需要有框作为起点。在同一个方案上的常见顺序包含两次运行:

  1. 在设置 writeback.include_unlabeled: true 的情况下运行 torchvision-detect 或 ultralytics-detect,让未标注的图像带上框预标签。
  2. 在同一个方案上运行 sam-finetune。每个框预标签都会变成多边形预标签,多边形取代这些框。

方案需要有供 SAM 学习的轮廓,因此请在 bbox 之外再给标注者一个轮廓工具。在留出的图像上,标注者自己的框就是提示,所以 val_mask_iou 报告框正确时轮廓的平均 IoU。这些留出预测会被打分,但绝不会存储或发布,因为这些预测是根据另一位标注者的答案作出的。回写报告用 n_skipped_heldout_only 统计这些留出预测。默认检查点是 facebook/sam-vit-base(Apache-2.0)。微调后的解码器只用于提供预标签。图像工具的点击分割在浏览器中运行另外的 ONNX 权重,sam-finetune 运行不会替换这些权重。

对整张图像分类

对于关于整张图像的问题,例如询问照片中是哪种动物的 radio 方案,请使用分类器,而不是检测器。hf-image-classification 微调 Hugging Face 的 AutoModelForImageClassification 能加载的任何模型,例如 ViT、DeiT、BEiT、ConvNeXt、Swin 或 DINOv2。默认模型是 google/vit-base-patch16-224-in21k。该训练器默认随机翻转训练图像。翻转会改变标签时(例如图像中有文字,或涉及左右),请在 Parameters 中把 flip 设为 false。lora: true 训练 LoRA 适配器和分类头,而不是训练全部权重。

当一个选择型方案已标注条目中至少 80% 带有以 .jpg、.jpeg、.png、.bmp、.webp、.tif、.tiff 或 .gif 结尾的图像路径时,该方案算作图像语料。在这样的语料上,没有指定向量化器的 sklearn-text 会在 CLIP 图像嵌入上拟合线性分类器。CLIP 基线不需要 GPU,只需要 pip install "potato-annotation[embeddings]" pillow。

图像路径和远程图像

图像训练器读取方案的 source_field 所指明的字段。没有 source_field 时,训练器取 image_url、image、image_path、file_name、url、path 以及音频和视频的对应字段中,第一个非空的条目字段。图像必须是项目内的本地文件:

  • 相对路径相对于 task_dir 解析,在 task_dir 下找不到文件时,相对于 media_directory 解析。
  • 像 /media/img_001.jpg 这样的 URL 路径相对于 media_directory 解析。标注页面从同一个位置加载图像。
  • 经由绝对路径、../ 或符号链接而位于任务目录和 media_directory 之外的文件会被排除,并给出警告。
  • 远程 http(s) 图像绝不会被下载。运行会排除每个图像是远程的或缺失的条目,运行日志会说明有多少,这些条目得不到预测。

要在远程图像上训练,请把它们下载到 media_directory。视觉格式页面和 potato import介绍如何把现有的图像数据集带入项目。

全玻片和其他大图像

检测和分割训练器会把每张图像缩放到几百像素,10 万像素的病理玻片或卫星场景上的物体在这种尺寸下就消失了。因此对于 image_annotation、microscopy 或 geo_annotation 方案,Potato 在构建运行的训练数据时把大图像切成图块。玻片格式(.svs、.ndpi、.mrxs、.scn 等)和长边达到 4,096 像素或更多的图像会自动分块。玻片需要 openslide:

bash
pip install "potato-annotation[wsi]"

训练使用包含已标注物体的图块,外加一些空图块的样本,每个物体都会被裁剪到所在图块内。预测覆盖组织上方的每个图块,每张图像最多 max_predict_tiles 个。每张图像的划分在图像被切分之前就已决定,因此同一张玻片的图块绝不会落入两个划分。图块预测会被合并回整张图像,两个重叠图块都找到的物体只保留一次。预标签使用整张图像的坐标,与深度缩放查看器存储的坐标相同。留出得分是在整张图像上计算的。远程后端下载的是图块,而不是玻片。

相关设置位于 model_training.tiling 下。这个片段以 1,024 像素的图块、在 20x 下训练一张 40x 的玻片:

yaml
model_training:
  enabled: true
  tiling:
    tile_size: 1024
    overlap: 128
    downsample: 2

请让 overlap 至少与物体一样大,因为较大的物体会被分块找到,每个图块一块。对于记录了分辨率的玻片,mpp 设置每个图块像素对应的微米数,并覆盖 downsample。在有些玻片上,有两项设置需要小心。空的训练图块假定已标注图像上的每个物体都画过了,因此标注者只画了玻片的部分区域时,请设置 background: 0。组织由玻片缩略图上的 Otsu 阈值检测,所以没有空白玻璃的玻片会丢掉颜色较浅的那一半,tissue_threshold: 0 则在每个图块上预测。tiling 表列出每个键及其默认值。

显微图像和地图

microscopy 方案的 ROI 可以训练 torchvision-detect、ultralytics-detect、torchvision-segment、ultralytics-segment 和 cellpose。每个带 ROI 的 z 切片和时间点,都按方案的默认通道和对比度渲染,成为单独的训练图像。渲染器就是查看器所用的合成器。尚未画任何内容的图像,在查看器打开时所在的位置预测,即中间的 z 切片和第一个时间点。预测以显微 ROI 的形式返回,并带有每个 ROI 的 z 和 t。细胞分类指向的是标签图像而不是形状,训练器会跳过细胞分类。

geo_annotation 方案的要素,在条目的栅格底图上训练同样的检测和分割训练器。GeoTIFF 按文件自身标签里的变换和 EPSG 代码定位,投影后的则用 pyproj 重投影。带范围的普通图像按经度和纬度定位。多边形、矩形、线和点在栅格上变成形状,多边形的洞会被填满。预测以 EPSG:4326 的 GeoJSON 要素返回,带有 source: model。地图把这些要素作为建议提供,只有当标注者编辑或接受其中一个时,这些建议才算作答案。底图是瓦片服务器或 GeoJSON 轮廓的地图没有可供训练的栅格,运行会排除这些条目并给出警告。

用 cellpose 处理细胞

cellpose 在 image_annotation 或 microscopy 方案中的细胞或细胞核轮廓上微调 Cellpose。请另行安装 Cellpose 3 或 4:

bash
pip install cellpose

默认的基础模型是 cyto3。nuclei 和 cyto2 也适用于 Cellpose 3,cpsam 适用于 Cellpose 4。Cellpose 自己的估计不准时,请把 diameter 设为典型细胞直径(单位为像素)。Cellpose 的分割不带类别,因此每个预测的细胞都得到 label 参数的值,未设置 label 时得到方案中最常见的标签。各类别外观不同时,请为每个标签训练一个模型。每个预测的掩码都以多边形返回,细胞的平均概率作为预标签的置信度。cellpose 在 train 划分里只需要 5 张已标注图像,检测器则需要 20 张。Cellpose 的代码是 BSD-3-Clause,权重来自 Cellpose 的作者,适用作者的条款。

声谱图

spectrogram_annotation 方案的框,在每段渲染成声谱图图像的录音上训练 torchvision-detect 和 ultralytics-detect。音频模型教程从头到尾介绍声谱图检测,包括渲染设置和训练器读取的录音格式。

用 layoutlmv3 处理文档版面

layoutlmv3 微调 LayoutLMv3,根据页面上的词及其位置,找出标注者在页面图像上画的区域。区域是任何带标签的块,例如标题、表格或总额。Potato 不运行 OCR,所以每个条目都带有页面的词,框使用页面图像的像素坐标或 0 到 1 的坐标:

json
{"id": "page_001", "image": "pages/page_001.png",
 "words": [{"text": "INVOICE", "bbox": [40, 52, 180, 82]},
           {"text": "Total", "bbox": [300, 700, 380, 730]}]}

regions 模式下的 PAGE XML 导入会根据文件的 Word 元素填写 words。来自 PDF 文本层或其他 OCR 工具的词也同样可用。每个词按其中心所落入的区域获得标签,预测的词会被合并回区域,每个区域变成围绕该区域的词的框预标签。留出得分是框 mAP。预测的框止于区域内的词,因此画得比词大得多的区域,得分会低于这些框应得的分数。

LayoutLMv3 权重的许可证是 CC BY-NC-SA 4.0,不可商用。与 YOLO 训练器一样,训练页面会在运行之前请求确认,API 运行需要 accept_licence: true,自动重新训练需要 auto_retrain.accept_licence: true。

用 vlm-sft 训练视觉语言模型

vlm-sft 在标注者关于图像的答案上微调视觉语言模型,默认使用 LoRA 适配器,默认模型是 Qwen/Qwen2.5-VL-3B-Instruct。该训练器从四种方案学习:

方案每个训练样本对应模型学习生成什么
图像条目上的 text条目标注者对方案所提问题的回答
region_caption带说明的区域以红框画出的区域的说明
grounding_eval已回答的表达区域(JSON)、一个点,或 {"absent": true}
gui_trajectory被判为 correct 且带点击的步骤点击,形式为 {"point": [x, y]}

坐标位于图像的 0 到 1,000 网格上,因此任何检查点都能从样本中学会这种格式。文本运行把生成的答案作为文本预标签发布。定位运行为每个表达发布一个区域或一个“不存在”,VLM 定位介绍 grounding_eval 方案。GUI 轨迹运行根据模型会点击的位置为每个步骤建议一个判定,评估计算机使用智能体介绍 GUI 轨迹。说明运行用 token F1 和 BLEU-4 打分,但绝不发布,因为带说明的区域是标注者的答案。

真实的检查点需要 GPU。Potato 团队在 Mac 的 Metal GPU 上,用 LoRA 在 26 张图像上训练了两个 epoch 的 Qwen/Qwen3-VL-2B-Instruct,用时 46 秒。在 Parameters 中写 {"lora": {"quantize": "4bit"}},会在 4 位基础权重上训练 QLoRA 适配器。请检查你所选模型的许可证。Qwen2.5-VL-3B 适用 Qwen 研究许可证,7B 模型是 Apache-2.0。LLM 微调教程介绍 LoRA、QLoRA,以及把模型发布到 Hugging Face Hub。

延伸阅读

参考文献

  • Ren, S., He, K., Girshick, R., and Sun, J. (2017). Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks. IEEE Transactions on Pattern Analysis and Machine Intelligence, 39, 1137–1149. doi:10.1109/TPAMI.2016.2577031
  • He, K., Gkioxari, G., Dollár, P., and Girshick, R. (2017). Mask R-CNN. 2017 IEEE International Conference on Computer Vision (ICCV), 2980–2988. doi:10.1109/ICCV.2017.322
  • Kirillov, A., Mintun, E., Ravi, N., Mao, H., Rolland, C., Gustafson, L., Xiao, T., Whitehead, S., Berg, A. C., Lo, W.-Y., Dollár, P., and Girshick, R. (2023). Segment Anything. 2023 IEEE/CVF International Conference on Computer Vision (ICCV), 3992–4003. doi:10.1109/ICCV51070.2023.00371
  • Stringer, C., Wang, T., Michaelos, M., and Pachitariu, M. (2021). Cellpose: a generalist algorithm for cellular segmentation. Nature Methods, 18, 100–106. doi:10.1038/s41592-020-01018-x
  • Huang, Y., Lv, T., Cui, L., Lu, Y., and Wei, F. (2022). LayoutLMv3: Pre-training for Document AI with Unified Text and Image Masking. Proceedings of the 30th ACM International Conference on Multimedia, 4083–4091. doi:10.1145/3503161.3548112