☰
肺结节CT数据集转YOLOv5格式全流程:从DICOM到目标检测训练
2026/9/28 5:16:46 网站建设 项目流程

简介:面向医学图像目标检测场景,这套肺结节CT图像数据集已按YOLOV5标准目录结构整理,含images与labels子文件夹,无需转换即可直接投入训练。数据共248张图像,其中训练集220张、测试集28张,图像均为标准JPG格式,每张均配有同名的TXT标注文件,目标类别仅'肺结节'一种,可直接用于YOLO系列模型的训练与验证。压缩包共499个文件,以248个JPG图像与248个TXT标签为主体,另附1个类别字典文件和1个Python可视化脚本,可随机绘制并保存单张图像的边界框,无需修改参数即可快速核验标注质量。整包大小约5.14MB,轻巧易用,适合医学影像检测方向的学习者、研究者作为基准数据、实验测试或课程设计素材。目前已有990人浏览学习,可大幅节省数据整理与格式转换时间。

1. 拿到肺结节CT目标检测数据集后,第一件事为什么是检查目录而不是训练

医学图像之目标检测数据集(YOLOV5目录格式)——尤其是肺结节CT图像目标检测数据集——听上去像是开箱即用的交付物,实际却常带着一堆“历史遗留问题”。YOLOv5只认images/和labels/配对、坐标归一化到 0~1 的 txt 格式;类别编号、图片名、data.yaml 任何一处错位,训练流程都会在启动阶段就崩掉。而大多数CT原始数据是DICOM序列加XML或JSON标注,真正能被train.py直接读取的少之又少。这篇笔记会沿着“目录检查 → 格式转换 → 训练调参 → 反向校验”的顺序,把肺结节CT图像目标检测数据集整理成YOLOv5可用格式的完整路径和坑讲清楚。适合刚拿到医学图像数据集、准备训练自己的目标检测模型的同学,也适合替医学影像团队做落地的算法工程师。

2. 从DICOM到YOLOv5目录格式:CT切片的坐标体系与标注格式变迁

2.1 CT影像为什么必须经过窗宽窗位映射才能用于YOLOv5训练

CT图像不是普通照片,它存的是体素CT值,范围通常在 -1024 到 3071 Hounsfield单位(Hu)之间。如果你直接把pixel_array存成PNG,得到的是一张灰阶范围极宽、对比度极低的图,肺结节和血管在视觉上糊成一片。YOLOv5输入要求的是uint8的0~255灰度或RGB图,所以必须先做映射。

常见做法是用“肺窗”参数做线性窗宽窗位映射。肺窗窗宽1500 Hu、窗位-600 Hu,可以把-1350~150 Hu的CT值映射到0~255,让肺实质和结节对比度拉开。纵隔窗则适合看大血管和病灶浸润,但对小结节不友好。

import pydicom import numpy as np ds = pydicom.dcmread('CT_slice_001.dcm') pixels = ds.pixel_array.astype(np.float32) # 部分DICOM有RescaleSlope/RescaleIntercept,必须还原成真实CT值 if hasattr(ds, 'RescaleSlope') and hasattr(ds, 'RescaleIntercept'): pixels = pixels * float(ds.RescaleSlope) + float(ds.RescaleIntercept) window_width = 1500 # 肺窗宽 window_level = -600 # 肺窗位 lower = window_level - window_width / 2 upper = window_level + window_width / 2 clipped = np.clip(pixels, lower, upper) image_8bit = ((clipped - lower) / (upper - lower) * 255).astype(np.uint8)

这段代码解决“读出来是灰蒙蒙”的问题。window_width和window_level是重点参数,直接影响后续目标检测模型的输入分布。如果你拿到的数据集已经提供PNG,也要确认它到底用的什么窗宽窗位,最好在预处理阶段统一,而不是把不同窗参数、不同设备的切片混在一起训练,不然模型会在推理时被CT设备的差异性带偏。

2.2 YOLOv5目录格式的三条硬性规定

YOLOv5目录格式的核心只有三条。

第一,图像和标签文件分开放在images和labels目录下,且文件名必须一一对应:slice_001.png对slice_001.txt。标签文件不存在时,该图会被当作纯背景负样本参与训练。

第二,每个txt文件里,每行代表一个标注框,格式是class_id x_center y_center width height,全部除以图像宽高归一化。例如一个100x200像素的图像里,标注框左上角在(20,30),右下角在(60,80),那么转换结果是:

0 0.4000 0.2750 0.4000 0.2500

计算方式是x_center=(20+60)/2/200=0.4,注意这里分母200是图像宽度;y_center=(30+80)/2/200=0.275,分母200是高度;宽度为40/200=0.2?这里我算错了,应该是 (60-20)/200=0.2。刚才给的0.400是中心点相对到左边的距离,而宽度占0.2,需要更正。避免误导,我在正文里用正确示例。

更准确来说:

0 0.4 0.275 0.2 0.25

其中0.4是x中心相对值,(20+60)/2=40,40/100=0.4;0.275是y中心相对值,(30+80)/2=55,55/200=0.275;0.2是宽度相对值,(60-20)/100=0.2;0.25是高度相对值,(80-30)/200=0.25。

第三,data.yaml中写明数据集根路径、训练/验证子目录、类别数和类别名。类别编号必须和txt第一列一致,这是最常见的翻车点。

为了降低混淆,我列一张格式对照表:

数据形式坐标基准典型内容
DICOM原始像素左上角起点,像素绝对坐标矩阵数值,不含归一化
Pascal VOC XML左上角起点,绝对像素xmin,ymin,xmax,ymax
COCO JSON左上角起点,绝对像素bbox: [x, y, width, height]
YOLOv5 txt左上角为0,右下角为1class 中心x 中心y 宽 高

2.3 拿到数据集先做清点,再动手转换

在转换之前,我会先统计文件数量、图像分辨率、每个类别的实例数。很多医学数据集的类别说明和实际标注并不一致,比如文档写着“良性0恶性1”,但txt里第一列只有0和2,中间编号全空。

下面这段脚本可以快速清点:

import os import collections label_dir = 'labels' cls_counter = collections.Counter() empty_files = [] for fname in os.listdir(label_dir): fpath = os.path.join(label_dir, fname) with open(fpath, 'r', encoding='utf-8') as f: lines = [line.strip() for line in f if line.strip()] if not lines: empty_files.append(fname) continue for line in lines: cls_counter[line.split()[0]] += 1 print('类别编号 -> 实例数:', dict(cls_counter)) print('空标签文件数量:', len(empty_files))

这段脚本能第一时间发现两个问题:某个类别没有样本,或存在大量空标签。空标签对应的是“正常切片”,在YOLOv5中作为负样本是合理的,但如果空文件数量异常多,就要检查是不是标注解析时漏读了一大批数据。这个清点步骤,应该发生在任何格式转换之前。

3. 把肺结节CT数据整理成YOLOv5可训练的目录:脚本化转换流程

3.1 建立目录结构并约定命名

无论原始数据是XML还是JSON,最终都要落到同样的磁盘结构。先创建固定的images/train、images/val、labels/train、labels/val四个目录。

mkdir -p lung_nodule_yolo/images/train mkdir -p lung_nodule_yolo/images/val mkdir -p lung_nodule_yolo/labels/train mkdir -p lung_nodule_yolo/labels/val

命名建议使用patientID_series_slice.png这样的可追溯命名,例如P003_2_044.png,对应标签P003_2_044.txt。文件名中的空格和中文一定要先清理掉,否则YOLOv5的Dataloader在Linux上会跳过,Windows上可能直接报编码错误。这一步看似基础,实则很多整理工作都败在文件名不一致。

3.2 标注文件转YOLO txt:坐标归一化与越界处理

下面给出一套从Pascal VOC XML转YOLO txt的最小可用脚本。如果你的数据是COCO JSON或医学专用格式(如NIfTI+JSON),只要按同样的思路把读取部分换掉即可。

# voc2yolo.py import os from PIL import Image from xml.etree import ElementTree as ET # 类别映射表,按实际数据集修改 class_dict = { 'nodule': 0, 'benign_nodule': 1, 'malignant_nodule': 2, } def voc_to_yolo(xml_path, img_width, img_height): """ 将单个Pascal VOC XML标注转换为YOLO格式的文本行。 返回字符串列表,每行一个目标。 """ tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall('object'): name = obj.find('name').text.strip() if name not in class_dict: continue cls_id = class_dict[name] bndbox = obj.find('bndbox') xmin = float(bndbox.find('xmin').text) ymin = float(bndbox.find('ymin').text) xmax = float(bndbox.find('xmax').text) ymax = float(bndbox.find('ymax').text) # 将坐标限制在图像范围内,防止标注框越界导致训练时出现负坐标 xmin = max(0, min(xmin, img_width)) ymin = max(0, min(ymin, img_height)) xmax = max(0, min(xmax, img_width)) ymax = max(0, min(ymax, img_height)) x_center = (xmin + xmax) / 2.0 / img_width y_center = (ymin + ymax) / 2.0 / img_height width = (xmax - xmin) / img_width height = (ymax - ymin) / img_height # 过滤宽高为0的废标注,避免训练loss出现NaN if width <= 0 or height <= 0: continue lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") return '\n'.join(lines) if __name__ == '__main__': xml_dir = 'xml_annotations' out_dir = 'labels_raw' image_dir = 'images_raw' os.makedirs(out_dir, exist_ok=True) for xml_name in os.listdir(xml_dir): if not xml_name.endswith('.xml'): continue stem = os.path.splitext(xml_name)[0] xml_path = os.path.join(xml_dir, xml_name) img_path = os.path.join(image_dir, stem + '.png') if not os.path.exists(img_path): print('缺少对应图片:', img_path) continue # 读取最终PNG尺寸,必须与喂给YOLOv5的图像尺寸一致 with Image.open(img_path) as im: w, h = im.size yolo_text = voc_to_yolo(xml_path, w, h) out_path = os.path.join(out_dir, stem + '.txt') with open(out_path, 'w', encoding='utf-8') as f: f.write(yolo_text + '\n')

这段脚本里有几个关键参数值得注意。

  • class_dict的键必须和XML里object/name的字符串一字不差。医学数据里常见nodule、non_nodule、benign、malignant混用,宁可先打印一遍所有XML里的name集合,再定映射表。
  • img_width和img_height取的是最终PNG的尺寸。如果CT原图是512x512,但保存时为了节省空间压缩成了256x256,那么XML里原有坐标必须同步缩放,否则框整体偏移。最稳妥的做法是先统一图像分辨率,再用统一后的尺寸做归一化。
  • 脚本过滤了宽或高为零的框。医学标注偶尔会出现坐标完全相同的点,如果保留,YOLOv5会在计算损失时除以接近零的值,导致训练过程出现 NaN。

3.3 编写data.yaml并解释路径陷阱

一个典型的肺结节数据集data.yaml长这样:

# lung_nodule.yaml path: /data/lung_nodule_yolo train: images/train val: images/val nc: 3 names: 0: nodule 1: benign_nodule 2: malignant_nodule

第一行path建议用绝对路径。YOLOv5的train.py在解析时,会把path与train/val拼接成/data/lung_nodule_yolo/images/train。如果path写成相对路径,而你的终端当前目录不在YOLOv5根目录下,就会报Dataset not found或No labels found。我自己的习惯是所有实验都先写绝对路径,训练完再决定要不要改成相对路径归档。

另外names的顺序必须与转换脚本里的class_id一致。0: nodule表示所有txt文件中第一列为0的都是普通结节。如果转换脚本里benign_nodule映射成了1,而data.yaml中1却写成malignant_nodule,那模型训练时会把良性结节样本强行拟合到恶性标签上,验证集上两个类别的mAP都会乱掉。

3.4 训练前用自检脚本兜底

训练花费很长时间,不值得为了一个低级路径错误反复启动。训练前把下面脚本跑一遍,能过滤掉八成低级问题。

import os from PIL import Image image_dir = '/data/lung_nodule_yolo/images/train' label_dir = '/data/lung_nodule_yolo/labels/train' for fname in os.listdir(image_dir): stem = os.path.splitext(fname)[0] label_path = os.path.join(label_dir, stem + '.txt') if not os.path.exists(label_path): print('缺少标签文件:', fname) continue with open(label_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: print('字段数量错误:', label_path) break cx, cy, bw, bh = map(float, parts[1:]) if not (0 < cx < 1 and 0 < cy < 1 and 0 < bw <= 1 and 0 < bh <= 1): print('坐标越界:', label_path, line.strip()) break

这个脚本验证三件事:标签文件是否存在、行格式是否为5个字段、归一化坐标是否在有效范围内。如果出现越界,原因通常是对图像resize时只改了图像文件,没有同步改坐标转换公式。这一步骤不消耗GPU,但能省下排查训练崩溃的时间。

4. 用YOLOv5训练自己的肺结节检测模型:命令与关键参数

4.1 环境与预训练权重的取舍

YOLOv5的训练环境配置相对固定,基本按官方requirements.txt安装即可。

conda create -n yolov5 python=3.8 -y conda activate yolov5 cd yolov5 pip install -r requirements.txt

这里要注意Python版本不需要强行上最新,3.8~3.10都能跑,重点是把torch版本和CUDA版本匹配好。亲测在PyTorch 1.13、CUDA 11.7的组合下,YOLOv5训练CT图像没有遇到特殊兼容问题。预训练权重建议从yolov5s.pt开始,它参数量最小,能快速验证数据流程是否走通。如果小模型跑通后mAP上不去,再换yolov5m.pt或yolov5l.pt,不要一上来就用大模型,因为医学数据集通常只有几百到几千张切片,大模型容易过拟合。

4.2 训练命令与超参详解

跑通数据流程的最小训练命令是:

python train.py --data lung_nodule.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache

命令行里几个参数在肺结节场景下需要认真权衡。

  • --img 640:控制输入分辨率。CT原始切片常见是512x512,你用640相当于放大了1.25倍,对小结节有一定帮助,但会额外消耗显存。如果原始切片是1024x1024高分辨率数据,我一般倾向直接把--img设为1024,不使用640。对于肺结节这种小目标,输入分辨率宁高勿低。
  • --batch 16:在16G显存下比较安全。如果显存只有8G,降到8更稳。医学数据集本来就小,单batch的波动对loss影响较大,不建议为了省时间盲目调大batch。
  • --epochs 100:对于几百张切片的小数据集,100轮往往还会欠拟合,但超过300轮又会过拟合。确定轮数的办法是看results.png里验证集loss在哪个epoch开始反弹。
  • --cache:将图像预加载进RAM,能避免训练时频繁磁盘IO,尤其是图像是几百KB的大PNG时提升明显。缺点是内存占用高,内存只有16G时慎用。
  • --seed 7:固定随机种子,方便复现对比实验。医学论文复盘时,这是最基本的要求。

补充一个常见超参表:

参数肺结节场景建议原因
--img512或1024与原始CT分辨率贴近,保留小结节细节
--batch8~16显存与loss稳定性折中
--epochs100~200小数据集过拟合风险高
--workers4~8图像解码快,避免数据加载变瓶颈

4.3 训练结果怎么看,怎么决定下一步

训练结束后,YOLOv5会在runs/train/exp下输出一堆指标图。第一优先看results.png,里面的mAP@0.5和mAP@0.5:0.95是整体效果;第二看confusion_matrix.png,它能揭示某个类别是否被大量预测成背景或其他类别。

在肺结节场景,我最关注的不是mAP峰值,而是在置信度阈值0.1下的召回率。原因是漏检是临床最不能接受的错误。如果验证集上小结节类别的AP只有0.2,而大结节类别AP达到0.8,说明模型对大结节学得好,但小结节的尺寸已经低于特征的表达能力。这种情况下,先不要急着换模型架构,回到数据层检查:小结节标注框是否过小、是否在resize时丢失、类别数量是否支撑训练。数据级优化往往比换模型更有效。

5. 肺结节CT目标检测数据集的常见问题:现象、原因与排查路径

5.1 图像和标签文件名不匹配

现象:训练启动后频繁打印No labels found in xxx.jpg,大量图片被当成负样本参与训练。验证集mAP可能还行,但训练集loss曲线一直在高位抖动。

原因:图片文件与txt文件的basename不一致。常见于转换脚本用stem = xml_name.replace('.xml',''),但图像文件名用的是另一套命名,比如加了下划线后缀。

解决:用shell循环快速找出没有对应标签的图片,再统一重命名到一套规范名称。

cd /data/lung_nodule_yolo/images/train for img in *.png; do base="${img%.png}" if [ ! -f "../../labels/train/${base}.txt" ]; then echo "缺失标签: $img" fi done

建议在训练脚本里加入这个检查,每次开始训练前自动执行。YOLOv5的Dataloader会跳过无标签图像,但它不会告诉你这些图其实就是正样本切片的漏标来源。

5.2 类别编号错位导致良性结节被识别成恶性

现象:训练loss正常,但推理结果中所有框的类别都集中在某一个类上,另一类完全不出现。

原因:转换脚本里的class_id编号与data.yaml的names顺序错位。比如转换时把benign映射为1,malignant映射为2,但data.yaml的names列表把1写成malignant,导致模型学到的是“看到良性样本就去拟合恶性标签”。

解决:用清点脚本打印每个class_id的实例数量,随机抽查两个txt标签的原文内容。不要只信数据集的README,以实际文件中的第一列数字为准。如果发现大量1的样本,而文档说1是背景,说明标注方在导出了YOLO格式后可能又换过类别顺序。

5.3 同一个病人的切片被拆进训练集和验证集

现象:验证集mAP高达0.9以上,但换到新CT设备或新病人上时,模型漏检严重,尤其是小结节几乎全军覆没。

原因:划分数据集时按“切片”随机分配。同一个病人的连续CT切片,既有在训练集里的,也有在验证集里的。模型在训练时已经见过同一病灶在不同切片下的形态,验证集相当于开卷考试。

解决:按病人ID分组后再划数据集,保证同一个病人的所有切片只出现在同一个集合。用GroupShuffleSplit是标准做法。

from sklearn.model_selection import GroupShuffleSplit import numpy as np # 假设每个文件名是 patientID_slice.png file_list = [...] # 所有文件路径 patient_ids = [os.path.basename(f).split('_')[0] for f in file_list] gss = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42) train_idx, val_idx = next(gss.split(file_list, groups=patient_ids)) train_files = [file_list[i] for i in train_idx] val_files = [file_list[i] for i in val_idx]

groups参数指定了分组键,GroupShuffleSplit会保证同一个patient_id下的所有切片不会被拆散。这是医学影像数据与自然图像数据集之间最重要的区别,也是不少论文评审会盯着看的细节。

5.4 小结节在resize时被直接抹掉

现象:训练前可视化标注时框还清晰可见,训练后模型对所有小结节完全不触发。看一下错误分析图,这类漏检都集中在小于16x16像素的框。

原因:原始CT是1024x1024,为了省显存强行把--img降到512,病灶从原来的24像素缩到12像素,特征图上的响应已经微弱到可以被忽略。另一个原因是预处理时压缩PNG尺寸,但标签没有同步更新。本质是分辨率不匹配。

解决:训练尺寸尽量贴近原始采集分辨率,或者在数据整理时保留一份高分辨率版本。如果显存确实不够,更合理的做法是把512x512以上的原图切块(tiling),而不是全局resize。这里不做展开,但记住:肺结节数据集里直径小的结节常常只有20像素左右,任何一次过于激进的resize都等于人为制造漏检。

5.5 背景框被当成结节:标注质量参差不齐

现象:验证集上F1不错,但把模型拿到真实CT序列上跑,出现大量假阳性,框全部集中在血管分叉、胸膜处。

原因:原始标注可能由不同医生完成,有些医生把直径3mm的微小高密度影都标为结节,有些医生只标明确的恶性病灶,导致类别内部形态差异极大。另外有些标注框过宽,把周边肺纹理也包进框内,模型学到的其实是“带纹理的背景”。

解决:先统计训练集所有标注框的面积与宽高比分布。如果大部分框面积小于100平方像素,模型就很难学到稳定特征;如果宽高比分布集中在1.0附近,说明标注框总体规整,如果出现大量宽高比2.0以上的框,多半是标注时框取了整片肺叶。最直接的解决手段是抽样画图,把标注框叠加到原图上人工复查,这一步不能被自动化完全代替。

import cv2 # 读取一张验证图片和对应标签,画出前几个框 img = cv2.imread('val_image.png') H, W = img.shape[:2] with open('val_image.txt') as f: lines = f.readlines()[:5] for line in lines: cls, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw / 2) * W) y1 = int((cy - bh / 2) * H) x2 = int((cx + bw / 2) * W) y2 = int((cy + bh / 2) * H) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite('visual_check.png', img)

这个可视化脚本不追求美观,但能在五分钟内让人判断出数据集标注是否靠谱。如果画出来的框有明显偏移,不要怀疑模型,直接修数据。

6. 用模型反向推理校验肺结节数据集的标注质量

6.1 为什么训练后的预测框是标注的照妖镜

训练好的模型已经见过大量“结节”和“非结节”的判别特征。如果数据标注本身可靠,模型在验证集上的预测框应当与真实框大面积重合。反过来,那些只有标签没有预测,或只有预测没有标签的区域,往往就是漏标或错标信号。靠人眼逐张检查几千张切片不现实,用一次推理来扫描数据集,效率高出一个量级。

6.2 批量预测并保留低置信度结果

先用训练好的best权重跑一遍验证集:

python detect.py --weights runs/train/exp/weights/best.pt \ --source dataset/images/val \ --save-txt --save-conf \ --conf-thres 0.1

--save-txt会在runs/detect/exp/labels/下生成与验证图同名的txt,格式比训练标签多一列置信度,也就是class_id x_center y_center width height conf。这里的--conf-thres 0.1是个关键参数:降到0.1可以让模型保留大量低置信度预测框,便于后续找出“模型觉得像结节但标签没有”的漏标候选。

6.3 高置信度无匹配框的自动筛选

下面脚本的核心思路是:把所有预测框与真实GT做IoU匹配,找出IoU低于0.1但置信度高于0.7的预测。这些框大概率是标签漏掉的结节,也可能是模型误检的血管或胸膜,无论如何都值得人工确认。

import os def iou(box1, box2): """box: (x1, y1, x2, y2),像素坐标""" x1 = max(box1[0], box2[0]); y1 = max(box1[1], box2[1]) x2 = min(box1[2], box2[2]); y2 = min(box1[3], box2[3]) inter = max(0, x2 - x1) * max(0, y2 - y1) area1 = (box1[2] - box1[0]) * (box1[3] - box1[1]) area2 = (box2[2] - box2[0]) * (box2[3] - box2[1]) return inter / (area1 + area2 - inter + 1e-9) gt_dir = 'dataset/labels/val' pred_dir = 'runs/detect/exp/labels' for pred_file in os.listdir(pred_dir): if not pred_file.endswith('.txt'): continue stem = pred_file.replace('.txt', '') gt_file = os.path.join(gt_dir, stem + '.txt') if not os.path.exists(gt_file): continue preds = [] with open(os.path.join(pred_dir, pred_file)) as f: for line in f: parts = line.split() if len(parts) == 6 and float(parts[5]) > 0.7: cls, cx, cy, bw, bh, conf = map(float, parts) # 先假设图像宽高为W,H,根据验证图实际值填入 W, H = 512, 512 preds.append((cls, cx, cy, bw, bh, conf)) gts = [] with open(gt_file) as f: for line in f: cls, cx, cy, bw, bh = map(float, line.split()) gts.append((cls, cx, cy, bw, bh)) suspicious = [] for pred in preds: # 转换到像素坐标后再做IoU pred_box = [ (pred[1] - pred[3] / 2) * W, (pred[2] - pred[4] / 2) * H, (pred[1] + pred[3] / 2) * W, (pred[2] + pred[4] / 2) * H, ] if all(iou(pred_box, gt_box_to_pixel(gt, W, H)) < 0.1 for gt in gts): suspicious.append((pred_file, pred, conf))

这个脚本执行后,你会得到一批“模型很有把握,但数据集里压根没有标注”的候选。人工复核这些区域,如果确实是结节,就在原数据集里补齐标注,重新训练一版;如果模型持续在血管或胸膜处误检,说明训练数据里欠缺负样本或标注框本身带有误导性。这个反向校验方法,我通常在第一次训练结束后就做,它能直接告诉你:下次迭代到底应该改模型还是改数据。

我现在的习惯是,等第一轮训练跑完,先不急着调超参数,而是花半小时做一轮反向推理,把高置信度且无GT匹配的框列出来。大多数情况下,数据集里存在的问题比模型还多,这部分清洗工作在后面的每一次迭代里都在帮你节省时间。希望这篇整理能帮你避开肺结节CT数据转换成YOLOv5格式的暗坑。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询