简介:面向医疗图像分析与机器学习研究者的帕金森病手绘螺旋/波浪数据集,包含来自健康人与帕金森病患者的完整预处理图像及YOLO格式注释,可用于目标检测模型训练与评估,辅助运动障碍模式识别与早期诊断研究。压缩包共2000个文件,其中1999个txt标签文件与1个yaml配置文件构成,整体约817.5MB;数据集内部按训练组与测试组划分,适合直接接入YOLO训练与验证流程。已有69人学习下载。除标准标注外,预处理环节包含图像标准化、尺寸调整与去噪,注释提供精确目标位置信息,便于复现原始研究中的实验结果;文件名以healthy和parkinson明确区分受试者类别,降低了数据筛选成本。该资源为医学图像分析与计算机视觉的交叉应用提供具体案例,无论是从事医疗辅助诊断的工程师,还是研究目标检测算法的学生,都能借助该数据集快速开展实验,验证模型在真实手绘轨迹上的识别效果。
1. 从手绘螺旋到 YOLO 检测:这份帕金森数据集帮你省掉哪一半工作量
大多数帕金森手绘数据集的整理思路是“分类”:给一堆螺旋或波浪图像,训练一个分类网络判断健康还是患病。这份数据集的思路正相反,它按 YOLO 目标检测的格式组织——每一张手绘图都配有归一化坐标的边界框和类别标签,标注细且干净,拿到手就能直接丢进 YOLOv8 训练。它的价值不在“多新颖”,而在于把医疗图像最脏的预处理和标注环节提前做完了:图像标准化、尺寸调整、去噪全部处理过,标注框也逐一核对,并且明确划分了训练组和测试组。适合做运动障碍分析的研究生、想用检测框架做医疗判读的 CV 工程师、以及想快速复现论文结果的人;它能让你省掉大半个数据工程阶段,把力气集中放在模型和验证上。下面我从文件结构、预处理、训练配置到避坑,按落地顺序把每个细节拆开讲。
2. 数据解剖:十个 TXT 文件的类别、编号与 YOLO 标注语义
2.1 螺旋与波浪为什么能暴露帕金森特征
手绘螺旋和波浪是临床上螺旋测试与波浪测试的图像化版本。让受试者沿给定轨迹画图,帕金森患者因为手部肌张力异常和运动迟滞,画出的轨迹会表现出很典型的异常:螺旋线间距忽宽忽窄,波浪的振幅与频率不再均匀,笔压不稳定、出现抖动锯齿。这些特征在疾病早期就已经存在,并且可以通过图像模式识别捕捉,所以它成了运动障碍分析领域里性价比很高的研究切入点。
把这类图像做成目标检测任务而不是常见图像分类任务,有一个实际好处:检测框不只告诉你“这张图是健康还是患病”,还能告诉医生“异常集中在画幅的哪个位置、置信度多高”。框的坐标对应绘图轨迹的分布区域,置信度则可以当作诊断概率的下界参考。这对辅助诊断工具的设计是很有价值的,这也是这份数据集愿意用 YOLO 格式发布的核心原因。
另外要注意,这里说的“目标”不是自然图像里的行人或车辆,而是整条手绘轨迹的外接主体。一张图里可能只有一个螺旋或一串波浪,也可能有多段轨迹,每一段都对应一个独立边界框。理解这一点,后面解析标注文件时才不会把“box 数量”和“图片数量”混为一谈。
2.2 十个文件的名字里藏着哪些信息
资源包里能看到十个标注文件,命名规律是“类别前缀_数字编号.txt”。
| 文件名 | 类别前缀 | 对应受试者类型 |
|---|---|---|
| parkinson_810.txt | parkinson | 帕金森患者手绘图 |
| parkinson_1790.txt | parkinson | 帕金森患者手绘图 |
| parkinson_1014.txt | parkinson | 帕金森患者手绘图 |
| parkinson_1825.txt | parkinson | 帕金森患者手绘图 |
| healthy_1722.txt | healthy | 健康对照手绘图 |
| parkinson_1936.txt | parkinson | 帕金森患者手绘图 |
| healthy_1712.txt | healthy | 健康对照手绘图 |
| parkinson_840.txt | parkinson | 帕金森患者手绘图 |
| parkinson_873.txt | parkinson | 帕金森患者手绘图 |
| parkinson_1857.txt | parkinson | 帕金森患者手绘图 |
一眼能看出两个信息。第一,帕金森文件明显多于健康文件,比例为 8:2,训练时需要处理类别不平衡,这一块我在第 4 章会展开讲。第二,数字编号从 810 到 1936 并不连续,说明它不是简单重编号后的结果,大概率是采集批次里生成的受试者编号。这份编号在 YOLO 训练里最重要的作用是“同名关联”,它必须能和图片文件名后半段精确对上。
我见过不少人把编号理解成“样本序号”,结果训练时每张图都配到了别人的标注,整个过程在静默地学垃圾。所以拿到这份资源的第一件事不是直接训练,而是先做一轮彻底的“文件名对账”。特别是同一个受试者可能画了螺旋又画了波浪,同一个编号会对应多张图,对账时要把相同前缀和相同数字的图片、标签全部列出来逐张确认。
2.3 YOLO 标注格式逐行解析
YOLO 的标注 txt 每一行表示一个目标框,固定五个字段:类别索引、框中心 x、框中心 y、框宽 w、框高 h。其中四个坐标值都归一化到 0~1,除以了图像宽高。类别索引从 0 开始,这份资源里的类别设计可视为二分类:0 对应帕金森,1 对应健康。框住的对象是整条手绘轨迹的外接矩形主体区域,不是画幅里的某个小局部。
先写一段脚本看看标注文件内部长什么样:
# 检查单个标注文件的内容与字段结构 file_path = "parkinson_810.txt" with open(file_path, "r", encoding="utf-8") as f: lines = [line.strip().split() for line in f if line.strip()] print(f"目标数量: {len(lines)}") for line in lines[:5]: cls = int(line[0]) x_c, y_c, w, h = map(float, line[1:]) print(f"class={cls}, cx={x_c:.3f}, cy={y_c:.3f}, w={w:.3f}, h={h:.3f}")字段顺序读法:先取line[0]作类别,再从line[1:]拆出四个浮点数。cx、cy 是边界框中心点在归一化坐标系里的位置,w、h 是框宽和框高。如果 w 或 h 出现大于 1 或小于 0 的值,基本可以断定文件被改动过;正常情况 w 和 h 应该在 0.2~0.8 这个量级,框住画幅主体而不会顶满整张图。
这份资源既然声称“完全预处理和注释”,说明这些 txt 已经经过一轮规范检查。但我的习惯是仍然跑一遍脚本,因为 YOLO 对格式顺序极度敏感,哪怕只是把 cx 和 w 写反,模型不会报错,只会慢慢收敛到一个“框永远偏一边”的奇怪状态。这种错误在医疗数据里最坑:loss 在降,指标也涨,但推理出来的框没有临床解释力。
2.4 用统计脚本给整个资源做一次体检
验证了格式之后,建议把所有文件的行数统统计一遍,确认每个文件对应多少目标,以及健康类和帕金森类的目标数量差异有多大。这一步相当于给数据集做体检,能提前发现很多训练期的隐患。
# 统计全部标注文件的行数分布 import glob for path in sorted(glob.glob("*.txt")): with open(path, "r", encoding="utf-8") as f: n = len([1 for line in f if line.strip()]) print(f"{path}: {n} 个标注框")这个脚本输出的是“每个文件的框数量”。正常情况下每个文件至少有一行,如果出现空文件,说明这张图的标注缺失或标注文件损坏,训练前必须处理。另外可以把帕金森类的总框数和健康类的总框数做个除法,得出一个大致的类别比例,这个比例会直接决定后面要不要补样本、要不要调类别权重。
3. YOLO 格式预处理实操:从原始绘图到 train/val/test 目录
3.1 数据清洗先做对账:删除空标注、统一后缀
预处理第一步不是图像前处理,而是“数据地址对不对”。YOLO 训练器按“同名不同后缀”找配对:图xxx.jpg找xxx.txt。你把图片复制到 images/train、标签复制到 labels/train 之后,任何一侧多一个文件都不会被报错,模型只会把缺标注的图当成背景样本,健康样本就被无声稀释了。这一步归入数据预处理里的数据清洗,看起来琐碎,翻车率反而最高。
我的清理顺序如下:
# 1. 统计 labels/train 下 txt 数量与 images/train 下图片数量 ls images/train | wc -l ls labels/train | wc -l # 2. 找出存在图片但没有对应 txt 的孤儿文件 for img in images/train/*.jpg; do base=$(basename "$img" .jpg) [ ! -f "labels/train/$base.txt" ] && echo "缺少标签: $base" done这段脚本的意图很简单:先“数对账”,再“逐一查”。wc -l只负责发现数量不一致,第二层循环才定位具体文件。真实项目里经常出现basename后缀写错的小问题,比如图是.jpeg而脚本里写的是.jpg,所以检查时我会把图片后缀统一成一种,再把四类常见后缀各跑一遍兜底。对完账之后,空 txt 直接删除,缺标签的图片要么补标要么移出训练集,不要留到训练阶段让 YOLO 静默处理。
3.2 图像预处理:灰度、自动对比度与统一边长
手绘扫描图的预处理,目标是“去背景、压噪声、统一输入口径”,而不是做花哨增强。螺旋和波浪本来就是细线条信息,过度处理会磨掉抖动细节,反而把诊断特征弄丢。常见做法是灰度化、自动对比度拉伸、轻度去噪,再把长边统一到某个固定尺寸,方便后续输入。
from PIL import Image, ImageOps, ImageFilter img = Image.open("sample.jpg").convert("L") # 灰度化 img = ImageOps.autocontrast(img, cutoff=2) # 自动对比度,去掉扫描底色 img = img.filter(ImageFilter.MedianFilter(3)) # 3x3 中值去噪,保留线条边缘 img.thumbnail((800, 800)) # 长边缩放到 800,保持宽高比 img.save("sample_prep.png")cutoff=2表示对比度拉伸时忽略两端各 2% 的像素,防止一小撮扫描噪点把色阶撑爆;MedianFilter(3)对孤立噪点有效,又不会像高斯模糊那样把笔迹边缘抹平;thumbnail保持宽高比缩图,白边留白不影响 YOLO,因为归一化坐标是相对整图算的。这里要说明,YOLO 训练时本身会把输入图再缩到 640,我先统一长边是为了让标注坐标的数值分布更稳定。
如果扫描图的底色不太均匀,比如纸张发黄或者有阴影,我会在灰度化之后再加一步二值化或自适应阈值处理,让笔迹与背景的边界更干净。但这个操作要小心:手绘线条本身有粗细变化和压力深浅,粗暴的单一阈值可能把浅色笔迹直接抹掉,导致标注框里的有效信息丢失。遇到颜色偏浅的图,宁可保留灰度也不要一刀切二值化。
3.3 目录结构搭建与训练/验证/测试划分
拿到资源后我习惯立刻把它改装成 YOLO 的标准目录形状,这样后面命令行一条都不用改:
pd_yolo/ ├── data.yaml ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/目录建好后,用脚本做划分。因为十个文件本来就按受试者命名,这里要守住一条原则:划分维度是文件,不是行。同一个受试者的所有手绘图必须落在同一个子集里,否则会出现数据泄漏——模型在训练时见过同一只手画的图,验证时再用这只手的图去评测,指标虚高得毫无意义。
import random, os, shutil random.seed(42) label_sources = [ "parkinson_810.txt", "parkinson_1790.txt", "parkinson_1014.txt", "parkinson_1825.txt", "healthy_1722.txt", "parkinson_1936.txt", "healthy_1712.txt", "parkinson_840.txt", "parkinson_873.txt", "parkinson_1857.txt", ] random.shuffle(label_sources) split = {"train": label_sources[:7], "val": label_sources[7:9], "test": label_sources[9:]} for subset, files in split.items(): for label_name in files: base = label_name.replace(".txt", "") img_src = f"images/{base}.png" label_src = f"labels/{label_name}" shutil.copy(img_src, f"pd_yolo/images/{subset}/{base}.png") shutil.copy(label_src, f"pd_yolo/labels/{subset}/{base}.txt")random.seed(42)让划分结果可复现,这是论文复现的基本要求;复制而不是移动,是为了保留原始资源不动,出了错有后悔药。有一点要提醒:如果这份资源的官方发布已经把 train/test 划分写死,那就不必自己再切,直接按官方目录放;我这里给的是“想重新做交叉验证”时的默认方案。验证集数量少是这类小数据集的通病,所以我通常会在训练完成后把 val 和 test 合并,只在做最终报告时才严格分开。
3.4 数据增强参数窗口:适合手绘线条的量级
YOLOv8 默认开启 mosaic、翻转、HSV 抖动等增强,但那套参数是为通用目标检测设计的,对手绘线条要重新踩一遍。我的经验值如下:水平翻转可以开,螺旋经过镜像后诊断语义不变;旋转控制在 15 度以内,再大就会把“螺旋的紧密程度”这个关键特征破坏掉;亮度对比度抖动开小档,让笔迹不至于被吞掉;mosaic 建议直接调低,因为螺旋主体是一整条轨迹,靠拼接会让模型学出“半个圆圈”这类伪模式。
这里特别说一下翻转。帕金森手绘要么是顺时针螺旋要么是逆时针螺旋,患者两只手画出来的方向可能不同。水平翻转会把顺时针变成逆时针,这在诊断上是允许的,因为医生判断的是线条间距和抖动频率,不分方向。但要避免垂直翻转和 180 度旋转混用,那会让波浪的起始端和结束端语义颠倒。
另外要留意“增强不代表加样本”。健康组只有 2 个文件,单靠增强撑不住训练稳定性,我会在训练前给健康图像做离线增强副本,比如每个健康原图派生 3~4 个旋转、平移版本,让训练时健康框的出现次数和帕金森大致相近。这个做法在检测任务里很常见,代价是训练集变大,但对类别不平衡的疗效很直接。做副本时记得同步复制对应的 txt,并保持文件同名,否则 YOLO 又会忽略这些增强样本。
4. YOLOv8 训练与损失收敛:医疗小数据集该有的配置
4.1 data.yaml 怎么写
YOLOv8 的训练入口是统一命令yolo detect train,它第一个要读的配置文件是 data.yaml。这个文件只做三件事:声明数据根路径、四个分组的相对路径、类别数。
# pd_yolo/data.yaml path: /path/to/pd_yolo train: images/train val: images/val test: images/test nc: 2 names: 0: parkinson 1: healthy路径建议写绝对路径,避免在不同机器上跑时因为相对路径解析不一致而白等一个 epoch。nc: 2和names必须和标注文件里的类别索引一一对应,这里 0 是帕金森、1 是健康。如果后续想加第三类“可疑”,需要同步改标注文件里的类别编号,而不是只改 names,否则索引错位会让所有框的类别都对不上。
4.2 训练启动与预训练权重选择
命令行可以这么起,用 n 版本权重做预训练起点:
yolo detect train \ model=yolov8n.pt \ data=pd_yolo/data.yaml \ epochs=100 \ batch=16 \ imgsz=640 \ patience=20 \ lr0=0.001 \ device=0yolov8n.pt 是官方预训练权重,第一次运行会自动下载;如果你的机器拉不动预训练权重,就提前下载后放到项目目录,再把model=指向本地路径,效果一致。epochs 我给 100,是因为手绘数据集体量小,模型很快就能收敛,跑 300 epoch 反而容易把线条噪声背下来。patience=20让它在验证指标连续 20 轮不更新时提前停,省时间也防过拟合。lr0=0.001是我在这种小数据集上的经验值,相比默认的 0.01 更稳。
显存紧张时先把 batch 调到 8,但不要低于 4。batch 太小会让 BatchNorm 的统计量抖动大,后面第 5 章会说到 NaN 问题。如果机器有 GPU,device=0直接使用;没有 GPU 就用 CPU 先跑 10 个 epoch 验证流程通不通,再换到服务器上全量训练。
4.3 三个损失函数:训练时到底在看什么
训练日志里常见box_loss、cls_loss、dfl_loss三项,我把它们对应的语义列一下:
| 损失项 | 全称/含义 | 收敛时的状态 |
|---|---|---|
| box_loss | 边界框坐标回归损失,衡量预测框与真实框的 IoU 偏差 | 稳定下降,最后在 0.8~1.2 区间浮动 |
| cls_loss | 分类损失,衡量类别判错的程度 | 下降到接近 0 后小幅波动 |
| dfl_loss | 分布式焦点损失,控制边界框边缘质量 | 缓慢下降,末尾容易出现锯齿 |
小数据训练里,三个损失的绝对值没有统一标准,不要拿别人的日志数字硬套;更有用的判断是三条曲线“趋势同步、没有明显背离”。如果 box_loss 在降而 cls_loss 长期不动,多半是类别分配有问题,回去检查标注类别索引;如果 dfl_loss 反复震荡,通常说明边界框标注本身存在噪声。这个数据集经过预处理,三个损失一般会比较平滑,一旦出现剧烈抖动,优先考虑是不是增强参数把线条切碎了。
4.4 类别不平衡:健康样本的补救顺序
刚才统计过,十个文件里 8 个帕金森、2 个健康,目标层面上健康样本天然处于劣势。如果不处理,模型会把“画幅里存在一条轨迹”当成帕金森特征,验证集准确率可能还挺高,但健康组召回率几乎为零。
我的处理顺序是:先做健康样本的离线增强副本,把健康图像数量补到与帕金森接近;再在训练时按类别加权采样,让健康样本被抽中的概率更高。
# 示意:按类别加权采样的逻辑 class_weights = {0: 1.0, 1: 3.0} # 类别1为健康,权重给到3权重给到 3 的含义是:健康样本在每一个 epoch 里被选中的机会是帕金森样本的约 3 倍。这样做的代价是健康样本的增强副本会被重复学习,容易过拟合到副本特有的噪声上。所以我不会把权重拉得太高,一般控制在 2~4 之间,同时配合提前停止。权重再高也不能替代真实样本,所以医疗向的判断里更多依赖精确率与召回率曲线,而不是单一准确率。类别平衡处理后,重点看 healthy 类的 recall 是否从 0 往上走,这才是这个项目有没有被救回来的标志。
5. 帕金森数据训练避坑指南:五个现场与补救办法
5.1 训练开始后 mAP 一直为 0,cls_loss 纹丝不动
现象:前十个 epoch 里三个损失都在下降,但 mAP50 始终是 0,验证集预测结果里连一个框都没有。
原因:最常见是 labels 目录和 images 目录的文件名没有对上。YOLO 按“同名找 txt”的规则运行,找不到就当作背景图,模型相当于在全是背景的数据上训练,自然一个框都吐不出来。
解决:先数文件数,再看零行标签。用find labels/train -name "*.txt" -size 0把空文件列出来,再用第 3.1 节的对账脚本找出孤儿图片。改好文件名之后重新训练,不需要换权重。这个坑我踩过两次,从那以后每次新数据集第一件事就是跑对账脚本。
5.2 健康样本在验证阶段被全判成帕金森
现象:训练完成后,模型在验证集上把 healthy 图几乎全部判成 parkinson,帕金森类 AP 却很高。
原因:这就是 8:2 类别不平衡的典型表现。模型把“背景块里存在一条螺旋线”当成了帕金森特征,对健康样本的同类特征没有区分能力;因为健康样本数量太少,损失函数里它的贡献也被稀释了。
解决:先按 4.4 的方式对健康类做副本增强和采样权重调整;同时把验证集的 healthy 样本单独列出来,逐张看预测置信度分布,确认健康类平均置信度是否明显低于帕金森类。如果仍然偏低,再考虑用验证集做一次阈值扫描,把置信度阈值抬高。这里需要接受一个事实:二分类里健康类的召回率永远是第一优先级,帕金森类的精确率次之,顺序不能反。
5.3 训练到一半 loss 变成 NaN:BatchNorm 崩溃
现象:训练到十几轮时,cls_loss 突然打印出 nan,随后所有指标清空,权重文件里的数值也变成 nan。
原因:在小数据集上用偏高的初始学习率配合小 batch,特征分布抖动过大,导致 YOLO 里的 BatchNorm 层统计量滚雪球式发散。这不是网络结构问题,是训练超参问题。
解决:调低 lr0 到 0.0005~0.001,把 batch 提到 16 以上,数据不够就顺手把 mosaic 关小,减少单 batch 内的特征分布剧烈变化。已经出现崩溃的直接用最后一次正常权重继续,不从头开始。判断“最后一次正常”有个笨办法:训练日志里找打印 nan 之前最近一个 epoch 的 best.pt 或 last.pt,用那个文件 resume。
5.4 旋转增强调太大,预测框开始跟着残影跑
现象:训练集上指标一路漂亮,验证集预测时框的位置却总偏,框住的不是螺旋主体而是图里一些零散的笔迹残影。
原因:增强参数过猛。有人把 rotate 开到 45 度,螺旋的“紧密程度”被切碎后,模型开始学“哪段线条像螺旋”而不是“整条螺旋在哪”,框自然就跑偏。
解决:rotate 控制在 15 度以内,mosaic 调得很低甚至关闭;同时保存增强后的中间样本图,肉眼抽查十张,确认没有出现半边螺旋、线条被拼接的情况。我给这类手绘数据集反复强调增强参数窗口,是因为手绘线条的语义信息非常脆弱,和自然图像里的人脸、车辆完全不是一个鲁棒性量级。
5.5 混淆矩阵总和对不齐,看着心慌
现象:训练报告里的 confusion matrix,每一行的百分比加起来不是 100%,有时多出 3% 到 5%。
原因:YOLO 的混淆矩阵计算里包含 background 类,未检出的目标会被算入背景;报告图里通常不画全背景这一行或列,所以行和看起来不对。
解决:不用纠结矩阵行和,直接看 precision、recall 和 mAP50-95 三个指标的一致性。如果实在需要矩阵做决策,就在预测结果里把conf=0的预测全量导出,用 sklearn 的 confusion_matrix 重新自己算一版。自己做矩阵时记得设置normalize='true',否则会看到一排大数字又被吓一跳。
6. 验证和部署小技巧:把模型输出变成接近临床的可信判断
6.1 测试集上先看哪几个数字
模型训练结束后,不要只看 mAP50。对医疗主题数据我固定看三件事:帕金森类的 recall 是否足够高、健康类的 precision 是否足够高、以及健康类测试样本的预测置信度有没有出现明显的双峰分布。双峰分布说明模型其实能区分两类,只是阈值没摆对;单峰说明特征没有学会,回去查数据和标注更划算。在这个数据集上我自己的期望是帕金森类 recall 不低于 0.9,健康类 precision 不低于 0.8,达不到就回去调预处理和类别平衡。
6.2 一条命令做出全部测试集预测
yolo predict model=runs/detect/train/weights/best.pt \ source=pd_yolo/images/test \ conf=0.5 \ save=Trueconf=0.5表示只保留置信度大于 0.5 的预测框。在这个项目里我会额外跑一遍conf=0.25,对比两次输出的差异。差异大说明模型对低置信目标不稳定,界面上就不要把低置信结果直接显示给医生;差异小说明 0.5 是合理阈值。保存出来的可视化图里,优先看健康类被误判成帕金森的样本,那才是最需要警惕的失败模式。
6.3 把输出的框转成“可疑区域提示”
实践里我的做法是让预测结果输出一张带框图和一份简单的置信度表,直接交给做临床对照的同事看,而不是让他们对着整个模型的内部黑匣子找信息。框坐标、置信度、类别索引三个字段,足够支撑“哪个区域、多大把握、什么性质”的判断。表头一般只用六列:图片名、类别、置信度、cx、cy、w。
从那以后,我每次跑医疗类 YOLO 数据都会强制走一遍固定流程:先对账文件名,再补健康样本,再看验证集按受试者划分的结果,最后把预测框可视化逐张过目才敢说模型可用。这套流程救过我不止一次,尤其是“验证集健康样本全被误判”这种看着指标挺好、一上真实场景就露馅的情况。希望这次的拆解能让你下载后少走几趟弯路,也希望帮到你。
本文还有配套的精品资源,点击获取