
简介资源包以mmdetection为框架将Faster-RCNN目标检测模型与DataAugmentation-ForObjectDetect数据增强技术组合构建成半导体晶圆表面缺陷自动识别系统。其适用对象是计算机视觉研发人员、算法学习者以及半导体产线质检工程师主要解决真实缺陷样本少、人工检测效率低、小目标与细粒度缺陷难定位等实际问题。压缩包共包含1161个文件大小约28.4MB包括802个Python源码文件、229张JPG图像样本、91个Markdown说明文档以及YAML配置、Shell脚本、Dockerfile、Jupyter Notebook 示例和MP4演示视频覆盖数据准备、模型训练、验证推理到结果可视化全流程。该资源包已有368人学习下载。通过学习使用者可以得到可直接运行的工程代码、完整数据增强与模型调参思路、mmdetection环境配置笔记和操作录屏并能基于示例扩展至划痕、污点、裂纹等多类缺陷场景快速搭建属于自己的自动检测系统。 做半导体晶圆表面缺陷自动识别最让我头疼的从来不是模型结构而是数据和部署之间的那条鸿沟。这个项目我前后折腾了将近两个月核心方案是基于mmdetection框架搭建Faster-RCNN目标检测模型再用DataAugmentation-ForObjectDetect做数据增强最终落地成一套能跑在产线边缘盒子上的晶圆缺陷识别系统。今天把完整技术链路、踩坑记录和调参思路写出来给正在做工业视觉检测的朋友做个参考。这套系统要解决的场景很具体工业相机拍下晶圆表面图像模型自动圈出颗粒污染、划伤、崩边、残胶等缺陷给出类别和位置。产线不要求我能区分几百种缺陷但漏检率必须压得很低因为一块有缺陷的晶圆流到后道工序损失是几何级放大的。所以我在技术选型上宁可牺牲一部分推理速度也要保证召回率。这个思路贯穿了后续所有决策包括为什么用Faster-RCNN而不是更快的YOLO包括数据增强怎么做也包括部署时怎么取舍。1. 项目背景与整体设计思路1.1 晶圆表面缺陷检测到底难在哪先说数据层面。晶圆缺陷有一个很麻烦的特点正常品数量巨大缺陷品极其稀少。产线一天拍几千张图可能只有几十张带着缺陷而且不同批次的缺陷形态差异很大。划伤可能是几条细线也可能是大片磨痕颗粒污染有大有小有的比头发丝还细崩边往往出现在晶圆边缘形状像锯齿。这种长尾分布直接导致一个问题——如果直接用原始数据训练模型模型会把“正常”学得特别好但真正的缺陷类别基本学不到。再说到图像本身。晶圆表面有大量重复的电路纹理和金属线条缺陷和纹理在灰度上经常非常接近。传统视觉方案在这个场景下很吃力阈值分割遇到低对比度缺陷就直接失效blob分析对不规则形状缺陷也容易误判。我之前试过用形态学运算加连通域分析理想情况下能抓到一部分颗粒污染但一旦光线角度变了同一块缺陷在不同图像里的灰度差很大阈值怎么调都不稳。所以这个项目本质上是一个“小样本、弱特征、高漏检代价”的检测问题。目标检测模型天然适合这个场景因为它能把“位置回归”和“分类”一起做掉不需要我手工设计特征。而模型选型的关键不是看榜单上的mAP多高而是要看它在这个数据形态下能不能稳定召回弱特征缺陷。1.2 为什么选mmdetection Faster-RCNN而不是YOLO我最早其实先试了YOLOv5因为网上资料多、部署简单、速度也快。但跑下来发现一个问题YOLO对几个像素大小的缺陷召回率明显不够。原因在于YOLO是单阶段检测直接在特征图上回归类别和框它对小目标的特征利用效率不如两阶段方法。Faster-RCNN多了一个RPN阶段先在一张图上选出几千个候选区域再用ROI head逐个精修相当于先把“疑似缺陷”筛出来再做精细分类。这个“先粗筛再精修”的流程对低对比度、小尺寸目标更友好。mmdetection选它的理由也很直接OpenMMLab生态把整个训练流程配置化了换backbone、调neck、改anchor都不需要动主代码改配置文件就行。对于工业项目来说这个特性太重要了因为你不确定上线前要调多少轮参。另外mmdetection里Faster-RCNN的实现很规范RPN的正负样本采样、ROI Align、NMS这些细节都处理得比较稳妥出了问题也方便查源码。当然我也不是完全否定YOLO。如果后续要部署到高帧率在线检测我大概率会用YOLO或者把Faster-RCNN蒸馏成一个轻量模型。但在当前阶段质检场景的优先级是“宁可慢一点也不能漏”Faster-RCNN的召回表现更符合业务要求。2. 数据与DataAugmentation-ForObjectDetect增强实战2.1 初始样本采集与标注数据来源是产线的光学检测设备它会自动扫描晶圆表面并保存高分辨率图像。一批缺陷图的量大概在700张左右其中各种缺陷类型分布极其不均划伤和颗粒污染相对常见崩边和残胶很少加起来可能不到100张。这个分布直接决定了后面训练的难点——不是所有类别都能学到足够的特征。我用Labelme做标注输出JSON文件框的类型是矩形框。类别命名我用英文因为后面转COCO格式和改配置文件都涉及类名用英文能避免编码问题。最终类别就5个particle、scratch、chip、stain、edge_crack。这里有一个我趟过的坑标注的粒度一定要统一。比如划伤有人习惯把整个划伤区域框成一个大框有人习惯把断断续续的划痕拆成多个框。如果标注口径不一致模型学到的边界就会很混乱。我在项目里定了一条规则同一连通区域的缺陷只标一个框框要贴着缺陷边界宁可紧一点也不要留太多背景。标注完以后建议让第二个人抽检一遍哪怕只是抽查20张也能发现不少问题。2.2 DataAugmentation-ForObjectDetect增强配置与避坑原始700张图肯定不够用即便Faster-RCNN预训练权重能带来一些迁移能力类别间的样本量差距也需要数据增强来拉平。这里我用的是DataAugmentation-ForObjectDetect这个库的核心价值在于它不是简单地对图像做变换而是会同步更新bbox坐标。增强时的框如果不同步训练数据就会变成“框错位”的脏数据模型收敛会变得很困难。我贴一下实际用到的配置片段你们可以照着改augmentation: - type: rotation value: 30 - type: scale value: [0.8, 1.2] - type: translate value: 0.2 - type: horizontal_flip value: true - type: vertical_flip value: true - type: brightness value: [0.8, 1.2] - type: contrast value: [0.8, 1.2] - type: blur value: 1看起来简单但有几类增强对缺陷检测是危险的第一随机裁剪要慎用。假如一个缺陷本身就很小裁剪后可能直接裁出框外数据的标签就废了。第二高斯模糊不能加狠了。晶圆划伤本来就是低对比度细线模糊强度超过一定程度模型学到的特征就全是“糊”的。第三翻转对方向敏感的缺陷要分情况。晶圆崩边一般出现在特定方向如果翻转后崩边跑到其他方向其实是在引入现实中不太存在的样本反而干扰模型。我实际扩增策略是每个类别单独设置增强倍数。颗粒污染和划伤这类样本相对多扩增2倍崩边和残胶扩增到5倍。最后训练集大概在3000张左右。这里有一个经验增强倍数不要贪多。一次扩到10倍以上模型会把同一张图的变换版本记下来验证集上看着很漂亮一到现场拍的新图就露馅了。增强完以后一定要抽一批图出来人工检查重点看bbox有没有跟丢、有没有被裁掉一半。这个检查花不了多少时间但能避免你后面整个训练白跑。3. mmdetection环境安装与自定义数据集接入3.1 版本匹配与安装要点mmdetection有一个让很多人头疼的点它跟mmcv的版本强绑定装错了直接import就报错。我调试下来最稳的组合是Python 3.8PyTorch 1.10CUDA 11.1mmcv-full 1.5.3mmdet 2.25.1这套组合跑Faster-RCNN很稳网上资料也多。mmdetection 3.x的API改动比较大如果不是新项目从零开始不建议直接上3.x。安装时我推荐用openmim这个工具pip install openmim mim install mmcv-full1.5.3 mim install mmdet2.25.1重点说一下mmcv-full。如果源码编译等的时间非常长而且经常因为GCC版本和CUDA_HOME环境变量不一致导致编译失败。用mim装预编译包能省掉这些麻烦它会根据你的PyTorch和CUDA版本自动选择对应的wheel。这个坑我踩过一次一开始手动源码编译编译了快一个小时最后报错说nvcc和gcc版本不匹配换mim之后十分钟搞定。3.2 数据集格式转换与模型注册mmdetection默认支持COCO和VOC格式我习惯直接用COCO格式因为它的annotation结构通用性好后面接什么工具都方便。Labelme的输出JSON需要转成COCO的JSON核心逻辑就是把每张图的标注信息汇总成一个统一的字典然后dump成json文件。转换脚本的关键部分大概长这样import json import glob from PIL import Image def labelme_to_coco(img_dir, label_dir, output_path): images [] annotations [] annotation_id 1 for img_idx, img_path in enumerate(glob.glob(f{img_dir}/*.png)): img Image.open(img_path) width, height img.size images.append({ id: img_idx, file_name: img_path.split(/)[-1], width: width, height: height }) label_file f{label_dir}/{img_path.split(/)[-1][:-4]}.json if not os.path.exists(label_file): continue with open(label_file) as f: label_data json.load(f) for shape in label_data[shapes]: x_min min(shape[points][0][0], shape[points][1][0]) y_min min(shape[points][0][1], shape[points][1][1]) x_max max(shape[points][0][0], shape[points][1][0]) y_max max(shape[points][0][1], shape[points][1][1]) w x_max - x_min h y_max - y_min annotations.append({ id: annotation_id, image_id: img_idx, category_id: class_to_id[shape[label]], bbox: [x_min, y_min, w, h], area: w * h, iscrowd: 0 }) annotation_id 1 with open(output_path, w) as f: json.dump({images: images, annotations: annotations, categories: categories}, f)转换完以后修改mmdetection的config文件。我用的是faster_rcnn_r50_fpn_1x_coco.py这组经典的预训练配置做基础需要改的地方有这么几处data_root换成你的数据集路径dataset_type保持CocoDatasetclasses换成你自己的5个类别名num_classes从80改成5ann_file和img_prefix分别指向标注json和图片目录这里最容易漏的是num_classes因为Faster-RCNN有两个head都要改。如果只改了RPN没改ROI head训练时loss能算但预测时类别数对不上推理结果全是乱的。我习惯在改完配置以后先故意把一个batch的数据跑一遍训练确认前向和反向都正常再正式跑。4. Faster-RCNN训练调参与缺陷识别效果优化4.1 训练策略与关键参数训练命令很简单python tools/train.py configs/faster_rcnn_r50_fpn_1x_coco.py --work-dir work_dirs/waf_defect但真正决定模型效果的是config里那些参数。我把关键参数列一下参数值说明lr0.005配合batch size 8使用lr_schedule1x12 epochs工业小数据集不用太长warmup500 iter打散预训练权重与目标域的差异batch_size8显存不够就降到4同时lr减半img_scale(1333, 800)如果缺陷太小调整到(1600, 1000)anchor_ratios[0.5, 1.0, 2.0]默认值后续针对长条缺陷调整anchor_scales[8]小目标多就增加小scalebatch size和learning rate是配套的。mmdetection里默认的lr是按8卡batch size 16来设置的如果你用单卡batch size 8初始学习率最好按比例缩比如从0.02降到0.005。直接拿默认lr跑单卡小batchloss很容易震荡。训练时我建议开启tensorboard或者用wandb记录loss曲线。重点看几个指标loss_rpn_cls、loss_rpn_bbox、loss_cls、loss_bbox。如果loss_cls下降很慢说明分类分支没学好优先检查数据是否有标注错误如果loss_rpn_cls一直震荡可能anchor设置不合理或者学习率偏大。4.2 缺陷类别不均衡与小目标调优第一轮训练跑完mAP整体在0.7左右但分类别看particle的AP有0.81edge_crack的AP只有0.42。这个结果在预料之中样本量小的类别就是学不好。我做了两件事一是把edge_crack和stain的数据增强倍数加大让模型在同一类上见到更多变化二是调整了anchor的ratios。晶圆缺陷的形状很有特点——划伤是长条状宽高比经常在5:1以上而默认的anchor ratios是[0.5, 1.0, 2.0]最大的2.0根本包不住长条缺陷。我把ratios改成了[0.2, 0.5, 1.0, 2.0, 5.0]训练后scratch类别的AP提升很明显从0.68涨到0.81。这个思路大家可以记一下不要迷信默认anchor先统计自己数据集里标注框的宽高比分布再针对性设置。过拟合的问题在第二轮训练后出现。训练集loss还在降验证集mAP却开始往下掉。原因是部分类别增强过多模型开始“背诵”训练集。处理办法是把扩增倍数适当回调同时对RPN的正负样本比例做了调整让背景样本占比稍微高一点帮助模型学得更泛化。最终验证集mAP稳定在0.83左右edge_crack的AP也涨到了0.65算是一个能接受的落地指标。5. 部署落地C/ONNX推理与问题排查5.1 ONNX导出与C推理训练完的模型是PyTorch的.pth格式没法直接用C推理。需要先导出成ONNX格式再用ONNXRuntime加载。mmdetection官方也提供mmdeploy工具支持一键导出OnnxRuntime和TensorRT模型。我当时用的是mmdeploy命令大致是python tools/deploy.py \ configs/mmdet/detection/detection_onnxruntime_dynamic.py \ configs/faster_rcnn_r50_fpn_1x_coco.py \ work_dirs/waf_defect/epoch_12.pth \ demo.jpg \ --work-dir deploy_output导出时有一个细节要特别注意导出函数的输入尺寸。如果部署环境对推理延迟有要求建议导出固定尺寸的ONNX模型比如固定到(800, 1333)。固定尺寸可以省掉动态shape带来的额外计算开销C端也不用处理复杂的动态维度逻辑。如果现场图片分辨率不固定就用动态shape导出但推理速度会慢一些。C端加载ONNX的代码不复杂核心就是建一个session把图像预处理成模型输入格式然后跑推理。在ONNXRuntime CPU上一张1333x800的图推理耗时大概在300-500ms。这个速度对于人工复检场景够用但如果要接自动分选机得换TensorRT加GPU推理能把单张耗时压到100ms以内。5.2 现场问题速查与工程经验部署阶段遇到的问题五花八门我把最典型的几个整理成表格方便大家排查问题现象可能原因解决方式ONNX导出时算子报错torch版本与导出工具版本不一致或模型里有自定义算子统一用mmdeploy配套的torch版本导出不要手动torch.onnx.export增强后bbox越界随机平移或旋转后目标超出图像边缘配置里加bbox_clip或在增强后过滤越界框推理结果重复框过多NMS的iou_threshold设置过高调小test_cfg里的nms.iou_threshold比如从0.5降到0.3大分辨率图检测慢整图直接送进模型小目标被缩放后更难检测做大图切块把晶圆图切成多个1024x1024的patch分别预测CUDA out of memorybatch_size过大或输入尺度太大推理时batch_size设1必要时降输入分辨率最后分享一个我个人的习惯在正式部署前把模型的test_cfg里的nms.iou_threshold往小调一点比如从默认的0.5改成0.3。这样重叠的候选框会被过滤得更狠输出的框数量少很多但是检测精度几乎不受影响。下游系统要统计缺陷数量时这个改动会省掉很多去重逻辑。做完整套系统我最想对后来者说的其实是缺陷检测的核心竞争力不是模型有多新而是数据整理的干净程度和部署的稳定程度。把这两件事做好Faster-RCNN这套方案完全够用。如果你正在做类似的工业视觉项目建议先从数据标注规范和增强策略入手再回头调模型你会发现自己能少走很多弯路。本文还有配套的精品资源点击获取