
简介目标检测技术广泛应用于行人、车辆等常规场景但面对乐谱符号这类小目标密集、类别相似且形态特殊的任务时通用流程往往失效。乐谱识别难点不在模型结构而在于数据集构建与标注规范。本文从目标检测的基础原理出发探讨小目标检测与数据增强的技术价值结合YOLOv5训练自己的数据集这一高频需求深入讲解如何设计类别体系、构建训练样本、设置关键超参数并解决密集小目标漏检、NMS误过滤与类别混淆等工程问题。针对乐谱扫描图的高对比度、灰度化、符号尺寸极小等特点给出切实可行的训练配置与推理优化方案为从事细粒度目标检测或小众识别任务的开发者提供完整参考。 用YOLOv5训练自己的数据集网上教程一抓一大把但把训练对象换成乐谱情况马上就变了。乐谱里的符号又小又密类别多且长得像跟车牌、行人和安全帽完全不是一个套路。这篇文章我打算从头讲一遍我拿YOLOv5做乐谱符号检测的完整经历训练数据集怎么来、标注怎么定、类别怎么设计、训练参数怎么配、哪些坑必须绕开。这篇文章没有用任何公开的现成乐谱检测数据集整个流程都是自己从零搭的希望能给正在做类似小众检测任务的人一些参考。1. 乐谱识别这个任务为什么难在数据而不是模型1.1 符号检测和通用目标检测的差异到底在哪一个乐谱页面五线谱上排满各种符号放在自然图像目标检测的框架里看它有四个非常不友好的特征。第一是目标小。一个全音符符头在300dpi扫描图里通常只有三四十个像素见方符干可能只有几个像素宽。YOLOv5下采样32倍原始640分辨率输入下一个30像素见方的目标在特征图上只占不到1个像素。这种尺度分布和常见的行人、车辆检测完全不同后者目标动辄占据数百像素模型天然容易学到。第二是密度极高。一行钢琴谱可能有几十到上百个符号一页双系统谱表差不多有四行以上全图符号数量轻松超过300个密集程度接近人群检测的极限场景。而在人群检测里人的外观差异大可以用人体关键点辅助乐谱符号却是高度规则、重复出现的几何图形一旦漏检就是纯漏检没有上下文能补回来。第三是类别多且局部结构相似。光是音符就分符头、符干、符尾、符杠、附点、休止符加上各种谱号、调号、拍号、临时升降号、连线、延音线、小节线类别通常要设计到30类以上。不少符号长得像比如十六分音符的符杠和连音线在局部都呈细长弧形符头和附点在某些分辨率下就是一个实心圆和一个小点。第四是图像形态特殊。乐谱扫描图是典型的高对比二值化图没有自然图像的纹理和色彩信息。预训练模型在ImageNet上学到的颜色纹理特征在这里基本失效模型的初始特征提取能力要靠数据增强和微调重新激活。这四个差异决定了后面每一个环节都不能照搬通用目标检测的流程。而大部分做YOLOv5教程的人默认你检测的是行人、车辆、安全帽这种大目标、中等密度、外观差异明显的物体。一旦换成乐谱最大的瓶颈不是模型结构而是数据集本身公开数据集几乎没有自己标又不知道怎么标才合理。1.2 类别体系设计别让模型替你干语义推理的活类别设计是数据集构建的第一步也是后面所有标注工作量的决定性因素。我第一次做的时候把符头细分为全音符、二分音符、四分音符等实心空心、有无符干多种类型结果标注工作量翻了好几倍训练时这几类之间反而频繁混淆因为实心符头和二分音符符头在低分辨率下区别极小模型很难学。后来我把类别体系简化成几个核心大类音符组件符头、符干、符尾、符杠、附点、休止符、谱表元素高音谱号、低音谱号、中音谱号、谱线、小节线、调性标记升号、降号、还原号、拍号数字、连音线与延音线。符头统一为一个大类不再区分时值符干、符尾单独作为组件检测。这样设计的原因很简单检测模型的责任是这里有一个符号它是什么符号而这个符头是几分音符需要结合符干、符尾、相邻符号才能确定这是语义推理更适合放在后处理规则里做。让检测模型去学时值等于把一个需要上下文推理的问题硬塞给一个做局部模式匹配的网络效果差、数据需求还大。我实测下来简化后的类别体系让标注效率提升了接近一倍训练收敛速度也明显加快mAP反而还涨了几个点。核心思想就一句话检测输出的是符号的几何位置和粗略类别真正的乐谱语义还原音符时值、调号判断放在后处理里用规则做比硬让目标检测模型去学省事得多。2. 数据集构建图片来源、标注规范与质量检查2.1 图片来源公共版权乐谱、打谱渲染加真实扫描乐谱训练数据没有现成的公开基准数据集可用所以图片来源需要自己解决。我用了三种来源混搭效果比较理想。第一种是公共版权乐谱库。古典乐谱大多已经进入公共版权领域可以合法下载扫描件和PDF。我主要取钢琴独奏、声乐、弦乐四重奏这几个体裁因为符号类型覆盖全钢琴谱有大量双符干、大跨度连音线声乐谱有歌词和音符混排弦乐谱有各种弓法和休止符组合。下载时优先选清晰度高的扫描版避免用低分辨率缩略图直接当训练图那样标注出来的框边缘会非常模糊。第二种是自己用打谱软件渲染。我用MuseScore导出钢琴谱、重奏谱的图片这种方式的好处是干净、类别可控还能批量生成不同调号、拍号的组合。比如我想让升号、降号、还原号在数据集中分布均衡就用脚本自动生成24个大小调的不同谱例再导出成图片。这一招对解决类别不均衡问题非常有效因为自然谱例里升号出现频率远高于重升号完全靠真实谱收集会非常痛苦。第三种是自扫纸质谱和手机拍谱。这个来源用来模拟真实场景中的污损、倾斜、光照不均、纸张泛黄。我拿家里的旧钢琴谱册扫了二十来页再用手持拍摄了十几页虽然标注起来最麻烦但它提供了前两种来源没有的真实噪声对最终部署在手机拍照场景下的鲁棒性帮助很大。三种来源我按6:3:1的比例混用。实测下来只用干净渲染图训练的模型在真实拍照谱上会掉接近20个点的mAP50混入真实扫描和拍摄后掉点能控制在5个点以内。2.2 标注工具与标注规范框贴边、遮挡标整、跨行切段标注工具我用过LabelImg和X-AnyLabeling最终固定用LabelImg的VOC格式导出因为YOLOv5的标签转换脚本最成熟社区里资料也多。如果团队协作标注可以考虑Label Studio但它有一定学习成本单人标注用LabelImg效率更高。标注中最关键的规范我认为有四条一定要在开工前写清楚不然不同标注者会标出各种风格。第一条框要贴边。乐谱符号是细长形比如符干宽度只有两三个像素框稍微大一点IoU计算就失真严重训练时正负样本区分度直接变差。我要求每个框标注时把视图放到最大尽量贴合符号边缘。第二条遮挡符号标完整还是标可见部分我的经验是标完整。两个符号重叠时比如符头和相邻的连线交叉虽然视觉上符头被遮住了一小块但从乐谱语义看它仍然是一个完整的符头。只标可见部分会让模型学到缺一块的符头推理时碰到完整符号反而不敢置信。第三条跨行连音线怎么标。如果连音线跨了整个谱表系统完整标注会让目标的高宽比变得极端模型很难学。我建议按谱表行切段标注让模型学习局部弧形后处理阶段再把相邻的连音线段拼接成完整语义。第四条谱线不要单独标。五线谱本身是背景干扰单独标注会产生大量重复框而且严重增加类别不平衡。模型需要学会的是在谱线背景下检测符号而不是检测谱线本身。标注规范一定要写成文档最好配上截图示例因为标注是纯体力活标准不统一的话后面清洗会耗费几倍的时间。2.3 清洗与可视化检查不看不行的质检环节标注完一定要清洗这一步不能省。我第一次标完就直接开训结果模型在验证集上的指标虚高一测真实图片就崩最后查下来是训练集里混了不少漏标的图模型把漏标区域的背景当成了负样本学到一堆错误模式。清洗我分三步走。第一步写脚本统计每张图的框数、目标面积分布、宽高比分布把异常值挑出来。比如一张钢琴谱标注框数是0或只有个位数基本可以断定漏标严重直接删掉或者回去补标。目标面积小于16像素的框说明要么标错了要么这个目标本身小到无法学习建议删掉。第二步是随机抽样可视化检查。把标签画回原图按图片ID随机抽20%逐张过一遍。我踩过的一个典型坑就是标注阶段把附点漏了不少符干的框有一批整体偏大这些单看统计数字很难发现但画回图上一眼就能看出来。第三步是类别数量核验。跑一个脚本统计每个类别的实例总数对数量远低于平均值的类别做重点排查。比如发现还原号只有几十个远远不够训练就回到渲染那一步专门补齐。类别不均衡在YOLOv5里虽然不像分类任务那么致命但极端情况下还是会拉低个别类别的AP值。清洗完成后数据集里大概会删掉5%~8%的残次图这些都是纯浪费时间的东西早删早省心。我最终的数据集规模是1200张训练图、150张验证图、150张测试图每张图平均190个目标总标注框数约28万个。3. 模型与输入的适配从YOLOv5选型到灰度单通道3.1 模型规模从n到m6的实测结论YOLOv5系列从n到x参数量和计算量差别很大。在乐谱这种小目标密集场景下我用yolov5s和yolov5m分别做过对比实验。用约600张训练图时yolov5s的mAP50能到0.85左右yolov5m能到0.90以上。如果目标是快速验证流程、跑通整个链路n或s完全够但想真正能上线应用建议m起步。我试过yolov5l精度提升不到1个点但训练时间长了快一倍性价比很低。这里还有一个针对小目标的重要选择P2输出层。YOLOv5的P2层保留了更大分辨率的特征图对小目标检测有明显帮助。我最终用的是yolov5m6它的输出层包含P2实测小目标召回率比yolov5m高约3~5个百分点。代价是显存占用和推理时间都增加训练和推理时都得更小心地控制batch size。所以选型建议是验证阶段用yolov5s跑通流程正式训练用yolov5m6或同等带P2层的模型。具体看你的显存和推理延迟要求如果部署端是嵌入式设备那yolov5n配合1280输入可能是更现实的选择。3.2 输入分辨率640在乐谱场景下是真的不够输入分辨率是乐谱识别里最关键的参数之一没有之一。我把同一个模型用不同输入分辨率训练和验证过对比结果非常直观。640输入小符号大量漏检符头、附点这类目标尤其惨。一张典型钢琴谱的召回率只能到70%左右密密麻麻的十六分音符段落几乎全军覆没。1024输入可接受符头召回率明显回升但符尾和符杠仍会有部分漏检尤其是那些只有两三个像素宽的长条目标。1280输入稳定了小目标召回率和mAP都到了一个合理水平同时显存占用还在可控范围内。1536以上精度提升有限但训练和推理时间成倍增加性价比极低。如果显存有限上不了1280可以把模型降为yolov5s或者yolov5n保持1280输入。注意不要用拉伸resize要保留YOLOv5自带的letterbox逻辑保持图像宽高比不变避免符号长宽比变形。3.3 灰度图与单通道不用改代码的省显存技巧乐谱本身就是灰度图很多人会问能不能直接用单通道输入省显存。YOLOv5代码里的输入层定义是3通道严格意义上的单通道输入需要改模型定义比较麻烦。但我实测发现把灰度图复制成3通道三通道数值相同喂进去效果和真正单通道几乎一样。原因在于卷积核对三通道同时做加权求和三通道相同时等价于对灰度值做一组组合卷积网络完全能学到等价的特征表达。所以不用改任何代码只需要在数据加载时用cv2.imread(path, 0)读成灰度图再转换成3通道或者直接用PIL.Image.open(path).convert(RGB)。这里有一个必须注意的坑默认的YOLOv5训练配置里开了hsv增强也就是hsv_h、hsv_s、hsv_v都不为0。灰度图做hsv扰动会产生不自然的色块比如灰背景变成紫红色这对模型是纯干扰。我在乐谱训练中把这些参数全部设为0实测能减少误检。另外和灰度紧密相关的还有对比度增强。乐谱符号是黑色、背景是白色如果扫描件对比度低符号边缘会模糊检测效果会明显下降。我会在数据增强阶段做随机对比度调整模拟不同质量的扫描件这个后面单独讲。4. 训练配置与超参数调优乐谱场景下的取舍4.1 标注格式转换与数据集划分一个公式和一个原则标注完成后先把LabelImg导出的VOC格式XML转成YOLOv5需要的TXT格式。核心转换公式如下x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height注意YOLOv5的标签格式要求归一化坐标保留6位小数即可。转换完一定要做一步反向验证把TXT里的坐标换算回像素画回原图和原标注对比。这一步能抓出百分之九十的格式转换错误比如宽高位置写反、坐标超出图像边界等。数据集划分我按8:1:1但有一个原则比比例更重要同一首曲子的不同页面不能一部分进训练集、一部分进测试集。同一首曲子的排版风格、字体、扫描条件高度相似如果把同一首曲子的不同页拆开验证集和测试集的难度会虚低评估结果虚高最后部署到新谱子上就翻车。正确做法是按曲目ID划分确保测试集里的曲子在训练阶段完全没见过。4.2 anchor与关键超参数关水平翻转关颜色增强开小幅旋转YOLOv5默认的anchor是在COCO数据集上聚类出来的和乐谱符号的尺寸分布完全不匹配。训练时YOLOv5的autoanchor机制会自动重新聚类但前提是标注框要准确、边界要真实。如果你的框普遍偏大聚类出来的anchor也会偏大小目标训练效果会打折扣所以标注清洗一定要在前面做扎实。几个和乐谱场景强相关的超参数我直接给出结论fliplr必须设为0。音乐符号的语义和方向强相关符干在左还是在右、符尾朝上还是朝下决定了音符的时值和音高。水平翻转等于把标签改成另一个语义模型学到的特征会产生矛盾训练时loss会降不下去。hsv_h、hsv_s、hsv_v全部设为0。原因前面说过灰度图不容许颜色扰动。degrees可以设为5左右。真实扫描件多少有点倾斜小幅旋转能提升鲁棒性。但不要超过10度因为低音谱号倒过来就不再是低音谱号了旋转过大会破坏符号的方向语义。mosaic建议从默认的1.0降到0.5~0.8。Mosaic确实能提升小目标检测能力但乐谱本身就极其稠密四张图拼接后符号被切得七零八落反而给标注框带来大量残缺正样本。我调低后小目标召回率没有下降训练稳定性反而更好了。4.3 显存受限时的训练技巧AMP和梯度累积1280输入加yolov5m6显存压力不小。我用的3060 12G显卡batch size只能开到4左右。如果显存不够有两条路可以走。一条是开启AMP混合精度。YOLOv5默认在训练时使用AMP显存占用能降低约30%同时训练速度还更快。如果显存仍然不够可以在超参数里进一步缩小batch size但要注意过小的batch size会导致BN统计不稳定影响收敛。另一条是梯度累积。YOLOv5的train.py里通过--batch-size参数指定名义batch size配合accumulate机制实现梯度累积。比如显存只够batch4但你想等价于batch16的效果就在配置里设置累积4步再更新一次权重。实测在乐谱任务上梯度累积到等效batch16或32训练稳定性明显提升mAP也比小batch直接训高1~2个点。如果显存实在太小上不了1280输入把输入降到1024、模型降到s是更务实的方案。先跑通再做增量优化比一开始就卡在硬件上要高效得多。5. 踩坑实录密集小目标漏检与符号误判的排查过程5.1 小目标漏检的根因定位与解决方案训练完第一版模型后我遇到的第一个大问题整体mAP看着还行但一检验发现符头、附点、符尾大量漏检。这不是个别现象而是系统性的小目标漏检。排查过程是这样的。我先写了一个分析脚本把所有GT框按像素面积分档小于32像素、32~64像素、65~128像素、大于128像素分别统计每档的召回率。结果非常清晰符头和附点几乎全部落在小于32像素这一档而模型在这个档位的召回率不到40%。65像素以上的目标召回率都在85%以上。根因有两层。第一层是模型本身的锚框设计和特征图分辨率对小目标不友好YOLOv5的下采样倍数太大小目标在深层特征图上几乎消失。第二层是训练数据里小目标的占比不够虽然乐谱符号整体偏小但不同符号间的尺寸差异也很大符杆的标注框面积是符头的十几倍模型在训练时天然偏向学习大目标。解决方案我用了组合拳。第一步把输入分辨率从640提到1280这一步效果最明显小目标召回率直接提升15个百分点左右。第二步换用带P2输出层的yolov5m6又提升了3~5个点。第三步在数据加载时对小目标做在线裁剪增强随机裁取小目标密集的区域放大后作为单独的训练样本。三步下来小于32像素档的召回率从40%提升到75%以上这个数字基本够用了。5.2 密集符号被NMS误过滤一个很容易忽略的坑漏检问题解决后训练中期又冒出一个诡异的现象模型训练时mAP一直在涨但推理结果里密集排列的十六分音符段落总是缺几个。一开始我以为是漏检后来发现不是。排查过程很关键。我把推理时的NMS关掉直接看模型原始输出结果发现所有十六分音符其实都被检出来了置信度也不低。问题出在NMS上一排十六分音符的符头、符干、符尾挨得非常近检测框之间的IoU很容易超过0.5而YOLOv5默认的NMS IoU阈值是0.45于是后检出来的框被前一个高置信度框抑制掉了。我做了两组实验验证。第一组把NMS的IoU阈值从0.45调到0.3密集音符段落的召回率提升了约7个百分点。第二组改用Soft-NMS对重叠框做置信度衰减而不是直接删除效果更好一些召回率提升接近10个百分点但会有少量重复框需要配合最低置信度阈值过滤。最终的部署方案是推理端用Soft-NMS置信度阈值设为0.25IoU阈值设为0.3。这么做能让密集场景下不漏检同时把重复框控制在可接受范围内。这个坑在通用目标检测中不太明显因为行人、车辆之间不会有那么规则的紧密排列但在乐谱这种符号密集场景里NMS配置几乎是决定成败的细节。5.3 类别混淆与背景误检的处理思路漏检解决之后误检开始变得显眼。训练后期我去看那些预测错误的图发现主要有三类问题。第一类符干被误检成符尾。符干和符尾都是细长条局部结构高度相似区别只在于方向和与符头的相对位置。模型在缺乏上下文时会混淆。我的处理办法是数据层面增加符尾的样本数量同时用copy-paste增强把符尾单独复制到不同位置让它学会区分。最终符尾的AP从0.72提升到0.86。第二类连音线的碎片被误检成符杠。这个本质上是类别定义重叠造成的。符杠和连音线在视觉上都是横卧的弧线如果训练数据里两者的形状分布过于接近模型确实很难分。我最终做了一个务实决定把符杠和连音线合并成一个横线类不细分为两类具体语义放到后处理里根据位置和长度判断。这个改动让这一类别的AP直接提升了6个点。第三类背景污渍被误检成符号。扫描件上的墨点、折痕、印刷噪点模型偶尔会当成符头。我补了一批纯背景区域的负样本图也就是故意裁取没有标注目标的空白谱表区域放进训练集让模型学到没东西就不输出。这个操作立竿见影误检数量直接减半。处理这些问题的核心思路是先看混淆矩阵找到真正互相干扰的类别对再做针对性调整不要盲目堆数据。6. 增强、评估与后续工程化从训练集到可用模型6.1 针对乐谱的数据增强不只靠YOLOv5自带的那些YOLOv5自带的增强策略对自然图像很有效但对乐谱这种特殊图像需要专门加一些领域相关的增强。我加的几个增强实测都对最终模型有正面帮助。随机膨胀腐蚀。用形态学操作模拟打印质量差、墨迹扩散的场景。一张干净的乐谱经过膨胀处理后符号边缘变粗变模糊这很接近老式印刷谱的实际效果。我按20%的概率对训练图做这个增强模型对低质量扫描件的鲁棒性明显提升。随机加噪声。高斯噪声和椒盐噪声各来一点模拟扫描仪的噪点。这个增强对防止过拟合很有帮助尤其是训练集里干净渲染图占比较高的时候。随机擦除局部。把图像中的随机区域用白色方块覆盖模拟遮挡。对密集场景来说这能让模型学会用上下文猜出被遮挡的符号不至于一遇遮挡就整个丢失。随机移除部分谱线。五线谱的谱线是符号的位置参照系但如果模型过度依赖谱线位置去找符号碰到谱线模糊的扫描件就不行了。我按一定概率随机擦掉几条谱线迫使模型直接学符号本身的形状特征。小角度旋转。控制在正负5度模拟输入图像倾斜又不破坏符号的方向语义。这些增强不是越多越好我建议每加一个就跑一次消融实验确认有效再保留。我最终保留的增强组合在验证集上比只用YOLOv5默认增强高5个点左右的mAP50。6.2 评估指标怎么读别只盯着mAP50训练完模型如果只看mAP50这一个数字很容易被表面的高分数迷惑。我在乐谱识别项目里的评估习惯是至少看四张图。第一张是各类别AP50的柱状图。乐谱符号类别多数字上看不到的问题在这里会暴露出来。比如某次训练的mAP50到了0.92但点开后发现附点类AP只有0.6这就是潜在的大坑。附点在乐谱里非常常见漏检率高了整个识别结果就没法用。第二张是置信度分布图。如果大量预测框的置信度集中在0.3到0.5之间说明类别可分性差模型对这到底是不是符号信心不足。合理的分布应该是双峰要么很高置信度接近1要么很低接近0中间段越少越好。第三张是混淆矩阵。这是找类别互相干扰最直接的工具。我前面提到的符干和符尾混淆、符杠和连音线混淆都是先看混淆矩阵才定位到的。第四张是页面级可视化。随机抽20张测试集外的新谱子把预测框画出来人眼过一遍。这一步虽然原始但能发现指标上看不出来的问题比如一行谱子某个区域全部漏检这往往和排版方式或光照条件有关。数值指标只能告诉你哪里不对可视化才能告诉你为什么不对。6.3 从训练完到能用导出、切图推理与后处理训练完成后模型要真正用起来还有几步工程化工作要做。导出方面YOLOv5提供了export.py脚本可以导出ONNX、TensorRT等格式。导出时的输入分辨率要和训练时保持一致我用的是1280导出的TensorRT引擎能以大约30ms每页的速度在嵌入式设备上跑整页推理。实际部署阶段有一个关键问题整页乐谱图像通常比1280大得多比如扫描件是2000x3000像素。直接resize到1280会把小符号缩得更小等于把数据集阶段的努力全浪费了。我的做法是做切图推理把大图按1280x1280的窗口滑窗切块窗口之间重叠100像素每块独立推理最后把检测结果坐标映射回原图。后处理阶段这是乐谱识别真正变成应用的关键。我把检测结果按谱表行分组再按x坐标排序把同一行的符头、符干、符尾、升降号、拍号等组合成完整的音符元素。这一步需要用到谱线的纵坐标位置、小节线的横坐标边界属于典型的规则式后处理也是把检测模型输出转化为可用乐谱数据必不可少的一环。整个项目跑下来我最大的体会是不要把检测模型当最终产品。乐谱识别正确的做法是检测加后处理规则模型只要稳定输出符号位置和粗略类别剩下的时值判断、调号识别、音符组合都交给规则去做。这样你的数据集压力会小很多模型精度要求也降下来了。同时数据问题永远优先于模型问题先把标注规范定好、把类别体系想清楚后面训练和调优才能真正顺起来。这就是我在这个项目里踩坑踩出来的经验希望对正在做类似小众检测任务的人有帮助。本文还有配套的精品资源点击获取