简介:这份资源是一套基于Roboflow平台完成标注的YOLOv8牙科解剖数据集,面向机器学习、计算机视觉方向的学习者与研究者,尤其适合需要标准图像分类或目标检测数据的模型训练场景。数据集按训练集、验证集、测试集三部分组织,分别包含505幅、112幅与107幅专业标注的牙齿图像,配套的data.yaml配置文件完整记录了类别名称定义与系统路径参数,可为模型训练与评估提供直接的元数据支持。压缩包共1797个文件,总大小约43.53MB,以jpg图像与txt标签为主要组成,同时包含模型参数pt文件、训练日志log文件、Jupyter Notebook脚本及相关配置文件,目录结构清晰便于按阶段检索与使用。目前已有91人浏览学习,适合用于YOLOv8模型的训练实践、牙科图像识别实验及数据集规范化处理的参考。
1. 牙科解剖数据集到底是什么:一份能直接喂给YOLOv8的标注牙片
做口腔AI辅助诊断的人,第一道坎往往不是模型,而是数据。手头要是没有一批带标注的牙科影像,YOLOv8牙科解剖数据集就是用来补上这个缺口的:它给你的是已经在Roboflow上标注完成的牙齿图像,以及配套的YOLO标签文件。这里的标签文件不是随手写的坐标,而是按YOLOv8目标检测格式整理好的txt,也就是模型真正拿来训练的食物。它能解决什么问题:省掉从零标注的时间,同时给你一个标准模板,理解牙科图像的标签该怎么画、怎么导出、怎么检查。适合谁用:想入门口腔影像目标检测的研究生、快速验证算法的医疗AI团队,还有准备上手Roboflow标注工作流的工程师。
2. 拆解数据集里的标签文件:YOLO格式的txt到底在描述什么
从Roboflow导出的牙科解剖数据集,打开压缩包后看到的通常是两层结构:images目录放牙片原图,labels目录放同名标签文件。这套结构与YOLOv8默认的文件读取逻辑一一对应。不少人拿到数据集的第一步是直接扔进训练脚本,结果翻车后就怀疑模型有问题。实际上,先花十分钟把标签文件拆开看一眼,能省下后面几天的排查时间。
2.1 牙科图像的三个特性,决定了数据集的标注尺度
牙科影像和自然图像差别很大,这些差别直接影响你怎么判断一份标签文件是否合格。第一,绝大多数牙片是灰度图像,牙釉质、牙本质、牙槽骨在X光片上灰度接近,肉眼都不一定能分清边界,检测模型只能靠边缘纹理去拟合,因此标注框的边界质量变得极其重要。第二,牙齿目标小且密集,一张全景片宽高通常在2000像素上下,单颗牙齿占的面积可能只有几十像素。这和COCO数据集里那些占图三分之一的物体完全不同,YOLOv8的检测头虽然对小目标有不错的敏感度,但对标签坐标的精度要求也高得多。第三,解剖结构固定但遮挡多,智齿横生、牙根重叠、充填体高亮,都会让同一颗牙在不同片子上形态不一致。数据集的标注要尽量贴近真实解剖边界,而不是像通用物体检测那样习惯性地用一个大矩形把物体和背景一起包进去。
这三个特性决定了后续几乎所有参数选择:为什么imgsz不能设成640,为什么增强选项要谨慎开启,为什么mAP50-95难拉高。理解了它们,你拿到任何牙科数据集时都能快速判断它能不能用。
2.2 标签文件内容:归一化坐标与类别id
YOLOv8读标签不认XML也不认JSON,就是纯文本txt,每行描述一个目标实例。行格式是五个空格分隔的数值:
class_id x_center y_center width height其中x_center、y_center是目标框中心点相对图片宽高的比例,width、height是目标框宽高相对图片宽高的比例,取值都在0到1之间。类别id从0开始,对应data.yaml里names列表的索引。Roboflow导出时有时会给标签文件名加后缀,常见的是原图名后面带.rf,比如001_jpg.rf.txt。
下面这段代码把单个标签文件的内容解析出来,方便你直接观察:
# 读取一个YOLO格式标签文件并解析每条目标 label_path = "labels/train/001_jpg.rf.txt" with open(label_path, "r") as f: lines = f.readlines() for line in lines: cls_id, xc, yc, w, h = line.strip().split() print( f"class={cls_id}, " f"center=({float(xc):.3f}, {float(yc):.3f}), " f"size=({float(w):.3f}, {float(h):.3f})" )这段代码的核心逻辑是split把一行拆成五个值,前两个坐标合成中心点,后两个合成长宽。输出的size能直接反映目标大小:如果w和h都在0.05以下,说明这颗牙在整张图中占比很小,训练时imgsz不够大就很容易漏检。另外要特别留意cls_id的范围,如果标签里出现了大于names长度的id,训练时会直接报错或静默跳过,这类问题用下一节的体检脚本就能揪出来。
2.3 用Python快速检查一套标签文件是否可训练
Roboflow导出的数据集质量整体稳定,但因为预处理选项组合太多,偶尔会出现标签坐标越界、空标签、类别id错位这类问题。我习惯在训练前先跑一遍体检脚本,把明显有问题的标签文件全部列出来:
# 快速体检:检测空标签、列数错误、非数字、越界坐标 import glob issues = [] for txt_path in glob.glob("labels/**/*.txt", recursive=True): lines = [l for l in open(txt_path).read().splitlines() if l.strip()] if not lines: issues.append((txt_path, "EMPTY")) continue for line in lines: parts = line.split() if len(parts) != 5: issues.append((txt_path, f"BAD_COLUMNS: {parts}")) continue try: cls_id, xc, yc, w, h = map(float, parts) except ValueError: issues.append((txt_path, f"NOT_NUMERIC: {parts}")) continue # 坐标在[0,1]内,宽高大于0且不超过1 if not (0 <= xc <= 1 and 0 <= yc <= 1 and 0 < w <= 1 and 0 < h <= 1): issues.append((txt_path, f"OUT_OF_RANGE: {parts}")) for p, reason in issues[:20]: print(p, reason) print("total issues:", len(issues))体检脚本的逻辑不复杂,重点看三个位置:空标签文件会让YOLOv8在读取时跳过对应图片,等于白少一个训练样本;坐标越界通常意味着Roboflow导出时resize设置和标签换算对不上;列数不对则说明文件被污染过。这里有个血泪经验:我曾经拿到一套看起来正常的导出包,训练到第40轮才发现data.yaml里的names顺序和标签id完全错位,模型等于把所有牙齿都学成了同一类。所以标签文件的健康检查必须和data.yaml的names顺序一起验证,缺一不可。
3. 用Roboflow从零标注牙片到导出:完整路径与三个关键设置
与其到处找现成的牙科数据集,不如把Roboflow的标注流程完整跑一遍。牙科数据的价值不只在一次下载,你总会不断拿到新牙片,需要按同一套标准继续补标注。Roboflow的价值在于把图像存储、在线标注、版本管理和格式导出串成一条流水线,数据标注这件事从单机手工活变成了可持续迭代的流程。
3.1 在Roboflow中创建项目并导入牙片
在Roboflow里新建项目时,Project Type选Object Detection,这一个选项决定了标注工具、导出格式和训练集划分都会按目标检测的标准走。上传牙片支持拖拽整个文件夹,但我不建议一次传上千张,页面会明显变卡,而且标注进度不好追踪。我一般按患者或拍摄批次组织,每批50张左右上传。
牙科影像经常是DICOM格式,导入前先转成PNG或JPG会更省事,Roboflow的图像处理管线对常规图片格式兼容性最好。文件命名建议带上编号规则,比如patient_001_left.png,这样导出后标签文件不会一团乱。如果原始文件名是一串相机生成的随机码,后续要从错误样本反查原始片子时会非常痛苦。
3.2 类别设计与标注框的粒度:别只标“牙齿”
类别设计是牙科解剖数据集里最容易被低估的环节。如果只标一个tooth类,模型确实能框出所有牙齿,但对辅助诊断没有实际意义。我一般按解剖结构起步:crown(牙冠)、root(牙根)、caries(龋坏)、pulp(牙髓腔)、implant(种植体),五个类别覆盖大多数场景。如果目标是龋病筛查,caries必须单独拆出来,它才是医生真正关心的病灶区域。
按FDI牙位编号是另一个极端,一次建32个类别,数据量不够时模型根本学不动,标注工作量也会翻好几倍。画框粒度要定统一规则:牙冠和牙根交界处以牙颈部为界,线以上算crown,线以下算root,两个标注员才不会产生分歧。龋坏区域形态不规则,矩形框要尽量紧贴病灶,宁可框略小也不要拉一个大方框把健康牙体包进去。Roboflow的自动标注辅助功能在牙齿这种低对比度灰度图上效果一般,最多智能框出大致牙齿区域,边界和病灶点需要全部人工复核。
3.3 导出YOLOv8格式:preprocessing、augmentation与下载
标注完一批图片后,在Versions页点Generate New Version,这里有两个关键设置直接影响训练效果。第一是Preprocessing里的Resize选项,Roboflow导出时会按你设定的尺寸实际缩放图片,并同步换算标签坐标。选Resize to 640x640导出的包确实能用,但牙科小目标细节已经被抹掉了。我建议选No Resize,或者Resize to 1280 x 1280,把分辨率选择的主动权留给YOLOv8训练时的imgsz参数。
第二是Augmentation里的增强选项,Roboflow默认给你勾了一堆翻转、旋转、HSV扰动。牙科图像全部关掉,一个都别开。水平翻转会把患者的左侧磨牙变成右侧,如果类别里有牙位编号,语义直接错乱;旋转会让解剖方向失真;HSV扰动对灰度牙片完全没有意义。导出格式选YOLOv8 PyTorch,下载回来的zip就是完整的训练包。
3.4 拿到zip后先做目录体检
解压后先看一眼目录结构,Roboflow通常会帮你划好train/valid/test:
unzip dental_dataset.zip -d dataset find dataset -maxdepth 3 -type d正常结构下,dataset里会有train、valid、test三个目录(test有时没有),每个目录下各含images和labels两个子目录,根目录放一个data.yaml。此时要检查一个细节:标签文件名是否带.rf后缀。如果带,需要批量改成与图片完全同名:
# 批量把 .rf.txt 后缀改掉,保持与图片文件名一致 for f in labels/train/*.rf.txt; do mv "$f" "${f%.rf.txt}.txt"; doneYOLOv8约定图片001.jpg对应标签001.txt,且标签文件放在images同级的labels目录下,ultralytics会自动做这个映射。改完后缀再跑一遍第2章的体检脚本,确认没有问题再进训练阶段。这个步骤没人会替你检查,漏掉任何一个,训练时都会静默丢样本。
4. 跑通YOLOv8训练:数据配置、命令与五个必调参数
环境配置好之后,正式进入训练环节。Roboflow导出的data.yaml确实能用,但直接跑和调好参数再跑,结果差距非常大。牙科影像不是常规自然图,五个参数必须重新设定。
4.1 数据目录整理与data.yaml配置
打开data.yaml,Roboflow会自动生成一套配置,但path字段经常是相对路径或绝对路径,换机器就容易断。我会手工重写一份干净的配置:
# YOLOv8 训练用 data.yaml path: /home/user/dataset # 换成你解压后的绝对路径 train: train/images val: valid/images names: 0: crown 1: root 2: caries 3: pulp注意几个细节。path写绝对路径,避免在不同目录下启动训练时找不到数据。train和val都指向images目录,ultralytics会自动把images替换成labels去找同名标注文件,不要手写labels路径。names的顺序必须严格对应标签文件里的class_id,导出包里crown是0,这里就得把crown放第0位,顺序乱了训练就全乱了。test目录存在与否不影响训练,可以留空不管。
4.2 最小训练命令与参数含义
一份能直接启动的训练命令长这样:
yolo detect train \ data=dataset/data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=960 \ batch=4 \ device=0model指定yolov8s.pt,首次运行会自动下载COCO预训练权重,这是迁移学习的基础。epochs我给150,牙科数据集通常几百到几千张,100轮以内模型还没收敛充分,150轮配合早停比较稳。imgsz设960是牙科影像的底线,全景片原图2000像素,缩到640后一颗牙只剩几个像素,特征全丢了。batch设4是因为高分辨率输入很吃显存,8GB显存跑batch=8大概率OOM。
下面这张表是我常用的参数基线,直接照抄可以少踩一半坑:
| 参数 | 建议值 | 说明 |
|---|---|---|
| imgsz | 960-1280 | 低于640会漏检小牙齿 |
| batch | 2-8 | 显存不够就降batch,不要降imgsz |
| epochs | 100-200 | 用早停机制兜底 |
| model | yolov8s | 数据量小用n,显存够用m |
| lr0 | 0.001-0.01 | 迁移学习不建议用默认0.01以上 |
4.3 数据量不足时的迁移学习策略
牙科数据集规模通常不大,几百张图属于常态。这时直接全量微调容易过拟合,验证集mAP好看,换一批新牙片就露馅。常见做法是冻结backbone前若干层,只训练检测头和靠近输出的特征层:
from ultralytics import YOLO model = YOLO("yolov8s.pt") # 冻结前10层,保留COCO学到的通用特征,只微调高层语义 for p in model.model[:10].parameters(): p.requires_grad = False model.train( data="dataset/data.yaml", epochs=80, imgsz=960, batch=4, lr0=0.001, )冻结的原理是让底层的边缘、纹理特征保持COCO预训练状态,这些低级特征在医学图像上同样适用,不需要重新学。只有高层检测头需要适应牙科解剖结构。如果数据集超过3000张,完全可以不冻结直接全量训练,效果通常更好。具体冻结多少层要看ultralytics版本,len(model.model)能拿到总层数,按比例取前三分之一比较稳妥。
4.4 训练过程中的监控指标
训练开始后,终端会持续输出P、R、mAP50、mAP50-95四列指标。对牙科数据来说,P和R的波动会比较剧烈,因为小目标多,边界稍有偏移IoU就掉下去了。我一般只看两个信号:mAP50有没有稳定上升,mAP50-95有没有跟着缓慢爬升。前者代表模型大致学会了定位牙齿,后者代表边界框精度在逐步提升。
训练结束后,用results.csv画损失曲线是检查收敛情况的标准操作:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/detect/train/results.csv") plt.plot(df["epoch"], df["train/box_loss"], label="train_box_loss") plt.plot(df["epoch"], df["val/box_loss"], label="val_box_loss") plt.legend() plt.savefig("loss_curve.png", dpi=150)看这张图时,重点观察val_box_loss在后期是否反弹。如果验证损失先降后升,说明模型开始过拟合,应该回调到验证损失最低的那个epoch对应的权重,或者减少epochs。牙科数据集的box_loss收敛得比自然图像慢,这属于正常现象,坐标精度要求高,模型需要更多轮次来磨边界。
5. 牙齿检测的避坑清单:5个让训练翻车的常见问题
牙科数据训练的翻车现场,大多不是模型问题,而是数据和参数设置问题。下面五条是我实际排查中遇到最多的,按“现象→原因→解决”的顺序整理,遇到类似情况可以直接照方抓药。
5.1 损失下降但mAP一直为零:先查标签文件坐标
现象:训练几十轮,box_loss正常下降,但验证集mAP从第一轮到最后一轮全是零。
原因:标签文件和data.yaml的names顺序错位,或者标签坐标越界。Roboflow导出时选了resize,标签坐标按新分辨率换算后,某些目标被裁切出了画面,宽高变成负值,YOLOv8读取时静默丢弃。
解决:先跑第2章的体检脚本,把所有越界标签打印出来。然后人工核对names顺序,拿一张图片和对应txt画框验证:
import cv2 img = cv2.imread("train/images/001.jpg") h, w = img.shape[:2] with open("train/labels/001.txt") as f: for line in f: cls, xc, yc, bw, bh = map(float, line.split()) x1 = int((xc - bw / 2) * w) y1 = int((yc - bh / 2) * h) x2 = int((xc + bw / 2) * w) y2 = int((yc + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imwrite("check.jpg", img)框如果画出来和牙齿位置对不上,问题必然出在标签坐标系与图片尺寸不匹配上。对越界框,可以直接把坐标clip回0到1区间,但宽高小于0.001的目标建议直接删掉,这是无效标注。
5.2 密集小牙齿漏检:imgsz与预处理尺寸不匹配
现象:大牙、磨牙框得不错,智齿、乳牙、根尖区小病灶漏掉一大片,召回率特别难看。
原因:牙片原图超过2000像素,imgsz设640把每颗牙缩到30像素以内,小目标的特征已经糊成一团。Roboflow导出时如果选了Resize to 640,情况更糟,原图就已经丢过一轮细节。
解决:训练时imgsz提到960或1280,同时把batch降到2,显存不够就换yolov8n。导出数据集时选No Resize,保证原图细节完整。我试过同一套数据,imgsz从640提到960,小目标的召回率能提升10个百分点以上,这是牙科检测里性价比最高的一个参数。
5.3 mAP50高但mAP50-95极低:标注框精度不足
现象:mAP50能到0.85,mAP50-95只有0.3,验证集看起来不错,实际用起来边界框明显偏松。
原因:标注框本身不够精细。龋坏区域边界模糊,标注员习惯性拉一个大框把周围牙体也包进去,IoU阈值在0.5时能对上,提到0.75就全对不齐。这是牙科数据集和白话自然图像数据集的显著区别。
解决:回到Roboflow重新精标模糊边界的类别。定一条规则:龋坏这类不规则病灶,标注框宁可紧贴病灶边缘,也不要为了“保险”往外扩。如果标注工作量太大,优先精标验证集,先把评估指标的可靠性救回来。
5.4 显存OOM:batch、imgsz与梯度累积的取舍
现象:batch设16,imgsz设960,训练几分钟就CUDA out of memory。
原因:高分辨率输入下显存占用指数级增长,960x960的输入加解码特征图,8GB显存根本扛不住大batch。
解决:batch降到2或4,imgsz保持960以上。显存还是不够就换yolov8n,模型参数量小一个量级。不要用降imgsz来换batch,小目标检测场景下,牺牲分辨率比牺牲batch严重得多。另外可以开启缓存选项,数据集小的时候cache=True能减少磁盘IO压力,但会额外占内存。
5.5 水平翻转增强让左右错乱:医学图像别乱开增强
现象:训练时开了水平翻转,训练集和验证集指标都正常,换成新牙片实测,牙位识别一片混乱。
原因:牙科编号以患者自身的左右为准,水平翻转把患者左侧磨牙变成图像右侧,但标签类别还是原来的牙位编号,模型学到的空间关系全是错的。
解决:类别是crown、root、caries这类解剖结构时,翻转的影响较小;类别包含牙位编号时,必须关闭所有Flip增强。Roboflow导出时的augmentation也全关,训练时用ultralytics默认的增强策略就可以了。牙科图像真正合适的增强只有轻微旋转、缩放和亮度微调,幅度控制在10%以内。
6. 验证模型效果与数据回流的进阶玩法
6.1 用类别级AP定位模型的薄弱点
训练结束后,在测试集上跑一次评估:
yolo detect val \ model=runs/detect/train/weights/best.pt \ data=dataset/data.yaml \ imgsz=960输出结果里除了整体mAP,还有每个类别的AP。牙科场景下重点看caries和root这两个类,它们边界模糊、样本量通常最少,最容易拖后腿。如果caries的AP明显低于crown,说明病灶区域的标注一致性有问题或者样本太少。这时返回Roboflow,把验证集里漏检的图片重新标注,补一批caries样本,再生成一个新版本,继续训练。
6.2 把结果消化成部署可用物
PyTorch权重只是中间产物,落地部署通常要转格式:
yolo export \ model=runs/detect/train/weights/best.pt \ format=onnx \ imgsz=960导出ONNX后,再按目标设备转成对应格式。比如边缘设备RK3588做推理,要经过RKNN-Toolkit转成rknn格式;如果只是做批量推理,ONNX配合ONNX Runtime就够用了。导出前确认一下输入尺寸和训练时一致,否则部署端会遇到两个问题:小目标再次丢失,或者标签框坐标偏移。
我自己的习惯是每训完一版模型,就把错误样本导出成一张拼图,肉眼过一遍。原因很简单,mAP指标不会告诉你漏检的是哪一类牙齿,而看拼图五分钟就能定位到问题。牙科数据集的价值不在于第一次训练分数有多高,而在于你能把每次失败样本都回流到Roboflow重新标注,让同一份数据在迭代中越来越强,模型越用越准。希望这个工作流对你有帮助。
本文还有配套的精品资源,点击获取