ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ADOPD:多模态大模型在线蒸馏,轻量模型实现工业异常检测

ADOPD:多模态大模型在线蒸馏,轻量模型实现工业异常检测 ADOPD 这个名称最近在工业异常检测方向出镜率不低。它的全称是 Reference-Privileged On-Policy Distillation for MLLM-Based Industrial Anomaly Detection核心思路可以概括成一句话用多模态大模型MLLM当导师在线蒸馏出一个轻量学生模型再把学生模型部署到产线质检场景。这个方法最有价值的地方在于训练阶段可以让教师模型拿到“参考特权信息”推理阶段却不依赖大模型只跑学生模型显存和时延成本能明显降下来。这篇内容不打算只复述论文摘要我会把 ADOPD 拆成 MLLM、On-Policy Distillation、Reference Privileged 三个关键词讲清楚说明它们分别在解决什么问题然后给出一套从环境准备、数据组织、训练蒸馏到 API 批量推理的落地思路。如果你正在工业异常检测项目里考虑引入多模态大模型又担心单卡显存不够、产线推断速度跟不上这篇文章可以直接收藏备用。需要先说明一个边界如果项目暂未公开完整代码下面给出的环境参数、命令行和 Python 脚本都属于通用模板实际操作时需要按官方仓库、模型权重和数据集路径替换。所有关于显存、速度的数字都应以本机实测为准我不会用假数据代替真实体验。1. ADOPD 核心能力速览能力项说明技术类型多模态大模型 知识蒸馏 工业异常检测核心任务产品表面缺陷检测、异常分割、缺陷分类与描述训练范式参考特权下的在线策略蒸馏推理模型轻量学生模型不依赖 MLLM 教师模型训练硬件需 NVIDIA GPU显存取决于教师模型规模通常建议 24GB 以上推理硬件视学生网络而定常见 CNN/ViT 可低至 6GB 或更少CPU 可跑但速度慢是否支持 CPU 推理通常可以但吞吐量会明显下降是否支持 API可自行封装 FastAPI/Flask 服务是否支持批量任务支持输入目录遍历或消息队列均可适合场景产线质检、PCB/钢材/电池/纺织表面缺陷检测、边缘设备部署从性能预期看这套方法的优势不在“一个新的检测网络”而在于把 MLLM 的复杂推理能力迁移到小模型上。也就是说最终线上跑的是小模型能力却有一部分来自大模型对图像和文本的联合理解。这也是 ADOPD 在工业瑕疵检测场景里值得关注的原因。2. 适用场景与使用边界2.1 适合什么场景工业异常检测通常有两类难题一类是缺陷样本少、形态多样传统 CV 规则容易漏检另一类是产线对部署成本敏感没法在每台工控机上放一块 24GB 显存的 GPU。ADOPD 这类“大模型蒸馏小模型”方法正好瞄准这两类问题。比较合适的场景包括表面缺陷检测金属表面划痕、凹坑、氧化、脏污。印刷品与 PCB 检测焊点偏移、线路断线、字符缺损。电池极片与新能源材料检测涂布不均、划痕、破损。纺织与无纺布检测破洞、污渍、纹理异常。需要输出文字描述的场景例如同时给出“缺陷类型”和“位置区域说明”。2.2 不适合什么场景如果数据量太少或者缺陷类型和正常产品几乎没有视觉差异任何深度学习方法的收益都有限。ADOPD 虽然可以利用 MLLM 的常识但依然需要一定量的正常样本和缺陷样本来做蒸馏训练。如果产线要求毫秒级响应且设备端没有 GPU只靠 CPU 跑较大的学生模型依然可能达不到帧率要求。此时需要配合量化、剪枝、ONNX/TensorRT 加速而不是只改蒸馏方案。2.3 版权、隐私与安全边界工业质检数据通常来自真实生产线可能包含设备型号、工艺参数、员工操作画面等敏感信息。使用 ADOPD 或任何 MLLM 方案时务必确认数据是否有权限用于训练和测试。涉及第三方模型权重时要检查开源协议是否允许商用涉及客户数据时尽量在本地或私有化环境训练避免把图像直接上传到公网 API。如果项目后续要接外部大模型 API不能把未脱敏的产线照片随意发送如果准备做声音、人脸、数字人等相关扩展必须获得明确授权。本文只讨论工业异常检测不涉及这些方向的具体实现。3. 方法拆解从三个关键词理解 ADOPD3.1 为什么是 MLLM多模态大模型MLLMMultimodal Large Language Model可以同时理解图像和文本。比如输入一张产品图模型不仅能判断“有没有缺陷”还能回答“缺陷在哪个位置、可能是什么类型、是否需要返工”。这种能力对工业质检非常有用因为传统模型通常只输出一个分数或 mask缺少可解释性。但 MLLM 也有明显问题模型体积大、推理速度慢、显存占用高。直接把他部署在每条产线上硬件成本很高。工业场景要的是“能力接近大模型但体积和速度接近小模型”这正好是蒸馏要解决的问题。3.2 On-Policy Distillation 解决什么问题蒸馏Distillation通常是把教师模型的输出作为软标签让学生模型去学习。但很多蒸馏方法是离线完成的先离线跑一遍教师模型把预测结果存下来再训练学生模型。这种做法简单但容易出现训练和推理分布不一致。On-Policy Distillation或者说在线策略蒸馏强调的是学生模型在当前策略下产生样本或特征教师模型再基于这些样本进行指导。整个过程更像“边采样边学习”学生模型不断更新教师模型也不断给出当前最需要的知识。这样能减少教师知识与学生接收能力之间的鸿沟特别适合缺陷类别分布不断变化的工业场景。在 ADOPD 中在线蒸馏的“策略”通常可以理解为学生模型如何从图像中提取特征、如何判断异常区域教师模型则根据当前学生模型的表征提供更精细的异常分数或语义指导。这个过程中教师模型知道的东西比学生多但最后线上推理时只保留学生模型教师模型作为一个训练阶段的辅助角色。3.3 Reference-Privileged 的含义“Reference”在工业异常检测里通常指参考图像比如无缺陷的正常样本。很多异常检测方法会拿待测图和参考图做对比缺陷区域会与参考图不一致。传统方法把这个比较过程放在线上推理阶段意味着实际部署时也必须有一张干净参考图且要保证参考图与当前产品是同一形态否则干扰很大。ADOPD 的 Reference-Privileged 思路是训练阶段教师模型可以使用参考图像作为“特权信息”更准确地识别缺陷学生模型在训练时只接收待测图不接收参考图。这样学生模型被迫自己学会“什么是正常、什么是异常”部署时就不再依赖参考图。这个思路和计算机视觉里的 Privileged Information 类似教师拥有额外信息学生只学习最终能力。换句话说参考特权降低的是部署时的依赖而不是训练时的上限。教师模型能看参考图所以它给出的监督信号更干净学生模型不能看参考图所以它必须真正理解正常纹理和缺陷纹理的区别而不是做简单的图像差分。3.4 整体流程概括数据准备正常样本 缺陷样本 参考图像 | 教师模型加载 MLLM可选 LoRA 微调输入待测图和参考图输出缺陷描述/异常分数 | 在线蒸馏学生模型接收待测图教师模型根据当前学生特征输出软监督 | 推理部署只保留学生模型输入单张图像输出异常分数 / 分割图 / 缺陷类别理解这个流程后后续的部署思路就很清晰了需要同时处理教师模型和学生模型但重点优化对象是学生模型。4. ADOPD 本地部署环境准备4.1 操作系统与基础环境从复现和部署的通用经验来看Linux 是首选Ubuntu 20.04 或 22.04 的兼容性最好。Windows 也可以做推理和部分训练但多模态大模型在 Windows 上容易出现路径、显存、加速库兼容问题不建议作为正式训练环境。建议先确认以下内容# 查看 GPU 驱动与显卡 nvidia-smi # 查看 Python 版本 python --version # 查看 PyTorch 与 CUDA 是否可用 python -c import torch; print(torch.__version__, torch.cuda.is_available())如果torch.cuda.is_available()返回False说明 PyTorch 版本与 CUDA 驱动不匹配需要重新安装对应版本的 PyTorch。4.2 Python 依赖ADOPD 的复现通常需要以下依赖Python 3.8 或更高版本。PyTorch 2.x。Transformers、Accelerate、Peft。OpenCV、Pillow。Numpy、Pandas、Scikit-learn。可视化或评估用的 Matplotlib、Seaborn。安装命令可以直接写成pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install transformers accelerate peft pip install opencv-python pillow numpy pandas scikit-learn matplotlib这里的 CUDA 版本号要根据本机驱动选择不是固定的。4.3 硬件门槛与磁盘空间训练阶段需要加载 MLLM 教师模型通常这类模型权重从 7B 到 34B 不等。7B 模型用 FP16 加载时权重本身大约 14GB 以上再加上优化器和激活值单卡 24GB 是比较稳妥的起步配置。如果教师模型是 34B 级别就需要多卡或使用量化方式加载。学生模型的选择决定了最终部署门槛。如果是轻量 CNN比如 ResNet50 或 MobileNet 系列推理显存可以控制在几个 GBCPU 也能跑只是速度会慢。工业部署时还可以把学生模型导出为 ONNX 或 TensorRT进一步降低延迟。磁盘方面建议准备 50GB 以上可用空间。工业图像数据集通常比较大MLLM 权重也可能达到几十 GB。4.4 端口占用检查如果后续要启动 API 服务建议先检查端口是否被占用lsof -i:8000 netstat -tunlp | grep 8000如果被占用可以换一个端口或者用--port参数指定。5. 训练与蒸馏部署思路5.1 数据目录组织工业异常检测数据集建议按以下方式组织便于写通用脚本dataset/ ├── reference/ │ └── good/ │ ├── ref_001.png │ └── ref_002.png ├── train/ │ ├── good/ │ └── defect/ └── test/ ├── good/ └── defect/reference目录放无缺陷参考图train目录放训练图片test目录放测试图片。如果缺陷图同时有像素级 mask可以再放一个mask目录用于异常分割评估。5.2 教师模型加载与微调MLLM 教师模型不一定要从头训练。常见做法是加载预训练权重用 LoRA 或 Prompt Tuning 在工业数据上做轻量微调让模型理解当前产线的缺陷定义。这样可以节省显存也避免覆盖多模态大模型原有的视觉语言能力。一个通用加载示例from transformers import AutoProcessor, AutoModelForCausalLM model_id your-mllm-teacher-model-name processor AutoProcessor.from_pretrained(model_id, trust_remote_codeTrue) teacher AutoModelForCausalLM.from_pretrained( model_id, torch_dtypeauto, device_mapauto, trust_remote_codeTrue )实际使用时model_id要替换成官方仓库提供的模型路径。不要直接照抄这里的大模型名称。5.3 在线蒸馏伪代码由于不同论文对“策略蒸馏”的具体实现不同这里只给一个可理解的伪代码结构。核心是每个 batch 内同时跑教师和学生模型教师根据自己的“特权输入”生成软标签学生模型只用普通输入并计算蒸馏损失。import torch # 假设 student 和 teacher 都已经加载 # teacher 需要参考图 ref_imagesstudent 不需要 for epoch in range(total_epochs): for batch in dataloader: images batch[images] # 待测图 ref_images batch[ref_images] # 参考图仅教师使用 # 教师模型输出软标签或注意力特征 with torch.no_grad(): teacher_out teacher(images, ref_images) # 学生模型只输入图像 student_logits student(images) # 计算蒸馏损失 有监督损失 loss_distill distillation_loss(student_logits, teacher_out) loss_task task_loss(student_logits, batch[labels]) loss loss_distill alpha * loss_task loss.backward() optimizer.step() optimizer.zero_grad()这个脚本只是为了说明数据流不能直接运行。实际实现中教师模型是否需要梯度取决于具体方案有些方法会同时更新教师模型。5.4 学生模型选择学生模型可以是分类网络、分割网络或混合结构。工业异常检测里常见的做法是只判断“正常/缺陷”使用 ResNet、MobileNet、ViT-Tiny 作为编码器后面接一个分类头。需要定位缺陷区域使用 U-Net 或分割头输出像素级异常概率图。需要输出缺陷类型分类头改为多标签输出或结合文本描述。选择学生模型时要先定好部署设备的算力上限再反推模型大小。不要一开始就选大模型否则后面量化、加速会很痛苦。6. 功能测试与效果验证6.1 测试数据集测试集不能和训练集重叠。建议单独留出一部分真实产线采集图片包含正常样本和缺陷样本。缺陷样本要尽量覆盖不同缺陷类型、不同光照、不同角度否则验证结果会过于乐观。如果使用公开数据集比如 MVTec AD 等要注意 ADOPD 论文里是否使用了相同协议。不同论文的数据划分方式不同直接比较指标可能不公平。6.2 评估指标工业异常检测常见指标包括指标含义使用场景AUROC异常分数排序能力衡量整体区分度F1-score阈值下的综合精度召回产线固定阈值时更实用误检率正常样本被判为缺陷的比例影响产线停线频率漏检率缺陷样本被判为正常的比例影响质量风险IOU分割结果与真值 mask 的重合度定位类任务线上部署时漏检率和误检率往往比 AUROC 更值得关注。AUROC 高不代表固定阈值下效果好还要画出 PR 曲线或 ROC 曲线确认阈值。6.3 推理验证脚本可以用一个简单的 Python 脚本跑测试集输出每张图的异常分数和预测标签import os import torch from PIL import Image from torchvision import transforms model load_student_model(path/to/student_model) transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.5], [0.5]) ]) test_dir dataset/test/defect threshold 0.5 for img_name in sorted(os.listdir(test_dir)): img_path os.path.join(test_dir, img_name) image Image.open(img_path).convert(RGB) input_tensor transform(image).unsqueeze(0) with torch.no_grad(): score model(input_tensor).sigmoid().item() pred defect if score threshold else normal print(f{img_name}: score{score:.4f}, pred{pred})这里load_student_model需要自己实现。如果你想测试多个阈值可以保存所有分数后统一分析不需要每张图都同时输出阈值结果。6.4 判断效果是否达标的办法不能只看测试集整体指标。建议把错误样本单独保存到fail/目录逐张人工查看。重点看漏检的缺陷是不是和训练集缺陷差异很大。误检的位置是不是反光、标注残留、正常纹理变化导致。模型对同一产品不同角度是否稳定。如果效果不达标优先检查训练数据和标签是否一致。很多蒸馏方案效果不好并不是蒸馏过程有问题而是数据里正常样本和缺陷样本分布不对。7. 接口 API 与批量推理部署7.1 用 FastAPI 封装推理服务当学生模型训练好后可以用 FastAPI 封装一个轻量推理服务方便接到产线 MES 系统或质检软件里。下面是一个通用示例路径、模型加载方式需要按实际项目替换。from fastapi import FastAPI, UploadFile, File from pydantic import BaseModel import io import torch from PIL import Image from torchvision import transforms app FastAPI() model load_student_model(path/to/student_model) transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.5], [0.5]) ]) class Result(BaseModel): file_name: str anomaly_score: float label: str app.post(/predict, response_modelResult) async def predict(file: UploadFile File(...)): image Image.open(io.BytesIO(await file.read())).convert(RGB) input_tensor transform(image).unsqueeze(0) with torch.no_grad(): score model(input_tensor).sigmoid().item() label defect if score 0.5 else normal return Result(file_namefile.filename, anomaly_scorescore, labellabel)启动服务uvicorn api_server:app --host 0.0.0.0 --port 8000如果不需要对外网暴露host建议改为127.0.0.1避免产线数据被局域网内其他设备访问。7.2 curl 调用示例服务启动后可以用 curl 测试curl -X POST http://127.0.0.1:8000/predict \ -F filetest_defect.png预期返回类似{ file_name: test_defect.png, anomaly_score: 0.87, label: defect }7.3 批量目录处理批量任务不一定需要每次都走 HTTP 接口。如果只是在服务器上跑离线检测可以直接写一个目录遍历脚本把所有结果保存到 CSV 或 JSON 文件。import os import csv import torch from PIL import Image from torchvision import transforms input_dir batch_input output_csv results.csv threshold 0.5 results [] for img_name in sorted(os.listdir(input_dir)): img_path os.path.join(input_dir, img_name) if not img_name.lower().endswith((.png, .jpg, .jpeg)): continue image Image.open(img_path).convert(RGB) input_tensor transform(image).unsqueeze(0) with torch.no_grad(): score model(input_tensor).sigmoid().item() results.append([img_name, round(score, 4), defect if score threshold else normal]) with open(output_csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, score, label]) writer.writerows(results) print(fdone, total {len(results)} images)如果是实时产线建议用消息队列把任务发到多个 worker 并发处理而不是在 HTTP 请求里同步执行大 batch 推理。批量任务需要记录每张图的状态方便失败后重试。7.4 接口稳定性与并发FastAPI 默认是同步接口推理函数会阻塞线程。如果并发量高建议把推理函数放到线程池或进程池中。更稳妥的方式是使用队列请求来了先进队列后台 worker 处理前端轮询结果。这样可以避免多并发同时抢 GPU 造成显存溢出。对产线系统来说稳定性比单次请求速度更重要。调用超时、显存占用、并发上限这些都要在联调阶段测清楚。8. 资源占用与性能观察8.1 显存观察方法训练和推理过程中可以用以下命令实时观察显存占用nvidia-smi -l 1也可以看 PyTorch 内部的显存分配print(torch.cuda.memory_allocated() / 1024**3, GB) print(torch.cuda.memory_reserved() / 1024**3, GB)不同模型、不同 batch size 下显存占用差异很大。不要直接套用别人的数字要以本机实测为准。8.2 教师模型和学生模型的资源差异训练阶段MLLM 教师模型通常是显存大头。如果只做推理蒸馏可以采用torch.no_grad()并配合混合精度减少显存占用。若显存仍不够就要把教师模型改造为 FP16 或 INT8 量化或者使用梯度检查点。推理阶段学生模型应该做到单卡可部署甚至可以导出到边缘设备。具体能压到多少取决于图像输入分辨率分辨率越高模型计算量越大。Batch size在线推理通常 batch size 1。模型量化FP16、INT8、INT4。推理引擎PyTorch、ONNX Runtime、TensorRT。8.3 性能瓶颈分析如果推理速度达不到产线要求可以按顺序排查图像预处理是否成了瓶颈例如 OpenCV 解码耗时。模型前向耗时是否稳定有没有 CPU 和 GPU 频繁切换。是否每次请求都重新加载模型。是否使用了不必要的浮点计算例如输入没有做归一化。是否在 GPU 和 CPU 之间多次拷数据。工业场景中一张 1920x1080 图直接输入模型往往很慢通常需要裁剪或缩放到模型适配尺寸。这里要平衡漏检率和速度不能简单把分辨率调得太低。9. 常见问题与排查方法问题现象可能原因排查方式解决方案训练时显存不足MLLM 教师模型过大或 batch size 过高观察nvidia-smi显存占用降低 batch size、使用梯度检查点、量化教师模型、多卡并行模型加载卡死权重文件路径错误或下载不完整检查模型目录、用 sha256 校验权重重新下载对应模型权重确认路径CPU 与 GPU 完全不一致没有加载 GPU 设备查看torch.cuda.is_available()检查 CUDA 与 PyTorch 版本重装对应版本异常分数全都接近 0.5学生模型未收敛或训练标签混乱检查训练 loss 曲线随机抽几张图人工看增大训练轮数整理标签重新训练漏检率偏高缺陷样本数量不足或教师模型没理解缺陷定义单独测试教师模型在缺陷图上的输出增加少量缺陷样本调整教师模型 promptAPI 请求超时服务未启动或模型还在加载中查看服务日志用 curl 测试等模型加载完成后再测试或提供健康检查接口批量任务卡住某张异常图片解码失败或线程死锁在每张图处理前后打印日志捕获异常并跳过坏图加入超时控制量化后效果明显下降INT8 量化对异常细节有损失对比量化前后 AUROC使用混合量化对关键层保留 FP16如果遇到依赖安装失败优先创建一个干净的虚拟环境不要直接往系统 Python 里装。虚拟环境下重装 PyTorch、Transformers 通常能解决大部分冲突。10. 最佳实践与合规使用建议10.1 先从小数据跑通流程第一次复现 ADOPD 时不要上来就训练完整数据集。建议先用几十张图跑通教师加载、学生训练、蒸馏、推理、评估这一整套流程。只要流程能跑通再逐步增加数据定位问题会容易很多。为每个阶段准备一套小脚本例如01_preprocess.py、02_train_teacher.py、03_distill_student.py、04_evaluate.py。这样可以独立运行也不会因为一处改动影响全流程。10.2 保留最小可运行配置训练时把关键超参数固定下来形成一份配置 JSON{ model: { teacher: your-teacher-model-name, student: resnet50, image_size: 224 }, training: { batch_size: 8, epochs: 20, learning_rate: 1e-4, alpha: 0.5 }, data: { train_dir: dataset/train, test_dir: dataset/test, reference_dir: dataset/reference }, output: { checkpoint_dir: checkpoints, result_dir: results } }这套配置的作用是让复现结果可追踪。修改任何参数后都要保留实验记录否则后面很难判断效果变好是因为数据、蒸馏方式还是模型结构。10.3 数据与标签一致性工业异常检测最容易踩的坑是标注标准不一致。不同标注员可能对“轻微划痕”的定义不同导致模型学到错误边界。建议在标注前统一标准并在训练集里抽查标注质量。如果缺陷 mask 是分割任务还要确认 mask 是否和图像对齐。很多标注工具导出格式不统一需要写脚本统一处理。10.4 合规与隐私使用真实产线数据做实验必须确认数据来源和权限。涉及客户委托的数据要签好保密协议涉及员工或人体相关的视觉数据要脱敏后再使用。MLLM 教师模型如果来自第三方开源项目要检查模型权重协议确认是否可以商用、是否需要署名。蒸馏后的学生模型并不会消除训练数据中的隐私信息。在对外发布服务或演示样例时要避免使用包含敏感细节的真实照片。11. 总结与下一步ADOPD 最值得尝试的点是把 MLLM 的语义理解能力和工业异常检测的部署需求结合起来。训练时让教师模型拥有参考特权在线蒸馏时又让学生模型不依赖参考图这个设计思路对产线落地很友好。如果你准备试这个方法我建议先验证三件事第一教师模型在你自己的缺陷数据上能不能给出靠谱的异常分数或语义描述第二学生模型在没有参考图的情况下能否接近教师模型的检测水平第三推理阶段的速度和显存占用是否满足现场设备条件。最容易踩的坑通常是数据质量和标签不一致而不是蒸馏算法本身。模型训练不收敛、检测效果差优先怀疑数据再调参数。下一步可以关注的方向是更轻量的学生骨干网络、把 PyTorch 模型导出到 ONNX/TensorRT、用真实产线数据做灰度测试。等这一套流程稳定后ADOPD 这类方法的价值就不只体现在论文指标上而是能真正降低多模态模型在工业场景里的落地成本。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进