Appearance
颗石藻智能识别平台技术路径
科研初期需要识别特定属种颗石藻并计数统计以根据相关海温公式来反推当时环境的地球状态,我在实际进行显微镜观察计数后发现真正的瓶颈是传统人工计数方法效率低且后续仍需要图像辅助。因此设计搭建了该颗石藻智能识别平台,用两周跑通了“图像上传-自动识别-人工修正-模型改进”的全流程,将识别计数效率提高80%。过程中沉淀出图像分割、分类模型的调优及使用方法,以及可复现的AI Agent工作流,后续复用到两个项目中。
这个项目将这项工作拆成了两个适合机器学习处理的步骤:先在整张显微图像中定位颗石藻候选体,再对每个候选区域进行分类。模型给出第一版结果后,研究人员可以在网页中接受、拒绝、改类、调整框或补充漏检目标;经过人工确认的数据还能被导出为新的训练集,用于下一轮模型优化。
平台目前通过云端服务器反向代理对公网开放,访问地址为:

速查:打开上面的地址 → 拖入一张显微图像(或填写图像/目录路径) → 点击”运行检测” → 在右侧队列里对每一项接受 / 拒绝 / 改类 → 点击”保存草稿”或”提交审核”。这四步就能完成一次最基本的识别与审核,后文再展开每一步的细节和原理。
项目解决了什么问题
平台当前提供以下能力:
- 对单张显微图像进行颗石藻候选体检测;
- 将每个候选体划分为
Noelaerhabdaceae、Helicosphaera、Calcidiscus、Coccolithus或Others; - 在原图上显示检测框、类别与分类置信度;
- 支持人工接受、拒绝、改类、调整边界框及新增漏检框,并支持按状态筛选、关键词搜索、批量接受待审核项和多步撤销;
- 填写图像路径或使用本地目录选择按钮时自动识别为目录并连续审核队列中的图像;
- 保存带来源、原始预测和人工修改记录的结构化审核文件,区分”仍有待审核项”的草稿和”已全部审核完成”的最终结果;
- 将审核结果同时导出为目标检测数据集和图像分类数据集;
- 使用新数据继续训练检测模型和分类模型,形成持续改进的训练机制。
从应用角度看,机器负责处理大量重复的搜索与初筛工作,研究人员把注意力集中到低置信度、易混淆和模型遗漏的样本上;从工程角度看,每一次人工修正都会成为可追溯、可复用的数据资产。
一次完整识别如何完成
平台把原本分散的图像识别和标注操作整合在同一个网页中。一张显微图像从输入到形成可训练数据,会依次经过以下步骤:
text
打开网页
-> 输入或上传一张显微图像(或填写/选择一个图像目录)
-> 点击“运行检测”
-> 查看原图上的候选框、类别和置信度
-> 在右侧审核队列中筛选、搜索、接受、拒绝或修改识别结果
-> 在原图上拖拽新增模型漏检的目标
-> 点击“保存草稿”(仍有待审核项)或“提交审核”(已全部审核完成)
-> 将人工确认结果导出为下一轮训练数据项目中的一张样例图像为:
text
dataset\ODP1208\1\图像-FreeModeAcquisition-11--01.jpg在当前项目环境中实际运行该样例时,平台在一张 2752 × 2208 的显微图像中给出了 39 个候选目标,并输出五个类别的预测结果。该数字是一次真实运行的页面结果,只用来说明系统链路已经贯通,模型在独立测试集上的性能指标需要另行统计。
启动平台
环境准备
平台使用 uv 管理 Python 依赖,首次运行前需要在项目根目录执行:
powershell
uv syncpyproject.toml 中锁定的 PyTorch 来自 CUDA 12/13 系列的官方索引,因此部署机器需要匹配版本的 NVIDIA 驱动;没有可用 GPU 时,检测和分类仍可以退回 CPU 运行,只是推理速度会明显变慢(参见后文“高级配置如何选择”一节中的 CUDA 自检说明)。
分类模型 checkpoint(例如下文用到的 bioclip2-rfdetr-20260607-dino/checkpoint.pt)和检测模型权重不随代码仓库分发,需要提前训练得到,参见后文“将人工审核结果导出为训练数据”和“使用导出数据继续训练”两节。
进入项目目录
在 PowerShell 中执行:
powershell
Set-Location "D:\Documents\Cocolithophores_workflow"启动 Gradio 网页
当前项目可使用 uv 启动:
powershell
uv run python scripts\gradio_review_app.py `
--classifier-checkpoint model\crop_classifier\bioclip2-rfdetr-20260607-dino\checkpoint.pt `
--server-name 127.0.0.1 `
--server-port 7860也可以直接使用项目虚拟环境:
powershell
.venv\Scripts\python.exe scripts\gradio_review_app.py `
--classifier-checkpoint model\crop_classifier\bioclip2-rfdetr-20260607-dino\checkpoint.pt `
--server-name 127.0.0.1 `
--server-port 7860浏览器打开:
text
http://127.0.0.1:7860如果端口被占用,可以把启动命令和网址中的 7860 同时替换为其他端口,例如 7861。
通过服务器转发实现公网访问
本机默认只监听 127.0.0.1,仅能在本机访问。若要通过云端服务器转发实现公网访问,通常需要:
- 启动命令中把
--server-name改为0.0.0.0(或反向代理所在的内网地址),使 Gradio 监听非本地网卡; - 在云端服务器上配置反向代理(如 Nginx)将公网域名/端口转发到本机
7860端口所在的内网地址; - 在反向代理层加访问控制(Basic Auth 或其他鉴权),避免识别与审核功能直接暴露给未授权访问者;
- 确认反向代理保留了 WebSocket 转发(Gradio 依赖 WebSocket 做界面更新),否则页面会出现连接后无响应的问题。
公网访问地址见文首。
单张图像识别
选择图像
页面提供三种输入方式:
- 把图片拖入“上传单张图像”区域,或点击该区域选择文件;
- 在“图像文件或目录路径”中填写服务器上的图像文件或目录路径;
- 点击“选择本地目录并自动扫描”,通过浏览器原生目录选择器直接上传整个目录。
如果同时提供了上传图像和路径,上传图像优先。项目内的相对路径会按照项目根目录解析,也可以直接填写绝对路径:
text
D:\Documents\Cocolithophores_workflow\dataset\ODP1208\1\图像-FreeModeAcquisition-11--01.jpg如果“图像文件或目录路径”填写的是一个目录,平台会在点击“运行检测”时自动识别为批量任务:扫描目录、载入第一张图像并直接运行识别,不需要额外的“扫描”步骤,详见后文“批量浏览与连续审核”。
选择模型
页面会自动扫描项目中的模型文件并显示两个下拉框:
| 模型 | 作用 | 当前平台配置 |
|---|---|---|
| 检测模型 | 在整张图中定位颗石藻候选体 | legacy-single-candidate-yolo26n-gpu-smoke/weights/best |
| 分类模型 | 对检测框裁剪出的目标进行分类 | bioclip2-rfdetr-20260607-dino |
选择下拉项后,下面的模型路径会自动更新。”刷新”按钮用于重新扫描新加入的模型。通常不需要手动编辑完整路径。
页面启动参数 --classifier-checkpoint 的默认值指向 smoke-dinov2-label-metrics,这是早期用于验证流程的冒烟测试 checkpoint;bioclip2-rfdetr-20260607-dino 是当前用于正式识别的分类模型,因此启动命令中显式传入了这个路径。新一轮训练产出新 checkpoint 后,同样需要在启动命令或页面下拉框中手动切换。
运行识别
点击“运行检测”。平台会依次完成:
- 读取高分辨率显微图像;
- 运行目标检测并获得候选框;
- 按检测框从原图裁剪候选体;
- 对每个候选体进行五分类;
- 生成带类别和置信度的可视化结果;
- 在右侧审核队列中载入所有结构化预测。
运行完成后,“状态”栏会显示 predictions.json 的保存路径,页面顶部的“任务与模型设置”面板也会自动收起,把更多空间留给识别结果和审核队列。这意味着本次预测不仅显示在页面上,也已经保存为可以继续分析的结构化文件。

如何阅读识别结果
原图上的每个青色框代表一个候选目标,框顶端由类别名称和分类置信度组成。例如:
text
Helicosphaera 0.83表示分类模型把该检测框中的目标判断为 Helicosphaera,当前最高类别概率约为 0.83。
审核表格同时记录以下信息:
| 字段 | 含义 |
|---|---|
| 检测 ID | 当前图片中每个候选体的稳定编号 |
| 预测类别 | 模型输出的类别 |
| 审核状态 | 人工对该预测采取的操作 |
| 修正类别 | 人工确认后的最终类别 |
| 备注 | 记录模糊、破损、重叠等特殊情况 |
| x1, y1, x2, y2 | 检测框在原图中的像素坐标 |
| 检测置信度 | 检测模型认为该区域包含候选体的置信度 |
| 分类置信度 | 分类模型对最高概率类别的置信度 |
检测置信度回答“这里是否有一个目标”,分类置信度回答“这个目标属于哪个类别”。二者对应两个不同任务,不应混为一个指标。
人工审核与修正
模型输出默认标记为 accepted,研究人员可以根据图像内容修改审核状态:
| 审核状态 | 使用场景 | 是否进入导出的训练集 |
|---|---|---|
| accepted | 检测框和类别均正确 | 是 |
| rejected | 该框是误检,应删除 | 否 |
| bbox_adjusted | 类别正确,但边界框需要调整 | 是 |
| relabeled | 框正确,但类别需要修改 | 是 |
| corrected | 框和类别均被修改 | 是 |
| added | 模型漏检,由人工新增 | 是 |

筛选、搜索与批量操作
审核队列不再是一张静态表格,而是自带一套工具条:
- 筛选下拉框:可切换“全部结果 / 只看待审核 / 只看不确定类别 / 只看人工修正 / 只看已拒绝”,快速把注意力集中到还没处理或最容易出错的目标上;
- 搜索框:按检测 ID 或类别关键词过滤,图像候选体较多时便于定位某一个目标;
- 接受全部待审核:一键把当前图像中所有仍为“待审核”的预测标记为已接受,按钮会先要求再次点击确认,避免误触;
- 撤销:最多回退最近 50 步表格或画布上的修改(包括批量接受、拖框和改类),配合
Ctrl+Z(Mac 为Cmd+Z)使用更快。
修改错误类别
在队列中选中一项,然后修改”修正类别”。平台支持以下快捷键:
| 快捷键 | 操作 |
|---|---|
| 1 - 5 | 按页面类别顺序修改类别 |
| A | 接受当前检测 |
| R | 拒绝当前检测 |
| ↑ / ↓ | 切换上一行或下一行 |
| J / K | 切换下一行或上一行 |
| Ctrl+Z / Cmd+Z | 撤销上一步修改(含表格编辑和画布拖框) |
1—5、A、R 需要先选中一行才会生效;↑/↓/J/K 未选中任何行时会默认从第一行开始切换。当鼠标焦点停留在文本输入框(例如“图像文件或目录路径”“精确坐标输入”)时,这些快捷键会被暂时禁用,避免和正常打字冲突。
当前五类的数字映射为:
text
1 -> Noelaerhabdaceae
2 -> Helicosphaera
3 -> Calcidiscus
4 -> Coccolithus
5 -> Others调整不准确的检测框
先在表格或图像中选中一个目标,再在交互画布上拖动或缩放检测框。已有框发生变化后,状态会自动转换为 bbox_adjusted;如果类别也被修改,则会转换为 corrected。
补充漏检目标
直接在原图上按住鼠标拖出一个矩形即可新增标注。平台会根据新框裁剪图像并再次调用分类器,给出一个初始类别,审核状态记为 added。
如果需要严格复现坐标,也可以展开“精确坐标输入”,填写 x1、y1、x2、y2、类别和备注,然后点击“按坐标新增检测框”。
保存审核结果
操作栏中的保存按钮会根据当前图像是否还有“待审核”项自动切换文案和行为:
仍有待审核项时按钮显示为“保存草稿”,点击后写入:
textartifacts\reviews\<会话编号>\<图像文件名>\draft.json全部结果都已审核(接受 / 拒绝 / 修正 / 新增)后按钮自动变为“提交审核”,点击后写入:
textartifacts\reviews\<会话编号>\<图像文件名>\review.json
批量浏览目录时还提供“提交并下一张”按钮,只有在当前图像已全部审核完成时才可点击,一次操作即可完成“提交审核 + 切换到下一张”。
无论草稿还是最终结果,文件都同时保留原始预测和人工修改记录,能够回答“模型原来怎么判断、人工后来改了什么、使用了哪张原图”等问题。保存动作本身不会直接触发训练,避免误操作污染模型。
除了这两个显式保存按钮,浏览器还会把当前审核队列的修改自动缓存到本机 localStorage(按图像路径区分);如果页面意外刷新或关闭标签页,重新打开同一张图像时会自动恢复未保存的修改,作为服务器端草稿之外的第二道保险。
批量浏览与连续审核
批量浏览不再需要单独展开某个面板,而是直接复用主输入区:
- 在“图像文件或目录路径”中填写一个目录(例如
dataset\ODP1208\1),或点击“选择本地目录并自动扫描”上传整个目录; - 点击“运行检测”(或目录选择完成后自动触发);
- 系统按自然顺序扫描目录中的图像、载入第一张并自动执行识别,操作栏中的“当前任务”会显示形如
批量进度:1/39的进度; - 使用“← 上一张”和“下一张 →”连续浏览,仅当队列中有一张以上图像时才可点击;
- 每张图确认后点击“保存草稿”/“提交审核”,或者在全部审核完成后直接点击“提交并下一张”一步切换。
如果当前图像有尚未保存的修改,第一次点击上一张或下一张时,系统只显示警告;再次点击同一方向才会放弃修改并切换。这一设计用于降低长时间审核时误丢标注的风险,配合浏览器本地自动缓存的草稿,进一步减少刷新页面或误操作造成的损失。
高级配置如何选择
一般情况下可以直接使用默认配置。面对不同分辨率、目标密度和成像质量的图像时,可参考下表调整:
| 参数 | 默认值 | 调整建议 |
|---|---|---|
| 检测置信度 | 0.25 | 误检过多时提高到 0.35-0.50;漏检多时适当降低 |
| IoU | 0.70 | 控制重叠框合并,通常无需修改 |
| 推理图像尺寸 | 1024 | 更大尺寸有利于小目标,但增加显存和耗时 |
| 最大检测数 | 100 | 目标密集图像可提高 |
| Top-k | 3 | 保存分类候选类别数量 |
| 分类弃权阈值 | 0.00 | 提高后,低置信度样本会标为 uncertain |
| 切片推理(SAHI) | 关闭 | 超高分辨率、小目标密集时启用 |
| 多尺度 | 关闭 | 同时合并全图和切片结果,精度与耗时均增加 |
| 切片尺寸 | 640 | 与目标尺寸和显存共同调整 |
| 切片重叠比例 | 0.20 | 避免切片边缘截断目标 |
页面顶部会显示 CUDA 状态。当前机器可识别 NVIDIA GeForce RTX 4060 Laptop GPU;若 torchvision 的 CUDA NMS 不可用,检测模块会自动回退到 CPU,而分类模块仍可使用 GPU。这种运行前自检能够避免设备配置错误在推理过程中才暴露。
torchvision 的 CUDA NMS 不可用通常是因为 torch 和 torchvision 没有装到同一个 CUDA 编译版本。当前 pyproject.toml 只把 torch 的来源锁定到 pytorch-cu130 索引,torchvision 由 ultralytics 间接引入、走的是默认 PyPI 索引,两者的 CUDA 编译版本不一定匹配,这正是回退到 CPU 的常见原因之一。排查时可以运行 python -c "import torch, torchvision; print(torch.__version__, torchvision.__version__)" 确认两者版本是否配套;如不匹配,需要把 torchvision 也加入 [tool.uv.sources] 并指向同一个 CUDA 索引。
将人工审核结果导出为训练数据
先验证全部审核文件:
powershell
uv run python scripts\export_review_datasets.py validate `
--reviews artifacts\reviews再创建一个带版本号的新数据集:
powershell
uv run python scripts\export_review_datasets.py export `
--reviews artifacts\reviews `
--output-dir artifacts\review_exports `
--version review-round-001输出包含两套彼此对应的数据:
text
artifacts\review_exports\review-round-001\
├── yolo\
│ ├── images\ # 原始全视野图像
│ ├── labels\ # YOLO 检测与类别标签
│ └── dataset.yaml
├── classification\
│ ├── images\<类别>\ # 按人工确认框裁剪的分类图像
│ └── manifest.jsonl
├── provenance.json # 每个对象的来源与修改记录
└── summary.json # 本轮导出摘要被拒绝的误检不会进入训练集;接受、改框、改类、综合修正和人工新增的目标会被导出。导出程序还会检查越界框、无效坐标和未知类别,避免错误标签进入训练管线。
使用导出数据继续训练
导出的数据集可以分别用于继续训练检测模型和分类模型,完整流程到此才算真正走完。
在现有检测权重基础上继续训练:
powershell
uv run python scripts\train_yolo_detector.py `
--data artifacts\review_exports\review-round-001\yolo\dataset.yaml `
--model model\detector_pretrain\legacy-single-candidate-yolo26n-gpu-smoke\weights\best.pt `
--epochs 20 `
--imgsz 1024 `
--batch 4 `
--device auto `
--project model\detector_review `
--name review-round-001 `
--exist-ok `
--single-cls训练完成后,新权重通常写入:
text
model\detector_review\review-round-001\weights\best.pt在现有分类 checkpoint 基础上继续训练:
powershell
uv run python scripts\dinov2_crop_classifier.py train `
--manifest artifacts\review_exports\review-round-001\classification\manifest.jsonl `
--output-dir model\crop_classifier `
--run-id review-round-001 `
--epochs 5 `
--batch-size 16 `
--device auto训练完成后,新 checkpoint 通常写入:
text
model\crop_classifier\review-round-001\checkpoint.pt拿到新权重后,回到“启动平台”一节,把启动命令中的 --classifier-checkpoint 和页面中的检测模型下拉框都切换为新路径,即可用更新后的模型开始下一轮审核。--device auto(检测)和 --device 传入具体 GPU 序号(分类,如 0)都依赖环境中安装的是 CUDA 版 PyTorch;如果 uv sync 拉取到的是 CPU 版本,训练会退回 CPU 并明显变慢,此时可以按前文“高级配置如何选择”一节中的排查方法,检查 torch/torchvision 的 CUDA 版本是否匹配。
项目的整体框架
平台采用“检测器 + 分类器 + 人工审核”的串联设计:检测器专注于从大图中找位置,分类器专注于判断每个小目标的类别,网页负责把模型结果变成人可以检查和修正的数据。
text
显微镜全视野图像
│
▼
目标检测模型(YOLO / RF-DETR)
│ 输出候选框、检测置信度
▼
按框裁剪候选体图像
│
▼
视觉分类模型(BioCLIP 2 / DINOv2 特征 + 分类头)
│ 输出类别、分类置信度、Top-k
▼
Gradio 人工审核平台
│
├── 接受正确结果
├── 拒绝误检
├── 调整边界框
├── 修改类别
└── 新增漏检目标
│
▼
review.json(原始预测 + 人工确认 + 来源追踪)
│
▼
训练数据导出
├── YOLO 全图检测数据
└── 分类裁剪图与 manifest
│
▼
下一轮检测器与分类器训练
└───────────────> 回到平台继续审核为什么要拆分为检测与分类两个模型
整张显微图像分辨率高,而单个颗石藻只占其中很小区域。如果直接对整张图进行五分类,会丢失“图中有多少个目标、分别在哪里”的信息;如果让检测器同时承担细粒度分类,目标尺寸、类别不平衡和相似形态会同时增加训练难度。
将任务拆开后,检测器可以专注学习“像颗石藻的结构在哪里”,分类器则在统一尺寸的局部裁剪图上学习细粒度形态。两个模型可以独立替换、评估和继续训练,人工调整框后能立即重新裁剪并分类,同一份审核结果也能同时反哺两个任务。这种模块化设计比把全部能力绑定在一个模型中更便于诊断和迭代。
模型与数据设计
检测阶段
当前平台使用 Ultralytics YOLO 检测器。旧项目标注只复用目标框的位置,并统一映射为候选体,避免继承不一致的旧分类规则。项目还保留了 RF-DETR、切片推理和多尺度合并接口,便于比较不同检测路线。
对于高分辨率显微图中的小目标,平台可以采用 SAHI 风格切片:把原图切成相互重叠的小块分别检测,再将结果映射回全图坐标并合并。这样可以减少整图缩放导致的小目标信息损失。
分类阶段
当前分类模型使用 BioCLIP 2 视觉特征和训练得到的分类头;项目代码也支持 DINOv2 路线。分类任务包含五类:
text
Noelaerhabdaceae
Helicosphaera
Calcidiscus
Coccolithus
Others现有训练记录包含 27,163 个训练样本和 7,143 个验证样本。由于 Noelaerhabdaceae 数量明显更多,训练使用加权交叉熵降低类别不平衡的影响,并尽量按来源组划分训练集和验证集,减少同源图像同时进入两边造成的数据泄漏。
该分类 checkpoint 在现有验证划分上的整体准确率记录为 96.98%,但数据类别高度不平衡,少数类的精确率、召回率和 F1 更能反映实际难度;显微图中的真实泛化效果还需要在独立来源、不同成像条件和人工盲审数据上继续验证。
数据资产
项目当前工作区已经汇集多种来源的图像,并保留数据清单、模型权重、预测结果、人工审核、导出数据和训练记录。数据通过 manifest、版本号和 provenance 相互连接组织。
这种设计使每个训练对象都能追溯到:
text
原始图像
-> 模型版本与原始预测
-> 人工审核状态
-> 最终边界框与类别
-> 数据集导出版本
-> 后续训练轮次代码结构与模块职责
text
Cocolithophores_workflow/
├── scripts/
│ ├── gradio_review_app.py # 页面组装、事件绑定、批量导航
│ ├── gradio_review_components.py # 自定义审核表格与交互画布
│ ├── gradio_review_inference.py # 模型扫描、推理、缓存和审核保存
│ ├── gradio_review_handlers.py # 表格与画布交互逻辑
│ ├── gradio_review_schema.py # 类别、审核状态与稳定数据结构
│ ├── detect_classify_pipeline.py # 检测—裁剪—分类主流水线
│ ├── export_review_datasets.py # 审核结果验证与双任务导出
│ ├── train_yolo_detector.py # 检测模型训练入口
│ └── dinov2_crop_classifier.py # 分类模型训练、评估与预测
├── dataset/ # 原始图像及分类样本
├── model/ # 检测与分类 checkpoint
├── artifacts/
│ ├── predictions_review/ # 每次模型预测
│ ├── reviews/ # 人工审核结果
│ ├── review_exports/ # 可训练数据集版本
│ └── training_rounds/ # 训练轮次与来源记录
├── tests/ # 数据结构、页面逻辑与 CLI 测试
└── docs/ # 使用说明、流程和项目记录页面代码被拆成应用、组件、推理、事件处理和数据结构五层,模型推理、审核规则和前端交互因此能够分别测试,替换检测器或分类器时也不需要重写整套页面。
工程成果
截至当前,项目已经完成从原始显微图像到人工确认训练数据的端到端链路,主要包括:
- 将全视野图像识别拆分为目标检测与细粒度分类两个可独立评估的任务;
- 整理多来源数据,并在分类训练中处理类别不平衡和来源泄漏问题;
- 接入 YOLO、RF-DETR、BioCLIP 2 和 DINOv2 等模型路线;
- 使用 Gradio 和自定义前端组件实现可交互的人工审核平台;
- 设计统一数据结构,将模型预测、人工修正和训练数据连接起来;
- 使用版本化导出和 provenance 文件记录数据来源与修改过程;
- 为数据结构、页面事件、批量导航、导出流程和训练入口建立自动化测试;
- 将一次性模型推理扩展为能够持续积累高质量标注的训练机制。
这些模块共同构成了一套可复现的实验与数据生产流程。模型版本、原始预测、人工修改、导出数据和训练轮次之间均保留明确关联,便于后续比较不同模型路线和数据版本。