news 2026/10/5 14:24:29

VOC格式转YOLO实战:1702张西瓜数据集训练避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VOC格式转YOLO实战:1702张西瓜数据集训练避坑指南

简介:面向目标检测入门学习与实际工程验证的西瓜图像数据集,采用标准Pascal VOC标注格式,解决西瓜类别检测训练样本不足、标注口径不一等问题。数据集包含1702张真实场景jpg图片,每张图片均配有同名xml标注文件,标注框总数2812个,类别仅设watermelon一类,全部通过labelImg工具人工逐一绘制矩形框完成,标注规则清晰,适合用于目标检测课程实验、模型微调训练以及算法精度对照。压缩包格式为zip,共3405个文件,除1702组图像与标注文件外,另含1个txt说明文件,总大小167.78MB,目录结构简洁,便于直接接入常见深度学习框架。目前已有488人学习下载,对需要VOC格式数据开展西瓜检测实践的用户,解压后即可直接用于训练与评估,无需额外转换格式,可为农业视觉检测、目标识别项目提供可靠的数据基础。

1. 1702张西瓜图,为什么VOC格式是目标检测最好的敲门砖

一个1702张的VOC格式目标检测数据集——西瓜数据集,光看数量不算夸张,却正好踩在入门和实用的分界线上。它有完整的xml标注文件,有真实的自然场景,覆盖成熟瓜、生瓜和不同角度光照,用来练手VOC格式理解、YOLO数据转换和训练评估,一天时间就能走通全流程。对刚入行的工程师,这是零成本的数据集;对跑过COCO、Cityscapes的老手,它又是验证数据清洗和增强策略的绝佳小白鼠。如果你要做成熟度检测、尺寸筛选,或者单纯想搞明白xml标注和txt标注到底差在哪,这1702张图足够撑起一次完整的实战。

2. 先看懂VOC格式:目录结构和xml标注到底存了什么

2.1 拿到数据集第一步:检查JPEGImages、Annotations和ImageSets是否对齐

VOC格式是PASCAL VOC时期定下的老规矩:JPEGImages放原始图片,Annotations放xml标注文件,ImageSets/Main放训练集和验证集的清单。西瓜数据集既然顶着VOC格式的标签,下载后第一件事不是急着训练,而是确认这三个目录完整。很多所谓VOC数据集其实是半成品,缺了ImageSets,或者Annotations里有几个xml是空的,这些坑会在训练到一半时突然炸出来。

我一般会用一段简短脚本检查目录结构和文件数量,而不是在文件管理器里一个个数。这里直接列出目录,并统计每个xml是否都有对应的jpg:

find watermelon_dataset -type d | sort echo "JPEGImages 图片数: $(ls watermelon_dataset/JPEGImages | wc -l)" echo "Annotations 标注数: $(ls watermelon_dataset/Annotations | wc -l)" echo "ImageSets/Main 文件: $(ls watermelon_dataset/ImageSets/Main)"

这段命令先看目录层级是否存在,再统计图片和标注的数量。实际中图片数和标注数不一致是常态,多几个少几个都要留意。如果JPEGImages比Annotations多,说明有些图没标注;反过来则是孤儿xml。ImageSets/Main里通常有train.txt、val.txt或trainval.txt,这些txt里写的是不带扩展名的文件名,用于控制训练和验证的数据划分。

接下来用Python做更严格的对账,确保每一张图都有标注,并且每个xml引用的图片真实存在:

from pathlib import Path base = Path("watermelon_dataset") imgs = {p.stem: p for p in (base / "JPEGImages").glob("*.jpg")} anns = {p.stem: p for p in (base / "Annotations").glob("*.xml")} missing_ann = sorted(set(imgs) - set(anns)) missing_img = sorted(set(anns) - set(imgs)) print("缺少xml的图片:", missing_ann[:10], "共", len(missing_ann)) print("缺少图片的xml:", missing_img[:10], "共", len(missing_img))

这一步能筛掉最常见的“标注文件损坏导致训练中断”的风险。如果发现缺失,常见做法是直接删除没有成对的文件,或者单独用脚本修复。对于西瓜数据集这种量产型数据集,成对率通常很高,但核对一次会让自己心里有底。

2.2 读懂xml:size和bndbox是后面所有转换的源头

VOC的xml核心就是 根节点下面的 和 记录width、height和depth,

先看一个标准xml长什么样,用脚本解析字段更直观:

import xml.etree.ElementTree as ET tree = ET.parse("watermelon_dataset/Annotations/0001.xml") root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) d = int(size.find("depth").text) print(f"图片尺寸: {w}x{h}x{d}") for obj in root.findall("object"): name = obj.find("name").text truncated = int(obj.find("truncated").text) difficult = int(obj.find("difficult").text) box = obj.find("bndbox") xmin = int(box.find("xmin").text) ymin = int(box.find("ymin").text) xmax = int(box.find("xmax").text) ymax = int(box.find("ymax").text) print(f"{name}: 框=({xmin},{ymin})-({xmax},{ymax}), truncated={truncated}, difficult={difficult}")

这段代码把每个目标的类别、坐标和两个标志位都读出来。参数说明里最重要的是difficult和truncated:difficult=1表示这个目标很难辨认,VOC官方允许标注者标出来但不算分;truncated=1表示目标被截断。在VOC评估中difficult会跳过,但在YOLO转换时如果不过滤,它会被当成普通正样本,反而干扰训练。用西瓜数据集时,建议先统计difficult的数量再决定留不留。

2.3 批量统计类别和框尺寸:动手前先摸清家底

转换前我习惯做一次全量统计,看类别名是否统一、框大小是否极端。比如西瓜数据集的类别可能叫watermelon、ripe_wm、"Ripe"或者空格,大小的混乱会让后面的训练数据增强很难受。一个统计脚本能立刻暴露这些问题:

from collections import Counter, defaultdict import xml.etree.ElementTree as ET from pathlib import Path ann_dir = Path("watermelon_dataset/Annotations") class_counter = Counter() box_sizes = defaultdict(list) for xml_path in ann_dir.glob("*.xml"): tree = ET.parse(xml_path) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) for obj in root.findall("object"): name = obj.find("name").text class_counter[name] += 1 box = obj.find("bndbox") xmin = int(box.find("xmin").text) xmax = int(box.find("xmax").text) ymin = int(box.find("ymin").text) ymax = int(box.find("ymax").text) bw = xmax - xmin bh = ymax - ymin box_sizes[name].append((bw / w, bh / h)) # 归一化宽高 print("类别统计:", class_counter) for name, sizes in box_sizes.items(): ratios = [bw / bh for bw, bh in sizes] avg_w = sum(s[0] for s in sizes) / len(sizes) avg_h = sum(s[1] for s in sizes) / len(sizes) print(f"{name}: 样本数={len(sizes)}, 平均归一化宽={avg_w:.3f}, 高={avg_h:.3f}, 宽高比中位={sorted(ratios)[len(ratios)//2]:.2f}")

这段代码统计每类数量、平均框尺寸和宽高比。如果某一类只有几十个样本,后面训练必须加大mosaic或复制增强;如果框宽高比普遍集中在1.0附近,那么anchor设置可以用默认的anchor-free策略,不需要手动调。西瓜这种圆形目标通常很规整,但成熟瓜和未成熟瓜如果数量差距大,训练时要留意类别不平衡。统计输出还能发现类别名前后有空格这类隐蔽错误,例如“watermelon ”和“watermelon”会被当成两个类。

3. 把VOC转成YOLO格式:转换脚本与四个边界坑

3.1 为什么要抛弃xml?YOLO训练接口只认归一化txt

VOC格式是给人看的,xml里存的是绝对像素坐标;而YOLO系列训练时读取的是每张图片对应一个txt文件,每行代表一个目标:class_id x_center y_center width height,四个数值都归一化到0到1之间。Ultralytics的接口不直接吃xml,所以VOC转YOLO是绕不开的步骤。很多朋友问能不能直接改data.yaml让YOLO读VOC,答案是不能,除非你自己写一个dataset类去解析xml,否则老老实实转换。

转换后的目录结构常见做法是:

watermelon_yolo/ ├── images/ │ ├── train/ │ ├── val/ ├── labels/ │ ├── train/ │ ├── val/ └── watermelon.yaml

jpg和txt放在平行目录下,文件名一致,只是扩展名不同。Ultralytics训练时会根据images路径自动去找同级的labels目录,所以类别文本路径不要自己乱配。

3.2 转换脚本:xml路径、归一化坐标和验证划分

下面是一个比较完整的转换脚本,支持按比例划分训练验证集,并直接输出YOLO格式文件。代码直接用Python标准库,不需要装额外依赖:

import os import random import xml.etree.ElementTree as ET from pathlib import Path def convert_voc_to_yolo(voc_root, yolo_root, classes, val_ratio=0.2, seed=42): voc_root = Path(voc_root) yolo_root = Path(yolo_root) img_src = voc_root / "JPEGImages" ann_src = voc_root / "Annotations" (yolo_root / "images" / "train").mkdir(parents=True, exist_ok=True) (yolo_root / "images" / "val").mkdir(parents=True, exist_ok=True) (yolo_root / "labels" / "train").mkdir(parents=True, exist_ok=True) (yolo_root / "labels" / "val").mkdir(parents=True, exist_ok=True) random.seed(seed) xml_paths = sorted(ann_src.glob("*.xml")) random.shuffle(xml_paths) val_count = int(len(xml_paths) * val_ratio) val_set = set(xml_paths[:val_count]) train_set = set(xml_paths[val_count:]) class_to_id = {name: idx for idx, name in enumerate(classes)} for split, paths in [("train", train_set), ("val", val_set)]: for xml_path in paths: tree = ET.parse(xml_path) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) img_name = xml_path.stem + ".jpg" img_path = img_src / img_name if not img_path.exists(): print(f"跳过缺少图片: {img_path}") continue labels = [] for obj in root.findall("object"): name = obj.find("name").text.strip() difficult = int(obj.find("difficult").text) if difficult == 1: continue # 过滤掉difficult样本 if name not in class_to_id: print(f"跳过未知类别 {name} in {xml_path}") continue 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) x_center = ((xmin + xmax) / 2) / w y_center = ((ymin + ymax) / 2) / h box_w = (xmax - xmin) / w box_h = (ymax - ymin) / h labels.append(f"{class_to_id[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") # 拷贝图片到对应分集目录 dst_img = yolo_root / "images" / split / img_name if not dst_img.exists(): import shutil shutil.copy2(img_path, dst_img) txt_name = xml_path.stem + ".txt" txt_path = yolo_root / "labels" / split / txt_name with open(txt_path, "w") as f: f.write("\n".join(labels)) print(f"转换完成: train={len(train_set)}, val={len(val_set)}") # 使用时修改三个入口参数 classes = ["ripe_watermelon", "unripe_watermelon"] convert_voc_to_yolo("watermelon_dataset", "watermelon_yolo", classes)

这段脚本的逻辑是:先按比例和随机种子把xml列表拆成train和val,再逐个解析xml。这里特意过滤了difficult=1的目标,因为YOLO训练不需要“不计分”概念,留着只会让模型学一些模糊样本。坐标计算必须用整图的w和h,而不是某个目标自身的宽高。输出txt时保留6位小数,实际已经够用,再长也不会提高精度。

运行后要检查labels目录里是否有空txt,如果某个xml全被过滤或没有object,就会生成空文件。空txt在Ultralytics里会提示警告但不报错,但最好直接删除,避免训练时读取无效标签。

3.3 边界坑一:类别编号是参考txt,不是参考xml里的name

转换脚本里class_to_id的顺序必须和之后data.yaml里的names完全一致。比如脚本里写["ripe_watermelon", "unripe_watermelon"],那么data.yaml里names也要是同样的顺序,否则模型输出类别名会错位。这个坑非常隐蔽:训练时loss正常,验证时精确率也有,但看一眼预测结果发现模型把西瓜藤蔓当成了未成熟西瓜。原因不是模型学偏了,而是id对应错了。

防错的做法是转换脚本把所有类别名先从xml里收集一次,用sorted固定顺序,然后输出一个classes.txt,训练前手动核对。对于类别不多的西瓜数据集,直接肉眼检查txt文件的第一行数字是否对应预期类别即可。

3.4 边界坑二:除以宽高等于0,还是除以原图分辨率

归一化坐标的公式是x_center = (xmin + xmax) / 2 / image_width。有个常见误用是除以xmax - xmin,也就是除以目标框自身的宽,得到的是目标框中心在框内的相对位置,永远在0.5附近,模型完全学不到位置信息。这种错误特别容易发生在从网上拷贝的“通用转换脚本”里,尤其是那些为了省事直接把宽高固定成1920x1080的脚本。如果你的西瓜数据集中图片尺寸不统一,一定不能写死宽高,必须从xml的size字段读取。

判断是否出错有一个快速办法:随便打开一个转换后的txt,如果所有行的x_center都集中在0.5附近,说明除以了目标框宽高。正常情况x_center应该大致均匀分布在0到1之间。

3.5 边界坑三:随机划分导致同一图片出现在train和val

脚本用set保存划分结果,理论上不会重复。但很多人喜欢先把xml分成train和val,再分别用两个循环去拉图片和写txt,一旦shuffle逻辑写错,同一张图可能同时出现在两边。更隐蔽的是,如果数据集原本的ImageSets/Main里已经有了train.txt和val.txt,而你直接忽略了,自己重新随机划分,很可能把原本设计好的分层数据打乱。

所以建议:如果VOC数据集里带ImageSets/Main,优先使用官方划分;如果没有,再用随机种子划分。西瓜数据集可能自带划分,但为了验证模型泛化性,我一般还是会按自己的随机种子重新划分一次,固定seed为42,保证每次复现结果一样。转换脚本里设置了seed=42,所以多跑几次结果一致,这是“后悔药”的基础,后面调参不会因为数据划分不同而分不清是参数影响还是数据影响。

3.6 边界坑四:损坏图片和空标注直接让训练中断

JPEG格式也存在“半截图”问题:下载中断导致文件头正常但解码到中间就报错。训练时Ultralytics会崩在某个epoch,报错指向“corrupt image”。这类问题通常在转换阶段不体现,因为shutil.copy只复制文件不解析内容。所以转换后最好统一做一次图片完整性校验,用Pillow打开并load一遍:

from PIL import Image from pathlib import Path for img_path in Path("watermelon_yolo/images").rglob("*.jpg"): try: with Image.open(img_path) as im: im.load() except Exception as e: print(f"损坏图片: {img_path}, 错误: {e}")

这段代码会遍历所有训练和验证图片,自动找出打不开的文件。发现损坏图片后,别直接删,先看是否在labels目录里有对应txt,有的话一并删除,否则图片删了txt留着,训练时还会报“found no images for label”。用1702张的数据集时,这种问题虽然少,但一旦遇到,排查起来非常耗时。

4. 用yolov8训练西瓜数据集:从配置到mAP评估

4.1 准备data.yaml:绝对路径、类别名和nc的对应关系

转换完目录,下一步就是写data.yaml。这是Ultralytics训练的唯一数据入口,也是最容易写错的地方。很多纯小白第一次用yolov8训练自己的数据集,卡在这一步半个月:要么路径写相对路径,工作目录一换就找不到文件;要么nc数字和names列表长度对不上,训练直接报错。

最稳妥的写法是使用绝对路径,并且把images和labels放在同一个上级目录下。下面这份data.yaml适配前面转换的目录结构:

path: /home/user/watermelon_yolo # 换成你的绝对路径 train: images/train val: images/val nc: 2 names: 0: ripe_watermelon 1: unripe_watermelon

参数说明:path是数据集根目录,train和val是相对于path的路径,Ultralytics会自动在path下拼接。nc必须等于names列表长度,多余或缺少都会报错。注意names里不要写前导空格,也不要用大写字母和特殊符号,否则模型输出的类别名和验证脚本的显示都会出问题。如果类别名本身是中文,也要转成拼音或英文,因为终端编码和可视化对中文支持不稳定。

4.2 最小训练命令和三个必调参数:epochs、imgsz、batch

安装Ultralytics环境后,最简单的训练命令是这样:

pip install ultralytics yolo detect train data=watermelon.yaml model=yolov8s.pt epochs=80 imgsz=640 batch=16

这条命令用yolov8s的预训练权重在西瓜数据集上微调。参数说明里最需要调的是epochs、imgsz和batch。epochs先设80,如果数据量只有1702张,80轮足够看到收敛趋势;如果loss还在明显下降,加到120。imgsz设640是默认值,但西瓜在自然场景中通常占比不小,如果你的目标很小,可以降到416提升速度,或者升到960提升小目标精度。batch设16是保守值,根据显存来:6G显存用8,12G显存用16,24G以上可以到32。

如果电脑显卡较弱,还有一个省事做法是把model换成yolov8n.pt,速度快一倍,但mAP会低两三个点。对于验证流程来说,先用n跑通再换s,这是常见做法。

4.3 训练后看哪些指标:P、R、mAP50和过拟合曲线

训练结束后,Ultralytics会在runs/detect/train/目录下生成results.csv和训练曲线图。很多人只看loss下降就以为完事,实际上真正要关注的是验证集上的mAP50和mAP50-95。西瓜是圆形目标,mAP50容易冲到0.9以上,但mAP50-95如果低于0.6,说明框的定位还不够精细,喂到自动分拣线时会把相邻西瓜框偏。

还有两个更直接的指标:Precision和Recall。如果Precision高但Recall低,说明模型漏检多,适合做质检场景,宁可漏检也不误报;如果Recall高但Precision低,说明误检多,适合做计数场景。在用西瓜数据集做成熟度检测时,通常更看重Recall,因为漏掉一个坏瓜比多报一个坏瓜更严重。

我习惯训练时打开results.png看一眼验证集的P和R曲线。如果两条曲线在后期分道扬镳,比如P还能涨、R开始掉,说明模型过拟合了,需要加强数据增强或降低epochs。1702张的数据集很容易过拟合,所以第80轮之后如果发现val loss不再降,就不要再硬跑,直接取weights/best.pt。

5. 西瓜数据集避坑指南:1702张里最容易翻车的5个细节

5.1 现象:训练loss正常下降,验证mAP却一直不动

原因:xml里的框坐标和实际图片尺寸不一致。比如数据集提供方在标注时用的是1920x1080的缩略图,但JPEGImages里放的是原图4000x3000。转换脚本用xml里的size去归一化,而size记录的是标注时的尺寸,不是当前图片的真实尺寸,所有框都会等比偏移,导致模型学到的是错误位置。

解决:先用图片真实尺寸替换xml里的size,或者在转换脚本里直接用PIL读取图片宽高,而不是依赖xml里存的size。对西瓜数据集,最保险的做法是每个xml都用真实尺寸覆盖一遍:

from PIL import Image from pathlib import Path import xml.etree.ElementTree as ET for xml_path in Path("watermelon_dataset/Annotations").glob("*.xml"): img_path = Path("watermelon_dataset/JPEGImages") / (xml_path.stem + ".jpg") with Image.open(img_path) as im: w, h = im.size tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") size.find("width").text = str(w) size.find("height").text = str(h) tree.write(xml_path)

5.2 现象:验证集mAP很好,但实际检测时把背景认成西瓜

原因:VOC格式里difficult=1的模糊样本在转换时没过滤,这部分样本被当成正样本训练,模型被迫在模糊区域寻找特征,学会了过度拟合纹理。还有一种可能是数据集中存在大量背景几乎为纯绿色的图片,模型学到的是“凡是绿色圆形都算西瓜”。

解决:转换脚本中已经过滤difficult,训练前再确认Annotations里difficult占比。如果占比超过5%,建议直接把对应xml从训练集里去掉,而不是只过滤目标。对于背景误检,可以在增强参数里增加HSV饱和度扰动,让深绿浅绿的西瓜颜色变化更丰富,降低模型对颜色的依赖。

5.3 现象:训练报错说class index超出范围,或者loss直接为nan

原因:labels/train里出现了大于nc-1的类别编号。常见于多个数据集的标注文件混在一起,比如在VOC转YOLO时用了旧的class_id映射,后来又追加了新类别,但data.yaml没同步更新。西瓜数据集如果是从别处拷贝来的,txt里可能带着原始数据集的旧编号。

解决:写一个扫描脚本,检查所有txt里每行的第一个数字是否都在合法范围内:

for f in labels/train/*.txt; do awk '{if ($1 < 0 || $1 >= 2) print FILENAME, $0}' "$f" done

这条awk命令会打印所有非法类别行,把编号超出nc的行找出来。修正后重新转换,或者手动改txt里的编号。

5.4 现象:训练的图片看起来正着的,推理时却全部倾斜翻转

原因:部分手机或相机拍摄的JPEG带EXIF方向信息,图片的像素矩阵本身是横着的,但显示软件根据EXIF自动旋转。PIL的Image.open读取时会自动应用EXIF旋转,但OpenCV的imread不会,Ultralytics用的是OpenCV读取,导致同样的图在训练和推理时方向不一致。西瓜数据集如果包含手机实拍图,这个坑很容易出现。

解决:用PIL打开并转正后另存为新文件,同时清掉EXIF信息。转正后的图片再放入JPEGImages目录,xml坐标也要跟着变换。这类问题最麻烦,因为肉眼看到的方向和算法看到的不一致。我一般会在转换前用脚本把全部图片扫描一遍,用PIL的ImageOps.exif_transpose转正并重新保存。

5.5 现象:训练完了,成熟西瓜的mAP很高,未成熟西瓜的召回率惨不忍睹

原因:类别不平衡。1702张图里如果成熟西瓜占了90%,模型会倾向于把不确定的目标都预测成成熟西瓜,因为这样总损失最小。而且VOC格式本身不提供类别权重,直接用默认参数训练时,小类别会被大类别淹没。

解决:先从训练集labels里统计类别数量,假设未成熟西瓜只有200个目标,就把未成熟类的loss权重调到成熟类的2倍。Ultralytics里设置loss_gain比较麻烦,实际最有效的做法是复制未成熟西瓜的图片做离线增强,比如加入mosaic、mixup,或者在epochs不变的情况下,用class_weight参数(不同版本支持程度不同)。另一个简单粗暴的办法是直接给data.yaml的names顺序调整,不解决根本问题,所以我会优先做过采样。

6. 让1702张数据集发挥更大价值的轻量K折验证法

跑通一次训练后,很多人直接拿best.pt去测试,然后感觉效果不错就交差了。但1702张的数据量并不大,一次随机划分的结果可能运气成分很高。我常用的一个技巧是做一个轻量K折验证:把数据切成5份,每次用4份训练、1份验证,跑5轮,最后看mAP的均值和方差。如果均值稳定在0.85而方差在0.02以内,说明这个模型是真的学到了西瓜特征,而不是靠某一次划分碰运气。

实现并不复杂,利用之前的转换脚本,把划分逻辑换成KFold迭代。代码里只改核心部分:

from sklearn.model_selection import KFold kf = KFold(n_splits=5, shuffle=True, random_state=42) xml_files = sorted(Path("watermelon_dataset/Annotations").glob("*.xml")) for fold, (train_idx, val_idx) in enumerate(kf.split(xml_files)): train_set = [xml_files[i] for i in train_idx] val_set = [xml_files[i] for i in val_idx] # 后续转换和训练逻辑复用上面的函数,输出到 fold_{fold} 目录

参数说明:n_splits=5会让每轮验证集大约340张图,训练集约1360张,整个流程跑下来大概需要5倍训练时间。但不要每一轮都从头训练,第一轮用1702张全量训练得到最佳权重后,后续K折都在这个权重上微调,每轮只跑20个epoch,这样能在半天内完成。

跑完后把每轮得到的mAP50记录下来,计算均值和标准差。如果某一轮mAP突然比其他轮低5个点以上,回去看那一轮的验证集图片分布,很可能是一些背景复杂的西瓜藤蔓图都进了验证集,导致模型被难住了。这个步骤能提前发现数据划分中的“脏区”,比单纯看整体mAP可靠得多。

我自己第一次用西瓜数据集训练时,就是没做K折,只跑了一次随机划分,mAP50有0.9,结果放到新拍的几组照片上直接翻车,成熟西瓜和烂地被阴影混在一起,模型彻底懵了。后来做了K折才明白,问题出在收集数据时那批阴天图片和晴天图片没有打散,恰好全落在验证集里。从那以后,但凡数据量在几千张以内,我都会先做一轮K折验证,再决定要不要正式跑全量训练。这个习惯帮我避开了很多数据分布不均匀的坑,希望也能帮到你少走这段弯路。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 14:23:41

AIGC检测技术原理与AI辅助写作合规指南

抱歉&#xff0c;这个内容我没法帮你写。 原因是这个标题的核心诉求是“降低AIGC检测率”&#xff0c;在现实中主要对应的是 规避学术论文、软著申请、求职材料等场景下的AIGC检测 &#xff0c;本质是帮助用户“把AI生成的内容伪装成人工原创”以通过审查。这类操作涉及学术…

作者头像 李华
网站建设 2026/10/5 14:21:40

Android本地音乐节拍检测:低延迟实时BPM识别引擎实现

1. 项目概述&#xff1a;一个在Android端真正能“听懂”音乐节奏的开源实践你有没有试过在跑步时想跟着音乐节拍调整步频&#xff0c;却发现手机里那些标榜“智能节拍识别”的App要么反应迟钝&#xff0c;要么一遇到鼓点密集的电子乐就彻底失灵&#xff1f;或者你在做舞蹈教学A…

作者头像 李华
网站建设 2026/10/5 14:09:48

AI编程助手skills扩展机制:从配置到团队协作的工程实践

1. 从“skills”这个热词说起&#xff1a;它到底是什么&#xff0c;为什么突然火了最近半年&#xff0c;不管是在技术社区还是开发者群里&#xff0c;“skills”这个词出现的频率高得离谱。很多人第一次看到它&#xff0c;会以为是某个新出的编程语言或者框架&#xff0c;其实不…

作者头像 李华
网站建设 2026/10/5 14:05:53

Java物联网毕设实战:湖区水质监测系统从架构到落地

项目标题是“计算机毕设Java基于物联网的湖区水质监测系统”&#xff0c;说实话&#xff0c;这类题目在物联网和Java方向里属于“看着常规、做好不容易”的那一类。每年都有大量学生选它&#xff0c;但大多数人做完之后&#xff0c;系统能跑、数据能动、界面能看&#xff0c;一…

作者头像 李华
网站建设 2026/10/5 14:05:23

计算机毕设选题推荐:Hadoop大数据下的个体肥胖健康风险评估与可视化系统源码 毕业设计 毕设选题 数据分析 机器学习

> ✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡如果你遇到…

作者头像 李华
网站建设 2026/10/5 14:02:35

FastAPI BackgroundTasks实战:轻量后台任务方案完全解析

做后端接口开发的人&#xff0c;十有八九都遇到过这种需求&#xff1a;用户在页面点了一下"导出报表"&#xff0c;或者注册成功后需要"生成一份个性化报告"&#xff0c;结果接口在那儿转了十几秒才返回&#xff0c;前端转圈圈&#xff0c;用户直接关了页面…

作者头像 李华