目标检测数据集制作全流程:VOC/COCO/YOLO格式互转与验证指南
发布时间:2026/9/30 10:24:36来源:尧图网络
做检测项目这几年我踩过最大的坑往往不在模型而在数据。前阵子帮朋友处理一套电力红外检测数据也就是大家常说的firc-dataset那种场景——几千张红外图标注完是标准的VOC格式转成YOLO格式送进去训练结果loss死活不降。排查了一个下午最后发现只是坐标归一化时把宽和高的顺序搞反了。这类“标注两星期转换两小时训练崩两天”的循环我相信做过目标检测的同行多多少少都经历过。这篇就把检测数据集制作的全流程一次讲清楚数据怎么收集、标注怎么做、VOC/COCO/YOLO格式之间到底差在哪、互转怎么一次搞对以及转完之后的验证流程。不管是做鸟类目标检测、车辆检测BDD100K那种、开关闭合检测还是传送带异物检测这套流程都是通用的。1. 数据收集先别急着标注想清楚这三件事再说1.1 数据来源公开数据集、现场采集和“半成品”数据的取舍数据集制作的第一步不是打开标注工具而是解决“图从哪来”的问题。我见过太多人上来就手动标注结果标了三分之一发现类别定义不清晰全部返工。根据我自己的实操经验数据来源一般分三条路公开数据集像VOC2007/2012、COCO、BDD100K、VisDrone这类优点是有现成的标签、类别体系成熟、可以直接用于预训练或者迁移学习。缺点是领域不一定匹配——你做电力设备检测总不能指着COCO里的猫狗用。所以公开数据集更适合作为“基础数据”或者“负样本”用来丰富背景多样性防误检。现场/真实场景采集这是工业项目里最可靠的数据来源。摄像头架在产线传送带上方、无人机飞一圈变电站、手机拍配电柜都是常见方式。采集的时候注意光照变化、角度变化、远近尺度变化尽量让目标出现的位置和大小在画面里覆盖足够多的可能性。另外不同时间段、不同天气、不同设备型号都要覆盖到否则模型很容易过拟合到某一种特定环境。“半成品”数据的再处理比如已有的视频监控录像抽帧、网上爬来的设备图、客户提供的现场照片。这类数据最大的问题是质量参差不齐标注前必须做一轮清洗。这里有个容易被忽略的点数据量不是越多越好均衡度才是关键。我曾经做过一个开关闭合检测的项目闭合状态的图片有8000张断开状态只有600张训练出来的模型对“断开”的召回率惨不忍睹。所以收集阶段就要有意识地做类别分布统计发现某个类别明显偏少优先补充那个类别的数据。1.2 清洗比你想的重要哪些图必须扔掉收集回来的数据不等于是能用的数据。我在项目里会固定做一轮清洗规则很简单严重模糊、过暗或过曝的图直接删。标注的时候勉强能看清边缘但训练的时候模型学到的是噪声。目标占比过小的图要谨慎。比如一张1920x1080的画面里目标只有10x10像素这种样本对检测器的训练意义不大还容易干扰损失函数。重复度太高的图要去重。视频抽帧出来的数据往往前后几帧几乎一样如果不做去重训练集和验证集之间就会有“近亲”数据导致验证指标虚高。标注难度极大的图目标被严重遮挡、多个目标密集交叠单独放。这类图不是不能用而是应该放在训练集后半段等模型先学会简单样本再上难度。我习惯在采集后写一个简单的统计脚本把所有图片尺寸、通道数、格式列出来检查是否有损坏图片或尺寸异常。很多转换脚本报错根源就是数据里混了一张损坏的JPG你花了一晚上debug最后发现是图片的问题。1.3 类别体系先设计再动手标注类别体系是数据集的地基这块我吃过亏。有一回做输电线路异物检测一开始只定义了“异物”这一个类别标了2000张后发现——“异物”和“鸟巢”“塑料袋”在模型眼里完全是不同类型的东西统一叫“异物”导致模型学得稀里糊涂。后来重新定义成“鸟巢”“悬挂异物”“飘挂物”三个类别效果立竿见影。设计类别体系时有几个原则类别名用英文不要用中文。倒不是崇洋媚外而是很多框架对中文标签支持不好VOC转YOLO的时候类别映射文件classes.txt里如果写入中文轻则告警重则训练直接崩溃。类别之间要互斥。同一个目标不能同时属于两个类别。比如“开关”和“闭合的开关”就不能同时作为两个类别存在正确的做法是定义“开关”类别再用状态细分或者用“开关闭合”“开关断开”两个互斥类别。类别粒度要服务于业务需求。做检测不是类别越多越好。比如车辆检测如果业务只需要区分“车”和“人”就不要硬分成“轿车”“卡车”“公交车”类别越多标注成本越高模型难度越大互相混淆的概率也越高。2. 标注环节工具选型和规范制定决定模型上限2.1 标注工具怎么选从LabelImg到半自动标注标注工具的选择直接影响效率和输出格式的兼容性。我个人的使用经验是这样的工具适合场景输出格式优缺点LabelImg小型项目、个人学习VOC XML / YOLO TXT轻量、入门简单但批量处理能力弱Labelme多边形/实例分割JSON做分割项目好使矩形检测就不必用它了X-AnyLabeling中等规模项目VOC / COCO / YOLO自带AI预标注效率高值得推荐CVAT团队协作、工业级VOC / COCO / YOLO / TFRecord功能最强但部署和维护成本高个人建议如果你只是几百张图的实验LabelImg够了如果是几千张甚至上万张的工程级数据集直接用X-AnyLabeling或者CVAT一定要选带“AI预标注”能力的工具。比如YOLOv8框一下人工去微调总比从零画框快好几倍。一个5000张的鸟类检测数据集纯手工标注大概要两周用预标注辅助一周就能搞定而且人工只需要关注边界修正。2.2 标注规范边框紧贴目标遮挡和边缘情况有规则很多初学者标注随意框大一点“差不多就行”这是数据集质量的大忌。检测模型学的是“框里的特征”如果框普遍偏大模型输出的框也会偏大框时紧时松模型学到的边界就是模糊的。我这边会定几个死规矩边框紧贴目标可见部分。矩形框必须贴合目标的最小外接矩形目标不可见的部分不纳入。比如人被车挡住一半框就框可见的那一半不要试图“脑补”完整人体。遮挡超过70%的目标可以不标。模型学不到被遮挡目标的完整特征强行标注反而会给训练带来噪声。边缘目标要标。目标只有一小部分在画面内依然要正常标注这对模型在真实场景中的鲁棒性很重要。清晰界定“困难样本”。VOC格式里有difficult标记这类样本如果标注了建议在转换时单独处理——有些转换脚本不区分difficult直接全部转成训练样本结果模型被迫去学那些极难分辨的案例精度反而下降。这里我得强调一句标注不是“画框”那么简单一致性比准确度更重要。同一个目标不同的人可能画出的框大小不同同一个人上午和下午的手感也可能不同。多人协作时务必统一规范否则你得到的就是一个“每个人都有自己的标法”的数据集。2.3 标注完成的质检抽检30%重点看边界和漏标标注完成不等于可以直接训练。我每次在标注完成后都会用脚本做一轮自动检查和一轮人工抽检。自动检查主要做这几件事框坐标是否越界xmin 0、xmax 图片宽度等框面积是否为0两个顶点坐标相同类别是否在预设列表里是否有多边形/矩形格式错乱人工抽检我通常会抽30%左右的图把标注框画回图上快速翻一遍重点看三类问题边界贴合度、目标漏标、类别标错。这些检查和抽检环节看起来多花了两三天实际上省的可能是后面调模型的一个星期。数据集质量不行你怎么调损失函数、怎么换骨干网络都是事倍功半。3. VOC、COCO、YOLO三种格式的底层差异互转不出错的关键3.1 VOC XML基于文件夹的层级结构适合中小数据集Pascal VOC格式本质上就是每个图片配一个同名XML文件里面记录图片尺寸、目标类别和目标的bounding box坐标。它的核心结构是顶层是annotation里面有folder、filename、source、sizewidth/height/depth每个object里有name类别字符串、bndboxxmin/ymin/xmax/ymaxVOC的坐标是绝对像素坐标左上角为原点x向右增大y向下增大。这点和OpenCV、PIL的坐标系统一致处理起来很直观。VOC格式的好处是**“一眼看懂”**XML是文本文件直接打开就能检查。缺点是冗余信息多2000张图就是2000个XML文件管理起来不如单文件方便。此外它的类别是字符串模型训练时需要映射成整数索引这个映射如果每次转换都重新定义就会出大问题——后面我会专门讲。3.2 COCO JSON单文件集中式管理信息更丰富COCO格式是我个人觉得“结构最优雅”的格式。所有信息放在一个JSON文件里顶层有info、licenses、images、annotations、categories五块。三个关键数组categories类别列表id是整数name是字符串images每张图的id、文件名、宽高annotations每个标注的id、关联的image_id、category_id、bbox、area和iscrowd这里要注意COCO的bbox是[x, y, width, height]其中x、y是左上角坐标width和height是框的宽度和高度单位同样是像素。很多人在VOC转COCO时习惯性地把坐标写成VOC的四点形式或者把宽高写成了右下角坐标这是高频错误。3.3 YOLO TXT每张图一个标签文件坐标全部归一化YOLO格式是目前实践中最流行的格式尤其YOLOv5/v8系列以及各种改进结构efficient head yolo、mamba yolo之类的仓库默认都吃这个格式。它的规则很简单每一张图片对应一个同名的.txt文件文件名前缀和图片完全一致只是扩展名不同。文件里每一行代表一个目标class_id center_x center_y width height注意三点class_id是从0开始的整数这个顺序取决于你转换脚本里定义类别列表的顺序而不是XML里的写序。坐标是相对于图片宽高的归一化值范围在0到1之间所以一个简单目标是100x100的框在1920x1080的图上它的w就是100/1920≈0.0521h是100/1080≈0.0926。文本文档里没有图片尺寸信息训练时靠的是图片文件本身去获取宽高再做反归一化得到像素框。YOLO格式的优点是可以直接用文本编辑器打开检查、占用空间小。缺点是如果图片尺寸变了比如训练时resize这些归一化坐标依然有效这其实是它比像素坐标更稳的地方。3.4 三种格式差异对照一张表说清楚维度VOC XMLCOCO JSONYOLO TXT存储方式每图一个XML单文件JSON每图一个TXT坐标类型绝对像素坐标绝对像素坐标归一化相对坐标框的表示xmin,ymin,xmax,ymaxx,y,width,heightcenter_x,center_y,width,height类别字符串object name整数idcategories定义整数idclasses.txt顺序附带信息尺寸、遮挡、困难标记面积、iscrowd等仅框和类别人类可读性好一般好这张表是格式互转的全部底层依据。你只要搞清楚坐标系统像素 vs 归一化、框的表示方式两点 vs 左上宽高 vs 中心宽高、类别表示字符串 vs 整数id这三个差异转换脚本自己都能写出来。4. 格式互转的实战脚本直接复制改改就能用4.1 VOC转YOLO最常用的转换注意归一化和类别映射VOC转YOLO在检测项目里出现的频率最高我直接给一个可复用的Python脚本核心逻辑用标准库加上xml.etree就能跑完不依赖第三方库。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_dir, output_dir, classes_file): # 读类别列表注意顺序就是YOLO的class_id with open(classes_file, r, encodingutf-8) as f: classes [line.strip() for line in f.readlines()] class_to_id {name: idx for idx, name in enumerate(classes)} os.makedirs(output_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 读取图片尺寸 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) # 文件名前缀保持和图片一致 base_name os.path.splitext(xml_file)[0] out_file os.path.join(output_dir, base_name .txt) with open(out_file, w, encodingutf-8) as f: for obj in root.iter(object): # 跳过difficult样本按需保留 difficult obj.find(difficult) if difficult is not None and int(difficult.text) 1: continue name obj.find(name).text if name not in class_to_id: print(f警告: {xml_file} 存在未知类别 {name}) continue cls_id class_to_id[name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 绝对像素转中心点归一化 cx ((xmin xmax) / 2) / img_w cy ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 钳制到0-1防止越界 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) f.write(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n) print(f转换完成输出到 {output_dir}) # 使用示例 # voc_to_yolo(VOC2007/Annotations, yolo_labels, classes.txt)这个脚本有几个细节我在实战中反复踩过classes.txt的顺序就是class_id的顺序。如果你的类别列表是手动敲的千万要和标注时的类别定义完全一致少一行多一行都会导致后续训练时类别错位。我最惨的一次教训就是把classes.txt里一个类别的顺序写错了训练出来的模型把“鸟”识别成“车辆”因为class_id对不上。使用./码时注意“中心点”才是YOLO需要的。从VOC的xmin/ymin/xmax/ymax先算中心点再归一化一步都不能省。有人图省事直接把xmin除以宽、宽度除以宽结果框全偏到左上角。钳制到0~1很重要标注时偶尔会遇到超出边界的框不钳制的话后续训练很容易出NaN。4.2 COCO转YOLO重点处理“类别ID映射”和“图片宽高”COCO转YOLO比VOC转YOLO稍微绕一点因为COCO的categories里的id是自定义的可能是100、120这种非连续值必须重新映射。import json import os def coco_to_yolo(coco_json, output_dir): with open(coco_json, r, encodingutf-8) as f: data json.load(f) os.makedirs(output_dir, exist_okTrue) # 构建COCO类别id - YOLO class_id的映射 cat_id_to_cls_id {} for idx, cat in enumerate(sorted(data[categories], keylambda x: x[id])): cat_id_to_cls_id[cat[id]] idx # 图片id - 宽高 和 文件名 img_id_to_info {} for img in data[images]: img_id_to_info[img[id]] { file_name: img[file_name], width: img[width], height: img[height] } # 按image_id聚合annotations img_anns {} for ann in data[annotations]: img_id ann[image_id] img_anns.setdefault(img_id, []).append(ann) for img_id, anns in img_anns.items(): img_info img_id_to_info[img_id] base_name os.path.splitext(img_info[file_name])[0] out_file os.path.join(output_dir, base_name .txt) w img_info[width] h img_info[height] with open(out_file, w, encodingutf-8) as f: for ann in anns: cls_id cat_id_to_cls_id[ann[category_id]] x, y, bw, bh ann[bbox][:4] cx (x bw / 2) / w cy (y bh / 2) / h nw bw / w nh bh / h f.write(f{cls_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}\n) # coco_to_yolo(annotations.json, yolo_labels)注意COCO的bbox里的宽高是绝对值直接对应到YOLO的宽高不需要再从两点坐标去推导。4.3 YOLO转COCO反归一化最容易被忽略的“图片实际尺寸”反向转换YOLO转COCO需要读图片才能拿到宽高因为YOLO的txt里没有尺寸信息。import json import os from PIL import Image def yolo_to_coco(img_dir, label_dir, classes_file, output_json): with open(classes_file, r, encodingutf-8) as f: classes [line.strip() for line in f.readlines()] images [] annotations [] categories [] ann_id 1 for i, cls in enumerate(classes): categories.append({ id: i, name: cls, supercategory: none }) for img_name in sorted(os.listdir(img_dir)): if not img_name.lower().endswith((.jpg, .jpeg, .png, .bmp)): continue with Image.open(os.path.join(img_dir, img_name)) as img: w, h img.size image_id len(images) 1 # 处理EXIF旋转问题后面会细讲 from PIL import ImageOps img2 Image.open(os.path.join(img_dir, img_name)) fixed_img ImageOps.exif_transpose(img2) if fixed_img.size ! img.size: w, h fixed_img.size images.append({ id: image_id, file_name: img_name, width: w, height: h }) base_name os.path.splitext(img_name)[0] label_file os.path.join(label_dir, base_name .txt) if not os.path.exists(label_file): continue with open(label_file, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) cx, cy, nw, nh map(float, parts[1:5]) # 反归一化 x (cx - nw / 2) * w y (cy - nh / 2) * h bw nw * w bh nh * h annotations.append({ id: ann_id, image_id: image_id, category_id: cls_id, bbox: [round(x, 2), round(y, 2), round(bw, 2), round(bh, 2)], area: round(bw * bh, 2), iscrowd: 0 }) ann_id 1 with open(output_json, w, encodingutf-8) as f: json.dump({images: images, annotations: annotations, categories: categories}, f) print(fCOCO JSON已生成: {output_json})这里的ImageOps.exif_transpose是个容易漏掉的细节。手机拍摄的照片自带EXIF方向信息PIL读取的尺寸和实际显示方向不一致不做修正转出来的COCO坐标就会整体偏移。我之前在车辆检测数据集中遇到一批手机拍的图就是这个问题导致框全部错位。4.4 互转时的三大“致命细节”类别顺序、坐标系统、路径一致性做格式互转这么多年我总结出最容易让转换脚本出错的三大细节类别顺序影响模型结果且不可见、难排查。YOLO训练时class_id完全由txt里的整数决定classes.txt的顺序一旦和预训练权重或标注定义不一致模型就会把A类目标当成B类学习。排查起来特别痛苦因为训练loss看起来正常模型也能收敛只是预测时类别全乱。建议每次转换后把classes.txt打印出来对照一下原始定义肉眼确认一遍。坐标系统搞混是最常见的“脚本写错”原因。VOC和COCO都是像素坐标YOLO是归一化坐标VOC是xmin/ymin/xmax/ymaxCOCO是x/y/width/heightYOLO是cx/cy/width/height。这三个坐标系互相转换的公式其实很简单但总有人转来转去搞混归一化和绝对坐标。我的建议是把转换函数单独封装加单元测试比如构造一个已知的框验证转换后再转回来坐标误差不超过1像素。路径一致性决定了训练脚本能不能找到数据。COCO的file_name是相对路径还是绝对路径YOLO的图片目录和标签目录是否同名VOC里folder字段会不会影响读取——这些在转换脚本里都要统一。我吃过一次亏COCO JSON里的file_name写的是相对路径images/xxx.jpg训练脚本的目录层级配错一位结果训练时一直报FileNotFoundError我当时还以为是转换脚本的问题。5. 转换后的验证不验证的转换等于白转5.1 可视化验证把标签画回图上一眼看出对错转换完第一件事不是直接开训练而是可视化。写一个小脚本把YOLO格式的txt读出来反归一化后画回原图看标注框和目标的贴合程度。import cv2 def draw_yolo_labels(img_path, label_path, classes): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) cx, cy, nw, nh map(float, parts[1:5]) x int((cx - nw / 2) * w) y int((cy - nh / 2) * h) bw int(nw * w) bh int(nh * h) color (0, 255, 0) cv2.rectangle(img, (x, y), (x bw, y bh), color, 2) cv2.putText(img, classes[cls_id], (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1, cv2.LINE_AA) cv2.imshow(check, img) cv2.waitKey(0)可视化不只是看“有没有框”要看三点框是否贴合目标、类别是否正确、是否有边框异常偏移。我一般随机抽30张图用脚本生成一张拼接大图一次看30个样本。如果90%以上都正常基本可以放心进训练如果发现框普遍偏左或偏下优先检查坐标归一化公式而不是继续转换。5.2 统计验证用数据说话避免“看起来没问题”可视化解决“单个样本对不对”的问题统计解决“整个数据集健不健康”的问题。我每个数据集都会输出一份统计报告图片总数、标注框总数、每张图的平均框数每个类别的样本数、标注框数、框面积分布是否出现空标注文件有图片没标签是否存在“图片尺寸为0”的异常记录这些统计指标能帮你发现几个比较隐形的问题比如某个类别的样本数明显偏低类别不均衡、某些图片完全没标注漏标严重的图、框的面积分布过于集中在极小或极大值可能标注不规范。这里我要插一句热词里提到的“yolo混淆矩阵总合不唯一”的问题。很多人训练完发现混淆矩阵行列加起来对不上第一反应是代码bug其实很多时候是数据集里存在重复标注、空标注文件或者类别映射错位导致的。统计验证提前发现这些后面就不会一脸懵。5.3 训练前的Smoke Test转完跑一个最小训练及时暴露隐患可视化没问题、统计也没问题还不够。我会在正式训练前跑一个“冒烟测试”——用一张显卡拿整个数据集训练1个epoch或者一个很小的batch观察三件事数据加载阶段是否报错图片路径错误、标签格式不合法、类别索引越界都会在第一个batch暴露。loss是否正常变化如果loss一开始就是NaN八成是标签里有非法坐标值比如0/0或者是图片损坏、读取失败。验证集上的基础指标训练完一个epoch后跑一遍validation看mAP是否有个小小的起步值。如果mAP是0且一直不动优先怀疑标签和真值的格式不匹配。Smoke test也就十几分钟的事但它能把“转换错误”和“模型配置错误”快速分开省掉后面排查弯路。6. 实战一年踩过的坑有些经验只能靠时间换6.1 图片损坏和EXIF旋转两个最隐蔽的“刺客”第一个坑是图片损坏。转换脚本code写得很完美但数据集里混了一张损坏的JPGPIL.Image.open能打开因为读取了文件头但cv2.imread读取失败返回None然后脚本直接崩溃。有时候不是完全损坏而是尺寸信息和实际像素不一致导致反归一化后的坐标偏到画面外。解决办法是在收集清洗阶段就统一用PIL.Image.verify()做一轮完整性检查把打不开的图全部筛选出来。第二个坑是EXIF旋转我前面提过一嘴这里展开讲讲。手机竖拍的照片元数据里会记录“需要旋转90°才能显示正确方向”但很多脚本不会自动处理这个方向信息。直接读像素时宽和高是反的实际是横图存成了竖图导致YOLO转COCO时尺寸错误、框的位置偏移了90度。这个问题在公开数据集里几乎遇不到但真实工业场景里手机拍的图一多就非常容易中招。解决方案就是前面代码里用ImageOps.exif_transpose处理或者干脆在收集环节对所有图统一做一次“方向标准化”把方向信息直接写进像素里。6.2 类别大小写和“隐藏字符”问题另一个微不足道但实际很烦的坑是类别字符串的大小写不一致。比如同一个类别在标注时有些图写的是bird有些图写的是Bird转换脚本在构建映射表时严格区分大小写就会导致部分目标被丢弃。我遇到过更极端的例子某一批XML里类别名末尾被神秘地加了一个空格通常是Excel编辑或者复制粘贴时带入的name.strip()没做的话这个类别就会被识别成“另一个类别”。这类问题只要在转换脚本里统一加一行name name.strip().lower()就能解决但前提是你得提前意识到。我的习惯是所有类别名字在进入classes.txt之前都做一次strip和统一大小写避免后续各种稀奇古怪的对不上。6.3 我的最终操作习惯一套稳定的“固定流程”踩过足够多的坑之后我现在做数据集基本上是一套固定流程每一步都是踩坑换来的收集后先清洗完整性检查、去重、清晰度初筛。类别定义写成文件命名一个classes.txt开工前全员确认。标注时定期抽查每周输出一次可视化拼接图确认标注质量稳定。转换脚本固定模板所有格式互转都用同一个脚本库不做重复造轮子。转换完成后三步验证可视化抽检、统计报告、冒烟测试跑一个epoch。数据和标签同步备份原始标注和转换后标签分开存放防止误覆盖。这套流程下来数据集制作基本可以做到“一次搞对”而不是反复返工。最后分享一个小技巧如果你在做一个全新的检测项目不知道数据集格式怎么组织最省事直接参考你准备用的那个训练仓库的data.yaml或者配置文件——YOLOv5/v8的仓库对数据集目录和标签格式有明确要求你按照它的规范去组织数据和转换标签比你自己“觉得合理”地乱放要省事得多。这算是我做了十几个检测项目后最真诚的一条建议数据集的目的是服务训练不是让你自己看着舒服所以先看训练框架的脸色再动手整理数据才是真正的“一次搞对”。
网站建设高端定制企业官网