1. Halcon深度学习分类模型到底是个什么东西
1.1 从产线质检的痛点说起
我在做机器视觉项目这些年,被问得最多的一类问题就是:“这个划痕算不算不良?”“这个字符有没有印歪?”“这颗螺丝有没有装反?”——这些问题用传统的Halcon算子,比如find_shape_model、measure_pos、threshold,能做,但做得非常痛苦。因为传统算子本质上是靠边缘、灰度、几何特征去卡阈值,一旦产品换批次、光照飘一点、来料表面有油污,阈值就得重新调,调到最后工程师自己都崩溃。
Halcon的深度学习分类模型,就是来解决这类“人一眼能看出来,但写规则写到吐”的问题的。它属于Halcon Deep Learning系列里最基础、最容易上手的一类模型,官方叫Deep Learning Classification,底层是一个卷积神经网络(CNN)分类器。你给它一批已经分好类的图片,比如“OK”和“NG”两个文件夹,它自己学特征,最后输出一个类别标签加一个置信度分数。
这里要先把概念理清楚,很多人一上来就搞混:
| 模型类型 | 解决什么问题 | 典型算子 | 输出 |
|---|---|---|---|
| 分类 Classification | 这张图属于哪一类 | classify_image | 类别 + 置信度 |
| 检测 Detection | 图里有没有目标、在哪 | detect系列 | 框 + 类别 |
| 分割 Segmentation | 每个像素属于哪一类 | segment_image | 像素级掩膜 |
| 异常检测 Anomaly | 没见过的不良长啥样 | apply_dl_model | 异常分数 |
分类模型是这四类里数据标注成本最低的——你不需要画框,不需要标像素,只要把图片按文件夹分好类就行。这也是为什么我一般建议新手从分类模型入门,跑通一遍整个流程,再去碰检测和分割。
1.2 它和OpenCV、YOLO那套有什么区别
经常有人问我:“Halcon深度学习和OpenCV、YOLO有啥区别?我直接用YOLOv8分类不香吗?”
这个问题得分开看。YOLO系列(包括v8、v11、v13这些)本质是通用目标检测框架,分类只是它的一个附带能力,生态活跃、社区模型多、预训练权重丰富,训练一般跑在Python + PyTorch上。而Halcon的深度学习是深度集成在Halcon自己的算子体系里的,它的优势不在模型本身有多先进,而在于:
- 部署链路极短:训练完的模型直接就是
.hdl文件,read_dl_model读进来就能在Halcon里推理,不需要Python环境、不需要ONNX转换、不需要TensorRT折腾。 - 和传统算子无缝混用:你可以先用
threshold粗定位,再reduce_domain裁出ROI,然后送进分类模型,整个流程在一个.hdev里跑完。 - 工业现场稳定性:Halcon的推理对CPU/GPU的兼容性做得比较扎实,产线上工控机没有独显也能跑(慢一点而已)。
反过来说,如果你追求SOTA精度、要做超大模型、要用LoRA微调、要玩多模态,那Halcon确实不是首选,该上PyTorch还是得上。工具没有优劣,只有合不合适。产线单机部署、要求快速落地、工程师是Halcon背景的,选Halcon分类模型基本不会错。
1.3 适合谁来学,需要什么前置基础
这篇内容我假设你满足以下条件:
- 装好了Halcon(20.11之后的版本对深度学习支持比较完整,建议23.11或更新),有可用的License(深度学习模块需要单独的授权,这点后面会专门讲)。
- 会基本的Halcon操作:读图、开窗口、显示、写个简单的
for循环。 - 对“训练集/验证集/测试集”这三个词不陌生,哪怕只是听说过。
如果你连Halcon的界面都没打开过,建议先去把read_image、dev_display、dev_open_window这几个算子跑一遍再回来。分类模型的代码量其实不大,难的是数据准备和参数调优的思路,这两块我会重点展开。
2. 训练之前必须想清楚的几件事
2.1 你的问题真的适合用分类模型吗
这是我最想先泼的一盆冷水。分类模型的隐含前提是:一张图(或一个ROI)里只有一个主体,且这个主体整体属于某一类。
举几个适合和不适合的例子,你对号入座:
- 适合:整块PCB板判断“良品/不良品”;一个瓶盖判断“正/反”;一颗药片判断“完整/破损”。
- 不适合:一张图里要同时找出3个缺陷并分别定位(这是检测的活);要判断缺陷的具体形状轮廓(这是分割的活);不良样本极少、根本凑不齐第二类(这是异常检测的活)。
我见过太多人拿分类模型硬做检测的活,最后精度上不去,回头骂Halcon不行。不是模型不行,是问题选错了工具。判断标准很简单:如果你标注的时候只需要给整张图打一个标签,那就是分类;如果需要画框或涂像素,那就换模型。
2.2 数据集的三个硬指标
Halcon分类模型对数据的要求,我总结成三个硬指标,缺一个都容易翻车。
第一,类别要平衡。假设你做OK/NG二分类,OK有2000张,NG只有50张,模型会学出一个“无脑判OK”的偷懒策略,准确率照样95%以上,但NG一个都抓不到。经验值是每类至少100张起步,类间数量比控制在1:3以内。NG样本实在少,就靠数据增强(旋转、亮度扰动、加噪声)硬凑,或者用gen_dl_samples配合增强参数。
第二,图像要一致。训练图和现场推理图必须来自同一套成像系统——同样的相机、镜头、光源、曝光、工作距离。我踩过最深的坑就是:实验室用环形光拍的训练集,现场换成条形光,模型直接废掉。如果现场光照会波动,训练集里就要主动把不同光照条件下的图都放进去,让模型学会忽略光照差异。
第三,标注要干净。分类的“标注”就是文件夹分类,听起来简单,但错标一张图的代价比检测里错画一个框大得多。我一般会做两轮复核:第一轮按文件夹抽查,第二轮把模型训出来之后,专门看置信度在0.5到0.7之间的那些图,这些是模型“犹豫”的样本,十有八九是标错了或者本身就是模棱两可的边界样本。
2.3 网络选型:pretrained_dl_classifier 那几档怎么选
Halcon提供了几个预训练的分类网络骨干,常见的有:
| 网络名称 | 输入尺寸 | 特点 | 适用场景 |
|---|---|---|---|
pretrained_dl_classifier_compact.hdl | 224x224 | 最轻量,速度快 | 类别少、特征明显、算力紧张 |
pretrained_dl_classifier_enhanced.hdl | 224x224 | 中等容量 | 大多数工业场景的默认选择 |
pretrained_dl_classifier_resnet50.hdl | 224x224 | 容量大,精度高 | 类别多、类间差异细微 |
选型的逻辑是:先用compact跑一版baseline,如果验证集精度不够,再往上升级。不要一上来就上ResNet50,训练慢、显存吃得多,而且在小数据集上反而容易过拟合。我做过一个药片缺陷分类的项目,compact就能到98.5%,换enhanced只涨了0.3%,但训练时间翻倍,完全不划算。
另外要注意,这些预训练模型是在ImageNet上训出来的,输入尺寸固定224x224。你的原始图如果不是这个比例,Halcon会做缩放,缩放会丢细节。如果缺陷本身很小,比如一个2像素的针孔,缩到224之后可能就没了。这种情况要么把ROI裁得更紧,要么考虑用检测模型。
3. 从零到一:完整训练流程拆解
3.1 数据准备与目录结构
Halcon读数据有两种方式:一种是用read_dl_classifier_data从文件夹读,另一种是用read_dl_samples从.hdict文件读。新手我强烈建议用文件夹方式,直观、好排查。
目录结构长这样:
dataset/ ├── train/ │ ├── ok/ │ │ ├── 001.png │ │ ├── 002.png │ │ └── ... │ └── ng/ │ ├── 001.png │ └── ... └── validation/ ├── ok/ └── ng/几个实操细节:
- 图片格式统一,别混着png、bmp、jpg。jpg有压缩伪影,工业场景尽量用png或bmp。
- 文件名别用中文和空格,Halcon某些版本对路径编码处理不友好,容易读图失败。
- train和validation的划分比例,我一般用8:2。如果数据量少于500张,用7:3,验证集多一点更能反映真实泛化能力。
- 划分要随机但可复现。别按文件名顺序切,否则可能train全是上午拍的、validation全是下午拍的,光照一变就露馅。用
shuffle打乱后再切。
3.2 核心算子逐个讲透
整个训练流程的核心算子其实就十来个,我按执行顺序列出来,每个都说说它到底在干嘛。
第一步,读数据。
read_dl_classifier_data ('dataset/train', 'class_name', TrainData, TrainLabels)这个算子会扫描目录,把每个子文件夹名当作类别名,返回图像路径元组和对应的标签元组。注意它返回的是路径不是图像本身,真正读图是在后面生成batch的时候。
第二步,创建分类器。
create_dl_classifier ('pretrained_dl_classifier_enhanced.hdl', 2, \ 'softmax', DLClassifierHandle)第二个参数2是类别数,这个必须和你文件夹里的类别数严格一致,多一个少一个都会报错。softmax是输出层,分类任务基本都用它。
第三步,设置超参数。
set_dl_classifier_param (DLClassifierHandle, 'batch_size', 8) set_dl_classifier_param (DLClassifierHandle, 'learning_rate', 0.001) set_dl_classifier_param (DLClassifierHandle, 'momentum', 0.9) set_dl_classifier_param (DLClassifierHandle, 'weight_prior', 0.0001)这几个参数是调优的重灾区,我后面单独开一节讲。
第四步,预处理参数。
set_dl_classifier_param (DLClassifierHandle, 'image_width', 224) set_dl_classifier_param (DLClassifierHandle, 'image_height', 224) set_dl_classifier_param (DLClassifierHandle, 'image_num_channels', 3)如果你用的是灰度图,image_num_channels设1,但预训练模型是按3通道训的,设1会丢掉预训练权重的第一层,精度可能受影响。我的做法是灰度图也复制成3通道,保持和预训练一致。
第五步,训练。
train_dl_classifier_batch (TrainData, TrainLabels, DLClassifierHandle)这个算子内部会自动做数据增强(旋转、翻转、亮度扰动),你不需要自己写增强代码。训练轮数通过set_dl_classifier_param的epochs控制,或者用train_dl_classifier_batch的循环次数控制。
第六步,评估。
evaluate_dl_classifier (ValidationData, DLClassifierHandle, EvaluationResult)返回的结果里包含混淆矩阵、各类精度、总体精度。重点看混淆矩阵,它能告诉你模型到底把哪一类错判成了哪一类。
第七步,保存。
write_dl_classifier (DLClassifierHandle, 'my_classifier.hdl')保存下来的.hdl文件就是最终产物,部署时read_dl_classifier读进来即可。
3.3 一个能直接跑的最小示例
下面这段代码是我平时做baseline用的模板,改改路径就能跑:
* 读训练数据 read_dl_classifier_data ('dataset/train', 'class_name', TrainData, TrainLabels) read_dl_classifier_data ('dataset/validation', 'class_name', ValData, ValLabels) * 创建分类器,2类 create_dl_classifier ('pretrained_dl_classifier_enhanced.hdl', 2, 'softmax', DLHandle) * 设置参数 set_dl_classifier_param (DLHandle, 'image_width', 224) set_dl_classifier_param (DLHandle, 'image_height', 224) set_dl_classifier_param (DLHandle, 'image_num_channels', 3) set_dl_classifier_param (DLHandle, 'batch_size', 8) set_dl_classifier_param (DLHandle, 'learning_rate', 0.001) set_dl_classifier_param (DLHandle, 'epochs', 30) * 训练 train_dl_classifier_batch (TrainData, TrainLabels, DLHandle) * 评估 evaluate_dl_classifier (ValData, ValLabels, DLHandle, EvalResult) * 保存 write_dl_classifier (DLHandle, 'pill_classifier.hdl')这段代码跑通,你就完成了80%的工作。剩下的20%是调参和排查问题,那才是真正拉开差距的地方。
4. 参数调优与避坑实战
4.1 学习率、batch_size、epochs 怎么配
这三个参数是训练的核心,我用一个表格把经验值列出来,再说背后的逻辑。
| 参数 | 常用范围 | 我的默认值 | 调整方向 |
|---|---|---|---|
| learning_rate | 0.0001 ~ 0.01 | 0.001 | 精度不涨就降,震荡就再降 |
| batch_size | 4 ~ 32 | 8 | 显存够就加大,小数据集用4 |
| epochs | 20 ~ 100 | 30 | 看验证集精度曲线,不再涨就停 |
学习率是最关键的。太大,loss来回震荡不收敛;太小,训练慢到怀疑人生。我的经验是从0.001起步,如果训练loss在前5个epoch就掉到很低但验证精度上不去,说明过拟合了,降学习率没用,得加数据或加正则。如果loss一直居高不下,先检查数据标注是不是有问题。
batch_size影响的是梯度估计的稳定性。batch太小(比如2),梯度噪声大,训练不稳定;batch太大,泛化可能变差。工业场景数据量通常不大,8是个比较稳的中间值。显存不够就降到4,别硬撑。
epochs不是越多越好。我一般会设一个较大的值(比如100),然后每5个epoch评估一次验证集精度,连续3次不涨就提前停。Halcon本身没有内置的early stopping,得自己在循环里写判断。
4.2 数据增强:Halcon帮你做了多少
Halcon的train_dl_classifier_batch内部默认会做这些增强:
- 随机旋转(小角度)
- 随机缩放
- 随机亮度/对比度扰动
- 随机裁剪
这些对工业场景基本够用。但如果你有特殊需求,比如产品会360度旋转,那默认的小角度旋转就不够了,得自己在预处理阶段用rotate_image把图转一圈生成多个副本。
注意:增强不是越多越好。过度增强会让模型学到“旋转不变性”这种在产线上根本不存在的特性,反而降低精度。增强策略要贴合实际工况——产线上产品怎么摆放,训练集就怎么增强。
4.3 常见报错与排查速查表
我把这些年踩过的坑整理成一张表,遇到问题先对号入座:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 训练loss不下降 | 学习率过大/数据标注错乱 | 降到0.0001重试,抽查标注 |
| 验证精度远低于训练精度 | 过拟合 | 加数据、加weight_prior、减epochs |
| 某一类精度特别低 | 类间不平衡 | 补充该类样本或调class_weights |
| 读图报错 | 路径含中文/格式不支持 | 改英文路径,统一png |
| License报错 | 深度学习模块未授权 | 检查License是否含DL模块 |
| 推理速度慢 | 用了大网络/CPU推理 | 换compact,或启用GPU |
| 置信度普遍偏低 | 训练不充分/域差异大 | 加epochs,检查训练与现场成像一致性 |
关于License我要多说一句。Halcon的深度学习模块是需要单独授权的,普通License跑不了create_dl_classifier。很多人下载安装完发现算子报错,第一反应是版本问题,其实是License没覆盖DL模块。这个在项目启动前就要确认,别等到训练到一半才发现。
4.4 推理部署:训练完怎么用起来
训练只是上半场,部署才是见真章的地方。推理的核心算子就两个:
read_dl_classifier ('pill_classifier.hdl', DLHandle) classify_image (Image, DLHandle, ClassName, Confidence)classify_image返回预测类别和置信度。实际产线上,我一般会加一个置信度阈值判断:置信度高于0.9直接判,低于0.9的图单独存下来人工复核。这样既保证了产线节拍,又不会漏掉边界样本。
如果要在C++或C#里调用,思路是一样的:ReadDlClassifier读模型,ClassifyImage推理。Halcon的深度学习模型跨语言调用非常干净,不像PyTorch模型还得转ONNX、装运行时,这点在工业部署上是实打实的优势。
5. 几个容易被忽略的进阶细节
5.1 类别不平衡的补救手段
前面提过类间数量比控制在1:3以内,但现实中NG样本就是少,怎么办?三个办法:
- 加权损失:Halcon支持通过
class_weights给不同类别设权重,让少样本类的loss贡献更大。这个参数在set_dl_classifier_param里设。 - 过采样:把NG样本复制多份,配合不同的增强参数,让每轮训练都能见到足够多的NG。
- 合成样本:用
gen_dl_samples配合增强算子,程序化生成变体。
我一般优先用加权损失,简单直接。过采样容易导致过拟合,合成样本则要小心生成得太假。
5.2 训练集和现场不一致怎么办
这是工业视觉最头疼的问题。实验室训得好好的,现场一上就拉胯。我的处理流程是:
- 先量化差异:拿现场图跑一遍模型,看置信度分布。如果普遍在0.6以下,说明域差异很大。
- 小样本微调:从现场采几十张图,混进训练集重新训。不需要全部重训,在已有模型基础上继续
train_dl_classifier_batch即可。 - 在线迭代:产线跑起来之后,把置信度低的图定期回收,人工标好再喂回去。这是个持续迭代的过程,不是一锤子买卖。
5.3 模型评估不能只看准确率
准确率这个指标在类别不平衡时会骗人。OK占95%,模型全判OK也有95%准确率,但NG一个没抓到。所以我评估时必看三个东西:
- 混淆矩阵:看清楚错判的方向。
- 每类召回率:NG的召回率才是产线真正关心的。
- 置信度分布:正常样本和异常样本的置信度是否分得开。
如果NG召回率上不去,宁可牺牲一点OK的准确率,把判定阈值往NG方向偏。产线上漏检的代价远大于误检。
6. 我个人的一些实操体会
最后分享几个文档里不会写、但实际项目里特别管用的经验。
第一,先跑通再优化。别一上来就纠结网络选型、参数调优。用compact网络、默认参数、30个epoch先跑一版,看看baseline在哪。有了baseline,后面每一步调整才知道是涨了还是跌了。
第二,数据质量决定上限,模型和参数只决定你能不能摸到上限。我见过太多人把时间花在调参上,结果回头一看训练集里混了十几张标错的图。花半天时间把数据清洗干净,比调三天参数管用。
第三,训练日志一定要存。每次训练的配置、精度、混淆矩阵都记下来,最好连模型文件一起归档。产线上出问题回溯的时候,你会感谢当时留了日志的自己。
第四,别迷信端到端。Halcon分类模型和传统算子结合使用,效果往往比纯深度学习好。比如先用threshold把ROI裁出来再分类,比整图直接分类精度高得多,因为模型不用去学“忽略背景”这件事。
第五,模型是要养的。上线不是终点,是起点。产线换料、换批次、换季节,成像都会变。建立一个定期回收低置信度样本、人工复核、增量训练的闭环,模型才能长期稳定。
这套流程我在好几个项目上跑过,从药片缺陷到瓶盖正反,从PCB良品判定到字符印刷质量,基本都能落地。Halcon的深度学习分类模型不是什么黑科技,它就是一个把CNN封装成工业算子的工具,用好它的关键不在模型本身,而在你对问题的理解和对数据的把控。