news 2026/10/19 2:56:23

深度学习焊接缺陷检测:从数据标注到U-Net落地全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深度学习焊接缺陷检测:从数据标注到U-Net落地全流程

1. 为什么必须用深度学习方法做焊接缺陷检测——传统算法的边界在这里

焊接缺陷检测系统,听起来像是一个很传统的工业质检话题,但真正用Python把深度学习方案完整落地时,你会发现这里面有大量容易被忽略的细节。我最近做一个模拟项目X,任务是针对射线检测图像中的气孔、夹渣、未焊透、裂纹等缺陷做自动定位与分类,要求不仅能画框,还要能统计缺陷数量与尺寸,最后为等级判定提供依据。项目整体采用基于深度学习的方案,目前已经跑通一套可复现的Python实现。如果你正在做类似的课程设计、实验室原型,或者打算在企业里搭一套焊缝质检算法验证环境,这篇文章应该能省掉不少弯路。

先摆结论:焊缝X光图像里的缺陷检测,深度学习能做的事远比传统机器视觉要多,但也不是把模型丢进去就能出结果。数据口径、标注尺度、模型选型、后处理逻辑,每一步都有坑。如果不提前想清楚这些东西,最后得到的很可能是一个“论文里很漂亮、现场用不了”的Demo。

1.1 焊接缺陷检测到底在检什么

射线探伤(RT)得到的数字化图像中,焊缝内部的缺陷会以不同的灰度形态呈现。气孔在图像里通常是圆形或近圆形的暗斑,中心灰度变化明显,直径往往很小。夹渣是不规则块状,亮度不均匀,有些比背景暗、有些比背景亮。未焊透和未熔合一般表现为沿坡口边界分布的连续线状暗条,比较粗。裂纹是最麻烦的一类,它细长、低对比度、端部尖锐,经常藏在焊缝余高的灰度起伏里,肉眼不仔细看都容易漏掉,更别说让传统阈值算法自动去找。

这些缺陷对结构安全的影响完全不同。裂纹和未焊透往往必须返工,气孔如果是个别的、尺寸不超标,可能还在允许范围内,但成串气孔也要处理。工业上通常依据检测标准来评级,所以检测系统最终要输出的不只是“有没有缺陷”,而是缺陷的位置、类型、数量、尺寸,以及是否越过标准线。这个目标直接决定了后面的任务设计:单纯画个框不够,还要能测面积、测长度、统计密集度。

1.2 传统图像处理方案的局限在哪

早些年做这类检测,最常见的套路是阈值分割加形态学处理。先做固定阈值或者自适应阈值,把明显的暗区提取出来,再用开运算、闭运算把碎噪声滤掉,接着根据面积、圆度、长宽比、与坡口边缘的距离这些手工规则,判断每个区域是气孔、夹渣还是伪缺陷。

这套做法在实验室里很顺,放到生产现场就容易翻车。焊缝余高的灰度变化、底片本身的颗粒噪声、机械磨痕、甚至工件表面轻微锈迹,都可能被当成缺陷。为了压制误检,就得不停加规则:面积多大的不要、圆度不好不要、离坡口太远的不要。问题是规则本来就是基于有限样本拍脑袋定的,换一种焊材、换一条产线、调整一下电压电流,灰度特征变了,老规则就要重新标定。我见过一个项目组花了大量时间在写这种特征规则,最后换了个曝光参数,整套逻辑基本失效。

深度学习不太一样。它不需要人工预定义“像缺陷”的显式特征,而是通过大量标注样本,自己学习“看起来像气孔”“看起来像裂纹”这类语义表达。输入图像、输出缺陷区域,中间的特征提取过程是自动学出来的。对低对比度、形态多变、背景干扰强的焊缝X光图来说,这种方案比手工规则鲁棒得多。

1.3 深度学习方案的整体流程

基于深度学习的焊接缺陷检测,整个流程可以拆成五步。第一步是图像采集与数字化,典型来源是DR设备直接输出数字图像,或者把传统胶片底片扫描成数字图。第二步是预处理,包括灰度归一化、对比度增强、尺寸统一,必要时还要做ROI裁剪,把焊缝区域单独切出来。第三步是模型推理,这也是核心,常见做法是目标检测模型或者语义分割模型。第四步是后处理,对模型输出做阈值化、连通域分析、面积筛选,得到最终缺陷列表。第五步是数据组织与输出,把结果写进数据库、生成可视化标注图、推送告警信号给产线执行机构。

整个链路里,模型结构是最容易被讨论的部分,但真正决定系统能不能用的,往往是前后两端:数据是否覆盖工艺变化、后处理是否能输出业务需要的信息。下面我按这个顺序,把每个环节的具体做法和踩坑记录都展开。

2. 数据是第一个真正的门槛:缺陷数据的收集、标注与预处理

很多人以为项目核心是模型,实际做下来才会发现,数据才是最先卡住你的地方。焊接X光图像不像自然图像那么好找,开源数据本来就少,标注口径也乱,直接用很容易得出自欺欺人的结果。

2.1 数据来源:公开数据、现场数据与仿真数据

网上能下载到的焊接射线检测数据集,一般几百张到几千张,主要覆盖气孔、夹渣、裂纹这几个常见类别,未焊透和未熔合有时候标得不够细。这类公开数据可以拿来验证算法思路,证明“这条路能跑通”,但我个人不建议把它直接当作品验收指标。原因很简单:公开数据的成像条件、工件材质、工艺参数你没得选,模型在里面学到的灰度特征,未必适配你真实要检测的生产件。

最能说明问题的是自建数据。准备一批有缺陷和没缺陷的焊接试板,用DR设备拍片,再组织人员标注。成本主要花在焊样制作和设备占用上,但数据越贴近真实工艺,模型落地越顺利。项目早期如果拿不到大量现场数据,我建议至少先收集几十块不同工艺、不同板厚、不同曝光参数的小规模样本,确定检测任务可解,再考虑规模化采集。

仿真与增强是一条性价比很高的补充路径。把已有的真实缺陷图做旋转、裁剪、缩放、亮度扰动、叠加噪声,可以生成大量形态相似但不完全相同的样本。需要记住一点,增强只能模拟“同一类缺陷在不同拍摄条件下的样子”,不能创造真实世界里没有的缺陷结构。像裂纹这种细长低对比目标,增强时会牵涉到尺度和方向,操作起来要比气孔更谨慎。

2.2 标注标准:缺陷类型决定了任务粒度

焊接缺陷标注的颗粒度,直接决定了任务形式。如果只想判断“这一块有没有问题”,用矩形框标注就够了,目标检测模型训练起来快,标注成本也低。如果要做等级判定、面积统计、长度测量,矩形框是不够的,因为框里包含大量正常纹理,无法准确计算缺陷的物理尺寸。这时应该用像素级掩膜(Mask)标注,让模型预测每个像素是否属于缺陷。

不同缺陷的标注策略可以参考下面这张表:

缺陷类型典型外观标注建议
气孔圆形或近圆形暗斑,尺寸小可用分割掩膜,便于统计直径和数量
夹渣不规则块状,灰度不确定分割掩膜,重点统计面积与密集度
未焊透沿焊缝根部的连续线状暗条分割掩膜,关注连续性和长度
裂纹细长、低对比、端部尖锐分割掩膜,小目标需放大标注

实操时建议第一轮先用矩形框做快速验证,确定整体精度趋势没问题,再补充像素级标注做定版系统。因为像素级标注的成本是矩形框的好几倍,等到算法都跑不通的时候再投入,容易浪费标注资源。标注工作至少要两个人独立标一遍,再合并结果,统一边界规则。特别是灰度介于背景和缺陷之间的边缘区域,人和人的判断差异很大,不定好规则,模型学出来的边界就很飘。

2.3 预处理、增强与类不平衡处理

X光图像作为输入,一般先做灰度归一化,把像素范围缩放到0到1之间。要不要做CLAHE局部对比度增强,我的经验是“对大多数焊缝图像有正收益”,因为缺陷区域和背景灰度太接近,把局部对比度拉开,模型会更容易收敛。但增强强度要控制好,太强会突出融合噪声,反而增加误检。

增强策略不能无脑套自然图像那套。焊接X光图的翻转方向要看着办,如果产线工艺方向固定,水平翻转通常安全,垂直翻转要看你拍的是横焊还是立焊,焊道方向变了,缺陷形态的空间关系也会变。灰度扰动和对比度扰动是性价比最高的增强方式,一张原图可以模拟多种曝光条件。

类不平衡是焊接缺陷分割最大的敌人。一张512乘512的图上,背景区域经常占99%以上,如果只是普通二分类交叉熵损失,模型很容易学会“全部预测为背景”,因为这样损失已经很小。应对办法有三个方向:损失函数层面用加权交叉熵、Dice Loss或Focal Loss;数据层面把小缺陷区域裁剪出来放大,作为额外训练样本;评估层面单独统计缺陷像素上的召回率,不要被总体精度迷惑。

2.4 到底需要多少数据

这个问题没有标准答案,我只能给一个比较实的参考区间。几百张清晰标注图就能把模型训起来,足够写论文、做课程设计展示。但要达到产线可用的稳定检测,至少需要数千张图像,而且必须覆盖不同材质、不同板厚、不同曝光参数、不同工艺状态。我在模拟项目里做过对比:只用公开数据训练,检测率到一定程度就上不去了;每加入一批新工艺数据,召回率都会有可见的提升。数据量不是线性的,但“现场数据比公开数据重要”这一点始终成立。

3. 检测模型选型:目标检测与分割两条路线怎么选

数据问题理清楚之后,模型选型反而没那么纠结。当前主流路线就是两条:目标检测和语义分割。两者解决的问题有重叠,但输出能力和适用场景不同,选错了后面会很别扭。

3.1 目标检测路线:实时选YOLO,精度选两阶段

目标检测路线把每个缺陷当成一个目标,模型输出“缺陷类别+置信度+矩形框坐标”。单阶段模型以YOLO系列为代表,推理速度快,工程生态完善,适合在线场景,配合数据增强和小目标专门策略,能在焊缝X光图上取得不错效果。两阶段模型Faster R-CNN这类精度上限高一些,但推理速度吃亏,对产线实时检测不太友好。

用目标检测做焊缝缺陷时,最大的问题是小目标。气孔这一类缺陷只有几个像素大小,深度网络下采样几轮之后,特征图上的信息几乎丢光。单纯堆基础检测头是不够的,需要特征金字塔这类多尺度融合结构,并且训练时对包含小缺陷的区域做裁剪放大。另外,成串气孔落在同一个矩形框里时,框内同时包含多个独立缺陷,这对后续按点数评级非常不利。如果你知道业务上需要“数缺陷个数”,用目标检测框就得专门处理聚团缺陷的拆分问题。

3.2 分割路线:统计面积和形态时才不可替代

语义分割路线的代表是U-Net及其各种变体,模型给每个像素输出一个缺陷概率,再做阈值化得到掩膜。分割方案的优势在于:细长裂纹不会因为矩形框而引入大量背景噪声,召回更准确;每个缺陷的像素面积、等效直径、长宽比、密集度都可以从掩膜上直接统计出来;如果检测标准对缺陷数量和大小有明确的等级限制,分割输出几乎是必须的。

代价是标注成本高,训练和推理速度比轻量目标检测模型要慢一些。但焊缝场景通常没有自动驾驶那么极端,单张图推理就算慢个几百毫秒,很多工位的节拍也能接受。后处理方面,分割输出做连通域分析之后,每个连通域的位置、面积、包围盒全部能拿到,其实一点也不麻烦。

路线输出形式标注成本推理速度主要适用场景
目标检测类别+矩形框低高实时筛查、批量判片、缺陷定位
语义分割逐像素掩膜高中缺陷面积统计、长度测量、等级判定

3.3 我的选型思路与路线切换过程

我在模拟项目初期先用了目标检测方案做快速验证,因为标注成框便宜,可以先把算法可行性摸清楚。跑下来发现细长裂纹的召回是最弱的,矩形框本身对“细长低对比”目标很吃亏,而且后续统计面积时还要从框里把背景像素剥离出来,很别扭。后来切换到分割路线,用U-Net类模型直接预测像素掩膜,再通过连通域分析得到每个缺陷的坐标、面积和包围盒,整体流程反而更顺。

所以我的建议是:如果业务最终要的是“有没有缺陷、在哪个位置”,目标检测足够;如果业务要的是“缺陷面积多大、长度多长、数量多少、要不要返修”,直接上分割,不要绕路。如果实在两者都想要,也以分割为主线,分割结果里的连通域信息完全够你生成矩形框,不用单独再训一个检测模型。

4. Python核心实现:从数据集封装到训练评估全流程

选型确定之后,就到了动手写代码的阶段。下面我给出一个精简但完整的Python实现路径,覆盖数据加载、模型定义、损失函数、训练循环、推理后处理。代码以PyTorch、OpenCV、NumPy为主要依赖,突出核心逻辑,直接改路径就能跑通原型。

4.1 工程目录与依赖

项目结构建议按下面这样组织,简单清晰,后期扩展也方便:

weld_defect/ ├─ data/ │ ├─ images/ │ └─ masks/ ├─ models/ │ └─ unet_like.py ├─ utils/ │ ├─ dataset.py │ ├─ loss.py │ └─ postprocess.py ├─ train.py └─ infer.py

依赖方面,需要安装PyTorch、OpenCV、NumPy、tqdm。数据加载用OpenCV读图,训练用PyTorch训练,后处理用OpenCV的连通域函数,这套组合在焊接图像场景下非常顺手。

4.2 数据加载与标签处理

数据加载很容易踩坑的点是掩膜读取方式。分割掩膜必须用灰度图读取,然后在代码里转成0和1的二值标签。resize的时候,输入图像可以使用双线性插值,掩膜必须使用最近邻插值,否则标签边界会因为插值产生假像素,导致模型学出模糊边界。

import cv2 import numpy as np import torch from torch.utils.data import Dataset from pathlib import Path class WeldDefectDataset(Dataset): def __init__(self, img_dir, mask_dir, size=512, augment=False): self.img_dir = Path(img_dir) self.mask_dir = Path(mask_dir) self.size = size self.augment = augment self.names = [p.stem for p in self.img_dir.glob("*.png")] def __len__(self): return len(self.names) def __getitem__(self, idx): name = self.names[idx] img = cv2.imread(str(self.img_dir / f"{name}.png"), cv2.IMREAD_GRAYSCALE) mask = cv2.imread(str(self.mask_dir / f"{name}.png"), cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (self.size, self.size)) mask = cv2.resize(mask, (self.size, self.size), interpolation=cv2.INTER_NEAREST) img = img.astype(np.float32) / 255.0 img = np.expand_dims(img, axis=0) mask = (mask > 127).astype(np.float32) if self.augment: if np.random.rand() > 0.5: img = np.flip(img, axis=2).copy() mask = np.flip(mask, axis=1).copy() img *= 1.0 + 0.1 * np.random.randn() return torch.from_numpy(img).float(), torch.from_numpy(mask).float()

注意增强里翻转对象是二维的灰度图,通道维度上不做处理,因为输入只有单通道。

4.3 简化分割模型与损失函数

一个完整的U-Net对博客来说太长,我保留最核心的编码器、解码器、跳跃连接结构,做成一个简化版,足够说明原理。完整U-Net只是把这个结构加深、加宽,每个阶段多加一层卷积而已。

import torch import torch.nn as nn class ConvBlock(nn.Module): def __init__(self, in_c, out_c): super().__init__() self.block = nn.Sequential( nn.Conv2d(in_c, out_c, kernel_size=3, padding=1), nn.BatchNorm2d(out_c), nn.ReLU(inplace=True), nn.Conv2d(out_c, out_c, kernel_size=3, padding=1), nn.BatchNorm2d(out_c), nn.ReLU(inplace=True), ) def forward(self, x): return self.block(x) class UNetSimple(nn.Module): def __init__(self, in_ch=1, out_ch=1, base=32): super().__init__() self.enc1 = ConvBlock(in_ch, base) self.pool = nn.MaxPool2d(2) self.enc2 = ConvBlock(base, base * 2) self.enc3 = ConvBlock(base * 2, base * 4) self.up1 = nn.ConvTranspose2d(base * 4, base * 2, kernel_size=2, stride=2) self.dec1 = ConvBlock(base * 4, base * 2) self.up2 = nn.ConvTranspose2d(base * 2, base, kernel_size=2, stride=2) self.dec2 = ConvBlock(base * 2, base) self.out = nn.Conv2d(base, out_ch, kernel_size=1) def forward(self, x): e1 = self.enc1(x) e2 = self.enc2(self.pool(e1)) e3 = self.enc3(self.pool(e2)) d1 = torch.cat([self.up1(e3), e2], dim=1) d1 = self.dec1(d1) d2 = torch.cat([self.up2(d1), e1], dim=1) d2 = self.dec2(d2) return torch.sigmoid(self.out(d2))

损失函数直接用二元交叉熵加Dice Loss的组合,这是分割场景里非常稳定的搭配。Dice Loss专门处理缺陷小、背景大的不平衡问题。

class BCEDiceLoss(nn.Module): def __init__(self): super().__init__() def forward(self, pred, mask): bce = nn.functional.binary_cross_entropy(pred, mask) smooth = 1e-6 inter = (pred * mask).sum() dice = 1 - (2 * inter + smooth) / (pred.sum() + mask.sum() + smooth) return bce + dice

4.4 训练循环与验证

训练循环本身不复杂,重点是验证阶段要保存最优模型。焊接缺陷这种极不平衡任务,训练损失下降很快,但验证Dice涨得很慢,不要看前几个epoch损失低就高兴太早。

from torch.utils.data import DataLoader from tqdm import tqdm import torch.optim as optim dataset = WeldDefectDataset("data/images", "data/masks", size=512, augment=True) train_loader = DataLoader(dataset, batch_size=8, shuffle=True, num_workers=2) model = UNetSimple(in_ch=1, out_ch=1, base=32) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) optimizer = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-5) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50) loss_fn = BCEDiceLoss() for epoch in range(50): model.train() total_loss = 0.0 for img, mask in tqdm(train_loader): img, mask = img.to(device), mask.to(device) pred = model(img) loss = loss_fn(pred, mask) optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() scheduler.step() avg_loss = total_loss / len(train_loader) print(f"Epoch {epoch + 1}/50, Loss: {avg_loss:.4f}")

训练完成后,单独验证每个类别的Dice和召回率,不能只看平均数值。

4.5 推理、连通域分析与结果输出

推理阶段,关键是后处理。模型输出的是概率图,需要阈值化,然后做连通域分析,把每个缺陷当成一个独立对象处理。

def inference(model, img_path, size=512, threshold=0.5, min_area=100): model.eval() img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) original_size = img.shape[:2] img_resized = cv2.resize(img, (size, size)).astype(np.float32) / 255.0 img_tensor = torch.from_numpy(np.expand_dims(img_resized, axis=0)).unsqueeze(0).float() with torch.no_grad(): pred = model(img_tensor.to(device)) pred = pred.cpu().numpy()[0, 0] pred = cv2.resize(pred, original_size[::-1], interpolation=cv2.INTER_LINEAR) binary = (pred > threshold).astype(np.uint8) num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8) result_boxes = [] for i in range(1, num_labels): area = stats[i, cv2.CC_STAT_AREA] if area < min_area: continue x, y, w, h = stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP], stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_HEIGHT] result_boxes.append({"bbox": [x, y, w, h], "area_pixel": area, "center": centroids[i]}) return result_boxes

这段代码把预测概率图放大回原图尺寸,再做阈值化,保证输出标注和原图对齐。如果现场图非常大,建议先把焊缝ROI区域切出来,只在ROI上做推理,既能提速又能减少背景干扰。

5. 训练与调参中的真实踩坑记录

代码跑通只是第一步,真正折磨人的是训练和调参阶段的那些奇怪现象。下面这些坑是我在模拟项目里真实撞上的,每个都值得提前排雷。

5.1 小目标气孔漏检

第一次训练完,看验证集整体Dice还挺好看,但把预测可视化出来,发现漏掉的基本全是小气孔。这类缺陷只有十几个像素,几次下采样之后,特征贡献几乎消失。

解决思路要分两层。第一层是模型结构,使用特征金字塔式的多尺度融合,让浅层的高分辨率特征参与最终预测。第二层是训练数据,把小缺陷区域裁剪出来放大,单独作为一批样本加入训练。我在项目中做了个小工具,跑一个简单的Blob检测找出小缺陷的位置,然后把这些位置周围的128乘128窗口额外保存为增强样本,下一次训练时漏检明显减少。

5.2 背景占比过高导致的“全不检”

如果训练日志里损失还在下降,但测试集上预测掩膜全是黑色,那多半是模型学会了“全输出背景”。原因前面说过,缺陷像素占比太低,普通交叉熵损失被背景主导。

我的排查顺序是:先统计训练集中缺陷像素的平均占比,如果低于1%,直接把损失函数换成BCE加Dice;然后看阳性样本上的召回率,而不是只看全局Dice。换完损失函数后,模型才真正开始学习缺陷区域的特征。Focal Loss也可以,但参数要调,我就是先从Dice Loss入手,旁边保一个BCE项稳定训练,效果已经够用。

5.3 只看准确率会被骗

在焊接缺陷这种极度不平衡的场景里,准确率是一个毫无意义的指标。一张正常焊缝图,缺陷像素为0,模型只要输出全背景,准确率就是99%往上。但如果产线每天拍几千张图,哪怕有1%的正常图被误检成缺陷,每天的误报量也很夸张,现场人员很快就不信这个系统了。

对应的做法是每次迭代都记录三个数:缺陷召回率、背景误检率、以及按连通域统计后的“块级”误报数量。只在像素级别评估还不够,因为几个噪点连成的小区域,在像素级可能只影响0.1%的Dice,但形成连通域之后就是一个实实在在的误报缺陷块。所以必须把评估放在业务单位上,也就是“检出了几个真缺陷”“误报了几个假缺陷”。

5.4 推理输入尺寸与滑窗拼接的坑

训练时图像resize到512乘512,推理时如果直接拿原图resize,小目标信息会被压缩,性能明显下降;如果直接输入原图,模型会因为尺寸分布不一致而退化。我在项目里遇到过类似问题,把推理改成滑窗之后就解决了。

做法是把原图切成若干重叠Patch,每个Patch按训练时的尺寸输入模型,推理完再把概率图拼回原位。重叠区可以用平均融合,减少拼缝处的跳变。重叠比例不建议太大,10%到20%足够。这个方案对小气孔和细长裂纹都有好处,因为局部裁切能保留更多细节。代价是推理总耗时增加,但焊缝检测场景通常不是毫秒级,可接受。

6. 系统落地时可以继续做的几件事

原型能跑通之后,离真正的产线可部署还有距离。下面这几个方向是我在项目后期验证过的,按优先级从高到低排列。

6.1 ONNX导出与轻量化部署

训练完的PyTorch模型直接给产线用,在环境和效率上都不合适。先导出成ONNX格式,再交给运行时推理引擎处理,是常见做法。焊接缺陷这种低对比小目标场景,量化要谨慎。FP16基本无损,INT8的精度损失在细小裂纹上会比较明显。我建议先跑FP16保证效果,INT8要等场地样本稳定后再做。

dummy = torch.randn(1, 1, 512, 512, device=device) torch.onnx.export( model, dummy, "weld_defect.onnx", input_names=["input"], output_names=["output"], opset_version=11 ) print("ONNX export done.")

导出完成后,用ONNX Runtime加载测试一遍,确认输出和PyTorch版本一致,再做部署。

6.2 在线产线的触发、缓存与告警链路

在线检测场景和离线分析是两回事。产线来的是一段连续图像流,不能傻乎乎每帧都推理。通常的做法是:传感器检测到工件到位后,触发采集当前ROI图像,放进队列;GPU推理线程从队列取出图像处理,结果写入数据库并关联工件条码;如果判定为不合格,触发声光告警,同时保存可疑原图和模型结果图。

这个缓冲设计最关键的是不能丢图,尤其是缺陷图。我见过因为推理速度跟不上,简单地把队列里的旧图像丢掉的方案,最后等于把最容易出问题的那批数据全扔了。宁可在告警侧加延迟,也不要随意丢帧。

6.3 错分样本回注与增量训练

我的项目里最有价值的操作,是每跑完一批现场数据,就把漏检和误检的图像挑出来重新标注,再追加到训练集中做增量训练。增量训练时学习率要调低,大概是原来的十分之一,防止新样本把旧知识冲掉。每次追加的样本量控制在1000张左右,跑20到30个epoch,效果提升通常很稳定。

第一批错分样本回注带来的收益最大,越往后越趋近于饱和。跑完回注之后,最好单独保存一个固定评估集,全程不参与训练,确保每次模型变化不会产生回归。不要因为某次回注让旧场景召回率下降而不自知,这是我吃过亏后养成的习惯。

最后再分享一个体会:焊接缺陷检测这个项目,技术本身不是最大的难点,难的是你愿不愿意把时间花在数据、标准和反复验证上。模型结构大家都能看懂,最后拉开差距的往往是这些“脏活累活”。但恰恰是这些脏活,能把系统从“论文Demo”变成“车间里真正能用的工具”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 2:56:10

fcgi-2.4.0源码解析:C语言如何成就Nginx高性能FastCGI服务

简介&#xff1a;fcgi-2.4.0 是 FastCGI 协议的一个特定版本&#xff0c;面向需要在 nginx 中接入动态脚本处理能力的开发者和运维人员。这套压缩包共收录 127 个文件&#xff0c;整体仅 1.03 MB&#xff0c;文件类型涵盖 C 语言头文件与源码&#xff08;.c/.h&#xff09;、co…

作者头像 李华
网站建设 2026/10/19 2:55:08

提示词工程实战:CO-STAR、思维链与少样本提示的组合技巧

提示词工程这件事&#xff0c;最近几乎是个人都在聊&#xff0c;但你真让他们拿一个复杂任务出来&#xff0c;还是只会往输入框里甩一句“帮我写个方案”&#xff0c;然后抱怨模型答得平庸。问题的根源很简单——大部分人把提示词当“咒语”使&#xff0c;而不是当“工程”做。…

作者头像 李华
网站建设 2026/10/19 2:54:53

FFmpeg 编译支持 H265 的 RTMP 版本:从补丁到推流全解析

FFmpeg 编译支持 H265 的 RTMP 版本&#xff0c;这句话看起来像一道编译题&#xff0c;但在直播开发群里隔三差五就有人问一次。默认装好的 FFmpeg 影音能力再强&#xff0c;遇到“H.265 RTMP”这个组合就只能干瞪眼&#xff1a;libx265 编码器能出 HEVC 码流&#xff0c;RTMP…

作者头像 李华
网站建设 2026/10/19 2:54:16

S32K144库函数本质:硬件时序语义的确定性翻译器

简介&#xff1a;本资源是一套面向嵌入式初学者与S32K144芯片开发者的完整库函数实践工程&#xff0c;聚焦NXP S32K系列MCU底层驱动开发&#xff0c;覆盖ADC、CAN、时钟、Flash、FTM、GPIO、NVIC、PIT、UART、WDOG、PDB及IAP&#xff08;CAN Bootloader&#xff09;等核心外设模…

作者头像 李华
网站建设 2026/10/19 2:54:07

Spring多数据源事务管理:Seata AT与编程式方案实战

1. 项目概述&#xff1a;当一个Spring应用要同时操作多个数据库时&#xff0c;事务还能“兜得住”吗&#xff1f;你有没有遇到过这种场景&#xff1a;订单系统需要往MySQL写主订单&#xff0c;同时往PostgreSQL同步库存变更日志&#xff0c;还要调用Oracle里的价格策略服务——…

作者头像 李华
网站建设 2026/10/19 2:53:41

Git基础操作实战:深入理解分支、合并与撤销逻辑

Git基础操作这件事&#xff0c;说起来门槛很低&#xff0c;任何一个写过几行代码的人都能在半小时内学会git add、git commit、git push这三板斧。但真正在项目里跑上几个月&#xff0c;你会发现很多人其实一直站在坑里&#xff1a;提交信息写得乱七八糟&#xff0c;分支乱成一…

作者头像 李华