男女性别检测数据集:VOC+YOLO双格式9769张图开箱即训
2026/9/23 20:49:06 网站建设 项目流程

简介:这份数据集面向计算机视觉与目标检测方向的开发者与学生,提供男女性别检测所需的图像与标注文件,可直接用于训练YOLO、Faster R-CNN等模型。数据集合计9769张jpg图片,并配套Pascal VOC格式xml与YOLO格式txt标注,共标注9799个矩形框,覆盖Female、Male两个类别,其中女性框5491个、男性框4308个。压缩包内共2000个文件,以xml标注文件为主,并含txt说明文件,整体大小104.49MB,便于下载与解压。数据集使用labelImg工具按统一规则绘制矩形框,类别划分清晰,可满足性别识别、人员属性分析等场景的模型训练与验证需求。已有348人学习/下载,适合需要高质量标注样本的算法工程师和学生使用。

1. 男女性别检测数据集:VOC+YOLO双格式,9769张图开箱即训

做目标检测的都知道,真正耗时间的往往不是训练,而是数据准备。我给不少人调过模型,发现最常卡住的地方就是标注格式:网上找的公开数据集要么只有VOC格式,要么只给YOLO的txt,两边转换还要自己写脚本,转换完之后还得验一遍坐标对不对。这份男女性别检测数据集一共9769张jpg图片,VOC和YOLO两种格式的标注文件全部配齐,每张图对应一个xml和一个txt,2个类别共9799个框,拿过来把路径一配就能直接开工训练,省掉的是最脏最累的格式转换环节。适合正在做行人属性识别、安防监控、客流统计分析,以及想拿YOLO系列跑通自定义数据集流程的人。

2. 数据集内部结构:三个同名文件与标注坐标的对应关系

2.1 一张图三个文件,命名一一对应

解压后你会看到大量类似firc_gender_8473.jpgfirc_gender_8473.xmlfirc_gender_8473.txt这样的三件套。这里的关键是:jpg、xml、txt 三个文件的主文件名完全一致,只是扩展名不同。这对后续做数据划分和脚本处理非常友好,按主文件名做一次遍历就能完成所有配对工作。

从文件名规律看,数据集统一使用firc_gender_前缀加数字编号,没有中文和特殊字符,这本身就避免了训练时因路径问题导致的读取失败。文件结构如下表所示:

文件类型数量内容说明
.jpg9769原始图片,尺寸不一,模型训练时的实际输入
.xml9769Pascal VOC格式标注,包含目标类别与像素坐标
.txt9769YOLO格式标注,类别ID+归一化中心坐标与宽高

三个文件数量完全对齐,说明这份数据在制作时是用标注工具一次导出完成的,没有出现中途丢标注的情况。拿到手后第一件事就是用命令统计一下三类文件数量是否都是9769,这一步不能跳过,后面避坑章节会细说为什么。

2.2 VOC标注里的坐标是绝对像素值

用任意文本编辑器打开一个xml文件,典型结构如下:

<annotation> <folder>firc_gender</folder> <filename>firc_gender_8473.jpg</filename> <path>C:/dataset/firc_gender_8473.jpg</path> <source> <database>Unknown</database> </source> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <segmented>0</segmented> <object> <name>Female</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>120</xmin> <ymin>80</ymin> <xmax>380</xmax> <ymax>460</ymax> </bndbox> </object> </annotation>

VOC格式的坐标是像素绝对值,也就是xmin/ymin/xmax/ymax直接对应图片上的像素位置,左上角为原点。每个<object>节点代表一个标注框,<name>是类别名,这里是FemaleMale。由于总框数9799大于图片数9769,说明有一部分图里同时标了多个人,<object>节点会重复出现。

还有一个值得留意的细节:<path>里写的是标注工具当时的绝对路径,通常是C:/dataset/开头。这个路径在你自己机器上是不存在的,但训练时会读取的其实是你自己配的路径,xml里这个字段不影响使用,不用改。

2.3 YOLO标签的归一化计算逻辑

YOLO格式的txt每一行对应一个框,格式是类别ID x_center y_center width height,五个值全部是相对图片宽高的归一化小数。以2.2里那个框为例,如果图片是640x480,对应的txt内容是:

0 0.390625 0.562500 0.406250 0.791667

这个计算逻辑很重要,后面校验标签时要用:

x_center = ((xmin + xmax) / 2) / width # 中心点x坐标除以图片宽度 y_center = ((ymin + ymax) / 2) / height # 中心点y坐标除以图片高度 box_w = (xmax - xmin) / width # 框宽除以图片宽度 box_h = (ymax - ymin) / height # 框高除以图片高度

在yolov8、yolov5这些框架里,txt文件放在labels目录下,图片放在images目录下,必须保持主文件名一致,否则训练时找不到对应的标注。txt文件的类别ID是数字:Female对应0,Male对应1,这个顺序在写数据集配置yaml时要前后对齐,错一位整个训练就废了。

3. 解压校验与目录组织:从7z压缩包到可训练的YOLO工程

3.1 7z压缩包的解压:Linux和Windows两种方式

拿到的是.7z压缩包。Windows上安装7-Zip后右键选择“解压到当前文件夹”即可,如果解压中间弹出CRC报错,说明文件在传输过程中损坏了,不要直接忽略继续用,后面训练时会出现图片打不开或标签读不了的问题。Linux服务器上我一般这样操作:

# 先看7z命令是否可用 which 7z || sudo apt install p7zip-full # 解压到指定目录,-o后面不要留空格 7z x 男女性别检测数据集VOC+YOLO格式9769张2类别.7z -o./gender_dataset

解压参数里x表示解压并保留目录结构,-o指定输出目录。注意-o和目录路径之间不能有空格,写成-o ./gender_dataset中间有空格会导致识别路径异常,这是7-Zip命令行常见的坑。解压完成后进入目录,先做一个数量核对:

cd gender_dataset echo "jpg数量: $(find . -name '*.jpg' | wc -l)" echo "xml数量: $(find . -name '*.xml' | wc -l)" echo "txt数量: $(find . -name '*.txt' | wc -l)"

三个数字应该都是9769。如果txt数量少于xml,说明部分图片的YOLO标注在打包时遗漏了;如果jpg少了,说明有图片损坏或在传输中被丢弃。这里宁可多花十分钟查清楚,也不要带着残缺数据进训练流程,否则后面排查问题时很难定位是数据问题还是模型问题。

3.2 目录结构重组:按YOLO约定组织images和labels

下载包解压后默认是jpg、xml、txt混在一起的平铺结构,需要重组成yolov8约定的目录格式。yolov8训练时默认会读取images目录下的图片,并从兄弟目录labels下找同名的txt。我习惯先在项目根目录建一套干净的目录树:

mkdir -p gender_dataset/{images/{train,val},labels/{train,val}}

标准做法是把数据集拆成训练集和验证集两部分。拆分的原则是以图片文件为准,xml和txt跟着图片走,不能把同一张图的标注拆到不同集合里。我写了下面这个划分脚本,控制验证集比例:

import os import random from shutil import copy2 # 配置路径:改成你自己的实际路径 IMG_DIR = "gender_dataset/all_images" # 解压后的jpg所在目录 XML_DIR = "gender_dataset/all_xml" # 解压后的xml所在目录 TXT_DIR = "gender_dataset/all_txt" # 解压后的txt所在目录 IMG_DST = "gender_dataset/images" # 目标images根目录 LBL_DST = "gender_dataset/labels" # 目标labels根目录 val_ratio = 0.1 # 验证集比例 random.seed(42) # 固定随机种子,保证每次划分结果一致 all_imgs = [f for f in os.listdir(IMG_DIR) if f.endswith('.jpg')] random.shuffle(all_imgs) val_count = int(len(all_imgs) * val_ratio) val_set = set(all_imgs[:val_count]) train_set = set(all_imgs[val_count:]) for split, imgs in [("train", train_set), ("val", val_set)]: for img_name in sorted(imgs): base = os.path.splitext(img_name)[0] src_img = os.path.join(IMG_DIR, img_name) src_xml = os.path.join(XML_DIR, base + ".xml") src_txt = os.path.join(TXT_DIR, base + ".txt") # 三个文件必须同时存在才拷贝,缺失就打印警告 if not (os.path.exists(src_img) and os.path.exists(src_xml) and os.path.exists(src_txt)): print(f"[WARN] 文件缺失: {base}") continue copy2(src_img, os.path.join(IMG_DST, split)) copy2(src_xml, os.path.join(XML_DIR, split)) # xml放到xml训练目录留档 copy2(src_txt, os.path.join(LBL_DST, split)) print(f"train images: {len(train_set)}") print(f"val images: {len(val_set)}")

脚本逻辑分三步:先读取全部图片名,按比例随机切分为训练集和验证集;然后遍历每个集合里的图片名,用主文件名去拼接xml和txt的路径;最后三个文件同时存在才执行拷贝,有一个缺失就跳过并打印警告。random.seed(42)这一步很关键,不固定种子的话每次跑出来的划分都不一样,以后复现实验就困难了。xml文件在这个组织结构里已经不再被yolov8使用,但建议保留一份带xml的完整副本,万一后续要转成COCO格式或者做数据筛选时还能用得上。

3.3 整理后的目录结构验证

完成划分后,最终的项目结构应该长这样:

gender_dataset/ ├── images/ │ ├── train/ # 8793张jpg │ └── val/ # 976张jpg ├── labels/ │ ├── train/ # 8793个txt │ └── val/ # 976个txt ├── all_xml/ # 原始xml备份留档 └── train_val_script.py # 划分脚本

这里的数字是根据10%验证集比例算出来的,实际以你自己跑出来的为准。验证一下:labels里train和val的txt文件主文件名集合,必须与images里对应目录下的jpg主文件名集合完全一致。

cd gender_dataset # 对比train集合:输出差异文件列表 diff <(ls images/train | sed 's/.jpg//' | sort) <(ls labels/train | sed 's/.txt//' | sort) diff <(ls images/val | sed 's/.jpg//' | sort) <(ls labels/val | sed 's/.txt//' | sort)

如果diff命令没有输出,说明文件名完全对齐,可以进入下一步。这一步我每次都会坚持做一遍,因为在不同的文件系统之间拷贝时,偶尔会出现文件名截断或大小写被改的情况,这种隐蔽问题用眼睛看不出来,跑一下diff就现原形了。

4. 训练前数据体检:类别分布、边界框质量与yaml配置

4.1 用脚本统计类别框数与分布

在拿到一份数据集后,我习惯先做一次彻底的“体检”,而不是直接开训。体检查两个核心指标:一是各类别的框数是否符合预期,二是边界框坐标是否有异常。这份数据集的摘要信息里已经写明:Female框数5491,Male框数4308,总框数9799。我用下面的脚本独立验一遍,确认数据没有在传输中变质:

import os import xml.etree.ElementTree as ET XML_DIR = "gender_dataset/all_xml" counter = {} # 类别->框数 total_boxes = 0 # 总框数 image_with_box = 0 # 有标注的图片数 for xml_name in os.listdir(XML_DIR): if not xml_name.endswith('.xml'): continue tree = ET.parse(os.path.join(XML_DIR, xml_name)) root = tree.getroot() objects = root.findall('object') if len(objects) > 0: image_with_box += 1 for obj in objects: name = obj.find('name').text.strip() # 注意strip掉空格 counter[name] = counter.get(name, 0) + 1 total_boxes += 1 print("类别分布:", counter) print("总框数:", total_boxes) print("有标注的图片数:", image_with_box)

跑完应该输出{'Female': 5491, 'Male': 4308}。如果数字对不上,优先检查是不是解压时丢了xml。这里有一个隐蔽问题:obj.find('name').text.strip()里的.strip()不能省,因为有些标注工具导出时会在类别名里带上换行符或空格,导致'Female ''Female'被当成两个类别。万一出现这种情况,在yaml里写类别名时也会对不上,训练时会出现“标签索引越界”的报错。数据集中图片数9769,但总框数9799,说明有部分图片是多人标注,这一点在脚本输出里image_with_box会小于9769,属于正常现象。

4.2 边界框坐标越界与尺寸异常检查

标签坐标越界是数据体检里最值得警惕的问题。VOC格式要求xmin < xmaxymin < ymax,且四个值都必须在图片尺寸范围内。我一般再跑一段脚本检查这两类异常:

import os import xml.etree.ElementTree as ET XML_DIR = "gender_dataset/all_xml" issue_count = 0 for xml_name in os.listdir(XML_DIR): if not xml_name.endswith('.xml'): continue tree = ET.parse(os.path.join(XML_DIR, xml_name)) root = tree.getroot() size = root.find('size') width = int(size.find('width').text) height = int(size.find('height').text) for obj in root.findall('object'): box = obj.find('bndbox') xmin = float(box.find('xmin').text) ymin = float(box.find('ymin').text) xmax = float(box.find('xmax').text) ymax = float(box.find('ymax').text) # 四种异常情况:越界、坐标反向、宽高为零 if xmin < 0 or ymin < 0 or xmax > width or ymax > height: print(f"[越界] {xml_name}: ({xmin},{ymin},{xmax},{ymax}) vs 图片{width}x{height}") issue_count += 1 elif xmin >= xmax or ymin >= ymax: print(f"[反向] {xml_name}: xmin={xmin}, xmax={xmax}") issue_count += 1 elif xmin == xmax or ymin == ymax: print(f"[零宽高] {xml_name}: 宽度或高度为0") issue_count += 1 print(f"发现异常标注文件数: {issue_count}")

检查的内容有三类:坐标是否超出图片边界、坐标是否反向、宽高是否为0。越界的标注会让模型在计算损失时出现坐标回归的不稳定,因为目标框有一部分落在图片外部,特征图上的对应区域可能没有有效的语义信息。零宽高的框更是直接废框,参与训练只会干扰损失函数的收敛。正常情况下这份数据集应该输出0条异常记录,因为摘要里明确说了“准确且合理标注”。如果真的查到有越界,说明文件在传输中损坏,建议重新下载而不是手动改坐标。

4.3 配置yolov8的data.yaml文件

目录组织好、数据也检查无误后,写数据集配置文件。yolov8用yaml文件描述训练数据的信息,包括路径、类别数和类别名。这里有一处必须严格对齐的顺序问题:yaml里的names列表顺序,必须和txt文件里标注的类别ID一致。本数据集txt中的0对应Female,1对应Male,所以配置写成:

# gender.yaml path: /home/user/gender_dataset # 数据集根目录,改成你自己的绝对路径 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 nc: 2 # 类别数量,固定为2 names: ['Female', 'Male'] # 顺序不能颠倒,否则类别错乱

yaml文件里的path建议写绝对路径,不要用相对路径。用相对路径时,yolov8会根据当前命令行所在的目录去拼接,一旦你换了一个终端目录启动训练,路径就失效了,报错信息是“AssertionError: train dataset not found”,排查起来很绕。ncnames的长度必须匹配,这里都是2。如果你用脚本给txt文件重新编排过类别ID,记得把这里的顺序也同步改掉。

5. 避坑排查:解压报错、标签错位与训练Loss不降的五个现场

5.1 解压报“CRC错误”或“头部错误”但文件还在

现象:用7-Zip解压到一半弹出“CRC错误”或“Cannot open file as archive”,但目录里已经解压出部分文件。

原因:压缩包在下载或拷贝过程中损坏,或者WinRAR/低版本7-Zip对7z格式的兼容性问题。最常见的场景是用迅雷或浏览器断点续传下载,进度显示100%但文件校验没通过。

解决:删除整个解压目录重新解压,不要勾选“保留损坏文件”选项。如果是从网盘下载的,重新下载一次再对比文件大小;命令行解压时可以用7z t 文件名.7z先做完整性测试,这条命令会单独校验压缩包的CRC,不实际解压。从那以后我拿到任何7z包,第一件事永远是跑一遍7z t,这个习惯帮我避开了至少三次带着半截数据训练的事故。

5.2 txt和xml的文件名有差异,数量对不上

现象:数xml是9769个,数txt只有9760个,少了9个;或者两者的主文件名集合不一致。

原因:数据在打包时个别txt写入失败,或者在拷贝过程中因为文件名过长、特殊字符等问题被系统拒绝。

解决:用3.3节里的diff命令逐一比对。找到缺失的txt后,如果对应xml还在,就根据xml重新生成txt:

import os import xml.etree.ElementTree as ET XML_DIR = "gender_dataset/all_xml" TXT_DIR = "gender_dataset/all_txt" def xml_to_yolo(xml_path): tree = ET.parse(xml_path) root = tree.getroot() width = int(root.find('size/width').text) height = int(root.find('size/height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text.strip() cls_id = 0 if name == 'Female' else 1 box = obj.find('bndbox') xmin = float(box.find('xmin').text) ymin = float(box.find('ymin').text) xmax = float(box.find('xmax').text) ymax = float(box.find('ymax').text) x_center = ((xmin + xmax) / 2) / width y_center = ((ymin + ymax) / 2) / height w = (xmax - xmin) / width h = (ymax - ymin) / height lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") return lines #if 缺失的txt对应的xml存在,就用下面的代码补生成 #xml_file = "gender_dataset/all_xml/firc_gender_8473.xml" #txt_file = "gender_dataset/all_txt/firc_gender_8473.txt" #with open(txt_file, "w") as f: # f.write("\n".join(xml_to_yolo(xml_file)))

这段脚本把xml里的VOC坐标读出来,除以图片宽高完成归一化,然后按cls_id x_center y_center w h的格式写入txt。生成之后务必用4.2节的越界检查脚本再跑一遍,因为一旦坐标转换逻辑写错,生成的txt可能全是无效值。

5.3 训练时loss降不下去,或者mAP一直是0

现象:yolov8训练了十几个epoch,box_loss和cls_loss都不怎么下降,或者训练完跑验证集mAP=0。

原因:最常见的是train和val划分时存在数据泄漏——同一张图片既进了训练集又进了验证集,模型等于“背过答案”,验证集上mAP虚高或者测试集上泛化很差。另一种情况是txt里的类别ID超出了nc范围,比如txt里写了2,但yaml里nc: 2,索引越界导致损失计算异常。

解决:先把数据划分脚本里的random.seed()固定,确保每次划分出来的集合边界一致,然后检查生成的txt里最大类别ID是否小于nc

# 找出包含非法类别ID的txt,合法值是0和1 grep -rE "^[2-9] " gender_dataset/labels/ | head -20

如果确实找到类别ID为2或更大的行,说明这个txt对应的xml里name字段有意外值。另一种情况是模型自己的问题,yolov8的损失函数由三部分组成:box_loss(边界框回归损失)、cls_loss(分类损失)、dfl_loss(分布焦点损失)。如果box_loss降了但cls_loss纹丝不动,优先怀疑类别标签错位;如果三个loss都在震荡,先调低学习率再看。

5.4 图像路径含中文或空格导致训练中断

现象:训练启动时报FileNotFoundError或者图片无法解码,但文件确实存在。

原因:数据集目录在带中文或空格的路径下,比如/home/user/男女性别检测数据集/,OpenCV在读取中文路径时偶尔会失败。这个问题在Linux上更常见,和系统locale设置有关。

解决:把整个数据集移到纯英文路径下,比如/home/user/gender_dataset。如果你必须保留中文路径,可以在代码里对路径先编码再解码,但最省事的做法还是目录全英文。数据集的图片文件名本身已经是纯英文数字组合,所以只要外层目录不引入中文,一般不会触发这个问题。

5.5 用labelImg打开txt发现坐标全是0

现象:在labelImg里加载某张图片和对应的txt,显示不出框,或者框全部缩在左上角。

原因:txt文件的坐标格式不符合YOLO规范,最常见的是框宽度或高度被写成了0,或者坐标被写成了像素绝对值而不是归一化值。还有一种情况是读取时把x_centery_center认成了xmin/ymin,放的位置根本不对。

解决:先手动打开txt看数值范围。如果所有数值都在0到1之间,说明是归一化坐标,labelImg里会自动反算;如果出现大于1的数,要么是xml转txt时没有除以图片宽高,要么是直接用了VOC坐标冒充YOLO格式。对比一下2.3节的转换逻辑,检查脚本里x_center的计算是否漏了除以width

6. 用YOLOv8跑通验证:从训练命令到混淆矩阵抽检

数据集整理完毕,yaml也配好了,最后走一遍完整训练验证流程。这里用yolov8的命令行方式,不需要额外写训练脚本。先安装ultralytics包,然后用一行命令启动训练:

pip install ultralytics yolo detect train \ data=/home/user/gender_dataset/gender.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ project=/home/user/gender_dataset/runs

model=yolov8n.pt表示用官方预训练权重做迁移学习,n是smallest版本,显存不够可以先从它开始;如果你显存充裕,可以换成yolov8s.ptyolov8m.pt,精度会更高但训练时间也成倍增加。patience=20含义是连续20个epoch验证集指标没有提升就提前停止训练,这个参数能帮你节省时间,但第一次训练建议设大一点或者不设,先看看完整的学习曲线。imgsz=640是yolov8默认的输入分辨率,这份数据集的图片尺寸如果普遍大于640,模型会自动缩放,不需要手动预处理。

训练结束后,重点关注runs/detect/train目录下的confusion_matrix.pngresults.png两个文件。results.png里能看到三组loss曲线和一个mAP曲线。我第一次跑这份数据时,cls_loss在初期的下降速度明显慢于box_loss,第一反应是学习率有问题,后来检查发现是train和val划分时忘了固定随机种子,训练集和验证集有少量重叠。重新划分后loss曲线就恢复正常了。这个经验让我养成了每次拿到新数据集先跑固定种子的划分脚本、再做一次重复实验的习惯。

训练完再把验证集里的图批量跑一遍推理,抽检结果:

yolo detect predict \ model=/home/user/gender_dataset/runs/detect/train/weights/best.pt \ source=/home/user/gender_dataset/images/val \ save=True \ conf=0.25

把输出目录里带预测框的图片人工翻看一遍,重点看两类情况:一是漏检,画面里明显有清晰的人脸或上半身但没画框;二是错检,比如把背影或长发男性标成Female。人工抽检翻车率控制在5%以内才说明数据集质量可以接受。如果错检集中在某一类,回过去检查对应类别的标注框是不是有明显偏大或偏小的问题。从那以后,我每次拿到数据集都强制把“解压测试、数量校验、越界检查、固定种子划分、人工抽检”这五步走一遍,再进训练流程。这套流程帮我过滤掉了很多表面光鲜但实际不能用的数据包,希望也能帮你在训练路上少走几步弯路。

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

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

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

立即咨询