
💡💡💡本文摘要:基于YOLO26的玻璃缺陷检测系统,阐述了整个数据制作和训练可视化过程
用YOLO检测该夹层玻璃缺陷数据集的意义:

博主简介

AI小怪兽 | 计算机视觉布道者 | 视觉检测领域创新者
深耕计算机视觉与深度学习领域,专注于目标检测前沿技术的探索与突破。长期致力于YOLO系列算法的结构性创新、性能极限优化与工业级落地实践,旨在打通从学术研究到产业应用的最后一公里。
🚀 核心专长与技术创新
🏆 行业影响力与商业实践
💡 未来方向与使命
秉持 “让每一行代码都有温度” 的技术理念,未来将持续聚焦于实时检测、语义分割及工业缺陷检测的商业化闭环等核心方向。愿与业界同仁协同创新,共同推动技术边界,以坚实的技术能力赋能实体经济与行业变革。

论文:https://arxiv.org/pdf/2509.25164
摘要:本研究对Ultralytics YOLO26进行了全面分析,重点阐述了其关键架构改进及其在实时边缘目标检测中的性能基准测试。YOLO26于2025年9月发布,是YOLO系列最新、最先进的成员,专为在边缘及低功耗设备上实现高效、精确且易于部署的目标而构建。本文依次详述了YOLO26的架构创新,包括:移除了分布焦点损失(DFL);采用端到端的无NMS推理;集成了渐进损失(ProgLoss)与小目标感知标签分配(STAL);以及引入了用于稳定收敛的MuSGD优化器。除架构外,本研究将YOLO26定位为多任务框架,支持目标检测、实例分割、姿态/关键点估计、定向检测及分类。我们在NVIDIA Jetson Nano与Orin等边缘设备上呈现了YOLO26的性能基准测试,并将其结果与YOLOv8、YOLOv11、YOLOv12、YOLOv13及基于Transformer的检测器进行比较。本文进一步探讨了其实时部署路径、灵活的导出选项(ONNX、TensorRT、CoreML、TFLite)以及INT8/FP16量化技术。文章重点展示了YOLO26在机器人、制造业及物联网等领域的实际应用案例,以证明其跨行业适应性。最后,讨论了关于部署效率及更广泛影响的见解,并展望了YOLO26及YOLO系列的未来发展方向。
关键词:YOLO26;边缘人工智能;多任务目标检测;无NMS推理;小目标识别;YOLO(You Only Look Once);目标检测;MuSGD优化器

结构框图如下:


1)池化次数灵活性:YOLO11 的 3 次池化是硬编码的,要修改必须改源码;YOLO26 通过n参数可灵活调整(比如设为 2 次或 4 次),无需改核心逻辑。
2)Shortcut 设计:YOLO26 新增的残差连接能缓解深层网络的梯度消失问题,提升特征复用能力,而 YOLO11 无此设计。
3)激活函数控制:YOLO26 禁用 Conv1 的激活函数,让特征在池化前保持更 “原始” 的状态,是工程上对特征提取的优化。

源码位置:ultralytics/nn/modules/block.py
1)注意力机制的新增:YOLO26 的 C3k2 首次引入PSABlock(金字塔注意力模块),通过attn参数控制是否启用,这是两者最核心的功能差异 —— 启用后模块会先通过 Bottleneck 提取基础特征,再通过 PSABlock 增强关键区域的特征权重,提升小目标 / 复杂场景的检测效果。
2)分支逻辑的扩展:YOLO11 的分支仅受c3k控制,而 YOLO26 的分支逻辑优先级为attn > c3k,即只要attn=True,会优先启用注意力模块,忽略c3k的配置。

重复模块m (n次迭代):
┌─────────────────────────────────────────────────────────┐
│ │
│ 如果 attn=True: │
│ Sequential( │
│ Bottleneck(self.c, self.c), │ ←─ 先特征提取
│ PSABlock(self.c, attn_ratio=0.5, num_heads=...) │ ←─ 后注意力增强
│ ) │
│ │
│ 否则如果 c3k=True: │
│ C3k(self.c, self.c, 2) │ ←─ 同YOLOv11
│ │
│ 否则: │
│ Bottleneck(self.c, self.c) │ ←─ 同YOLOv11
│ │
└─────────────────────────────────────────────────────────┘YOLO26 C3k2代码:
源码位置:ultralytics/nn/modules/block.py
YOLO26引入了多项关键架构创新,使其区别于前几代YOLO模型。这些增强不仅提高了训练稳定性和推理效率,还从根本上重塑了实时边缘设备的部署流程。本节将详细描述YOLO26的四项主要贡献:(i)移除分布焦点损失(DFL),(ii)引入端到端无NMS推理,(iii)新颖的损失函数策略,包括渐进损失平衡(ProgLoss)和小目标感知标签分配(STAL),以及(iv)开发用于稳定高效收敛的MuSGD优化器。我们将详细讨论每一项架构增强,并通过对比分析突显其相对于YOLOv8、YOLOv11、YOLOv12和YOLOv13等早期YOLO版本的优势。

YOLO26最重要的架构简化之一是移除了分布焦点损失(DFL)模块(图3a),该模块曾存在于YOLOv8和YOLOv11等早期YOLO版本中。DFL最初旨在通过预测边界框坐标的概率分布来改进边界框回归,从而实现更精确的目标定位。虽然该策略在早期模型中展示了精度提升,但也带来了不小的计算开销和导出困难。在实践中,DFL在推理和模型导出期间需要专门处理,这使针对ONNX、CoreML、TensorRT或TFLite等硬件加速器的部署流程变得复杂。
源码位置:ultralytics/utils/loss.py
通过reg_max 设置为1,移除了分布焦点损失(DFL)
class BboxLoss(nn.Module):
"""Criterion class for computing training losses for bounding boxes."""
def __init__(self, reg_max: int = 16):
"""Initialize the BboxLoss module with regularization maximum and DFL settings."""
super().__init__()
self.dfl_loss = DFLoss(reg_max) if reg_max > 1 else NoneYOLO26从根本上重新设计了预测头,以直接产生非冗余的边界框预测,无需NMS。这种端到端设计不仅降低了推理复杂度,还消除了对手动调优阈值的依赖,从而简化了集成到生产系统的过程。对比基准测试表明,YOLO26实现了比YOLOv11和YOLOv12更快的推理速度,其中nano模型在CPU上的推理时间减少了高达43%。这使得YOLO26对于移动设备、无人机和嵌入式机器人平台特别有利,在这些平台上,毫秒级的延迟可能产生重大的操作影响。
源码位置:ultralytics/utils/nms.py
训练稳定性和小目标识别仍然是目标检测中持续存在的挑战。YOLO26通过整合两种新颖策略来解决这些问题:渐进损失平衡(ProgLoss)和小目标感知标签分配(STAL),如图(图3c)所示。
ProgLoss在训练期间动态调整不同损失分量的权重,确保模型不会过拟合于主导物体类别,同时防止在稀有或小类别上表现不佳。这种渐进式再平衡改善了泛化能力,并防止了训练后期的不稳定。另一方面,STAL明确优先为小目标分配标签,由于像素表示有限且易被遮挡,小目标尤其难以检测。ProgLoss和STAL共同为YOLO26在包含小目标或被遮挡目标的数据集(如COCO和无人机图像基准)上带来了显著的精度提升。
YOLO26的最后一项创新是引入了MuSGD优化器(图3d),它结合了随机梯度下降(SGD)的优势与最近提出的Muon优化器(一种受大型语言模型训练中使用的优化策略启发而发展的技术)。MuSGD利用SGD的鲁棒性和泛化能力,同时融入了来自Muon的自适应特性,能够在不同数据集上实现更快的收敛和更稳定的优化。
源码位置:ultralytics/optim/muon.py
本文伪代码:
# glass_defect_yolo26_pyside6.py
# 伪代码:基于 YOLO26 + PySide6 的玻璃缺陷检测系统
# 功能:数据集配置、模型训练、图片/文件夹检测、结果展示、缺陷统计、日志
# 依赖:pip install ultralytics pyside6 pyyaml opencv-python
import sys
from pathlib import Path
import yaml
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QLabel, QPushButton, QTextEdit,
QFileDialog, QVBoxLayout, QHBoxLayout, QListWidget, QMessageBox
)
from ultralytics import YOLO
# -------------------- 配置 --------------------
DATASET_DIR = Path("glass_defect_dataset")
DATA_YAML = DATASET_DIR / "data.yaml"
MODEL_WEIGHT = "yolo26n.pt" # 替换为实际 YOLO26 权重
PROJECT_DIR = "runs/glass_defect"
CLASS_NAMES = {
0: "scratch", # 划痕
1: "bubble", # 气泡
2: "crack", # 裂纹
3: "inclusion", # 夹杂
4: "stain", # 污渍
}
# -------------------- 数据集配置 --------------------
def ensure_data_yaml():
"""若 data.yaml 不存在则自动生成"""
if DATA_YAML.exists():
return
data = {
"path": str(DATASET_DIR.resolve()),
"train": "images/train",
"val": "images/val",
"names": CLASS_NAMES,
}
DATA_YAML.parent.mkdir(parents=True, exist_ok=True)
with open(DATA_YAML, "w", encoding="utf-8") as f:
yaml.safe_dump(data, f, allow_unicode=True)
# -------------------- 训练 --------------------
def train_yolo():
ensure_data_yaml()
model = YOLO(MODEL_WEIGHT)
model.train(
data=str(DATA_YAML),
epochs=100,
imgsz=640,
batch=16,
project=PROJECT_DIR,
name="train",
)
# -------------------- 检测线程 --------------------
class DetectionWorker(QThread):
log_signal = Signal(str)
result_signal = Signal(str, dict) # 结果图路径, 缺陷统计
def __init__(self, source, conf=0.25, iou=0.5):
super().__init__()
self.source = source
self.conf = conf
self.iou = iou
def run(self):
try:
self.log_signal.emit(f"加载模型: {MODEL_WEIGHT}")
model = YOLO(MODEL_WEIGHT)
self.log_signal.emit(f"开始检测: {self.source}")
results = model.predict(
source=self.source,
conf=self.conf,
iou=self.iou,
save=True,
project=PROJECT_DIR,
name="predict",
)
counts = {name: 0 for name in CLASS_NAMES.values()}
result_image = ""
for r in results:
for cls_id in r.boxes.cls:
name = model.names[int(cls_id)]
counts[name] = counts.get(name, 0) + 1
# 伪代码:取结果图路径
result_image = str(Path(r.save_dir) / Path(r.path).name)
self.result_signal.emit(result_image, counts)
self.log_signal.emit(f"检测完成,统计: {counts}")
except Exception as e:
self.log_signal.emit(f"检测失败: {e}")
# -------------------- 主界面 --------------------
class GlassDefectWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("基于 YOLO26 的玻璃缺陷检测系统")
self.resize(1200, 800)
self.current_source = None
self.worker = None
self.init_ui()
def init_ui(self):
central = QWidget()
self.setCentralWidget(central)
main_layout = QHBoxLayout(central)
# 左侧:按钮 + 统计 + 日志
left_layout = QVBoxLayout()
self.btn_select_image = QPushButton("选择图片")
self.btn_select_folder = QPushButton("选择文件夹")
self.btn_detect = QPushButton("开始检测")
self.btn_train = QPushButton("训练模型")
self.list_counts = QListWidget()
self.text_log = QTextEdit()
self.text_log.setReadOnly(True)
left_layout.addWidget(self.btn_select_image)
left_layout.addWidget(self.btn_select_folder)
left_layout.addWidget(self.btn_detect)
left_layout.addWidget(self.btn_train)
left_layout.addWidget(QLabel("缺陷统计"))
left_layout.addWidget(self.list_counts)
left_layout.addWidget(QLabel("运行日志"))
left_layout.addWidget(self.text_log)
# 右侧:原图 + 结果图
right_layout = QVBoxLayout()
self.label_original = QLabel("原图")
self.label_result = QLabel("检测结果")
for lab in (self.label_original, self.label_result):
lab.setAlignment(Qt.AlignCenter)
lab.setMinimumSize(400, 300)
lab.setStyleSheet("border: 1px solid gray;")
right_layout.addWidget(self.label_original)
right_layout.addWidget(self.label_result)
main_layout.addLayout(left_layout, 1)
main_layout.addLayout(right_layout, 2)
# 信号连接
self.btn_select_image.clicked.connect(self.select_image)
self.btn_select_folder.clicked.connect(self.select_folder)
self.btn_detect.clicked.connect(self.start_detect)
self.btn_train.clicked.connect(self.start_train)
def log(self, msg):
self.text_log.append(msg)
def select_image(self):
path, _ = QFileDialog.getOpenFileName(
self, "选择图片", "", "Images (*.jpg *.jpeg *.png *.bmp)"
)
if path:
self.current_source = path
self.show_image(path, self.label_original)
self.log(f"选择图片: {path}")
def select_folder(self):
path = QFileDialog.getExistingDirectory(self, "选择文件夹")
if path:
self.current_source = path
self.log(f"选择文件夹: {path}")
def show_image(self, path, label):
pix = QPixmap(path)
if not pix.isNull():
label.setPixmap(
pix.scaled(label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
)
def start_detect(self):
if not self.current_source:
QMessageBox.warning(self, "提示", "请先选择图片或文件夹")
return
self.list_counts.clear()
self.worker = DetectionWorker(self.current_source)
self.worker.log_signal.connect(self.log)
self.worker.result_signal.connect(self.on_result)
self.worker.start()
def on_result(self, image_path, counts):
if image_path and Path(image_path).exists():
self.show_image(image_path, self.label_result)
for name, num in counts.items():
self.list_counts.addItem(f"{name}: {num}")
def start_train(self):
# 伪代码:实际项目应放入线程,避免界面卡顿
self.log("开始训练 YOLO26 模型...")
train_yolo()
self.log("训练完成")
# -------------------- 入口 --------------------
if __name__ == "__main__":
app = QApplication(sys.argv)
window = GlassDefectWindow()
window.show()
sys.exit(app.exec())关于数据集 该数据集包含小型夹层玻璃样本的高分辨率图像,这些样本存在各种制造缺陷,例如气泡、分层和金属颗粒。所有图像均在受控光照条件下拍摄,以确保一致性和清晰度。
训练集381张,验证集122张

标签可视化分析

ps:建议填写绝对路径
path: 'D:/YOLO26/data/glass-defect' # dataset root dir
train: images/train
val: images/val
test: images/test
# Classes
names:
0: "DEFECT"import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
model = YOLO('ultralytics/cfg/models/26/yolo26.yaml')
model.train(data='data/glass-defect.yaml',
cache=False,
imgsz=640,
epochs=200,
batch=8,
close_mosaic=10,
device='0',
optimizer='SGD', # using SGD
project='runs/train',
name='exp',
)YOLO26 summary (fused): 122 layers, 2,375,031 parameters, 0 gradients, 5.3 GFLOPs
Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 4/4 2.8it/s 1.4s
all 122 138 0.963 0.95 0.982 0.778
预测结果:

受益于人工智能的崛起,Python语言几乎以压倒性优势在众多编程语言中异军突起,成为AI时代的首选语言。在很多情况下,我们想要以图形化方式将我们的人工智能算法打包提供给用户使用,这时候选择以python为主的GUI框架就非常合适了。
PySide是Qt公司的产品,PyQt是第三方公司的产品,二者用法基本相同,不过在使用协议上却有很大差别。PySide可以在LGPL协议下使用,PyQt则在GPL协议下使用。
PySide目前常见的有两个版本:PySide2和PySide6。PySide2由C++版的Qt5开发而来.,而PySide6对应的则是C++版的Qt6。从PySide6开始,PySide的命名也会与Qt的大版本号保持一致,不会再出现类似PySide2对应Qt5这种容易混淆的情况。
pip install --upgrade pip
pip install pyside6 -i https://mirror.baidu.com/pypi/simple基于PySide6开发GUI程序包含下面三个基本步骤:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。