☰
步态数据集完全指南:从剪影序列解析到跨视角评测
2026/9/30 0:30:10 网站建设 项目流程

第一次打开步态数据集的压缩包,很多人会愣住:没有图像分类那种"一张图一个类别"的清爽结构,取而代之的是一堆以三位数字命名的文件夹,点进去是几百张灰度剪影,人形只有黑白两色,脚底下还带着毛刺。你本能地想点开一张看细节,结果发现单看一帧根本分不出是谁——这恰恰是步态数据集最核心的特征:它承载的信息不在单帧画面里,而在时间维度上,在一个完整的行走周期里。

步态数据集是步态识别这项任务的燃料。它把"一个人怎么走路"这种原本只能靠肉眼意会的东西,变成了有编号、有视角标签、有序列边界、可以喂进神经网络的矩阵集合。无论你是想复现一篇论文的 rank-1 指标,还是准备自己搭摄像头采一套数据做行人检索,第一步永远是搞清楚手里的数据集是怎么组织的、哪些字段是标签、哪些文件是干扰项。这篇就把我这些年翻过的坑、对过的目录结构、以及那些官方 README 里通常不会写的细节,一次讲清楚。

1. 步态数据集到底在存什么:一段走路视频被拆成了几层

1.1 步态作为一种生物特征的独特之处

人脸、指纹、虹膜这些都是"静态"生物特征,采集瞬间就能定身份;步态不一样,它是行为特征,必须观察一段时间才能成立。这带来两个直接后果:第一,数据集的存储单元不是"一张图"而是"一段序列";第二,同一个人的同一个序列,从不同帧切进去看,信息量完全不同——你从侧后方看一个人抬腿的瞬间,和看双脚并拢的瞬间,得到的判别力差得远。

更要紧的是,步态识别天然是远距离、非配合场景下的方案。人脸要正脸、要清晰度,指纹要接触,步态只要你在镜头里走过就行,几十米外、背对着镜头、光线一般,照样能采。这也是为什么步态数据集的采集场景往往长得像"监控视角",而不是像人脸库那样规整的正脸照。

从信号层面讲,一段步态数据里包含的信息大致三层:体型轮廓(身高、肩宽、腿长比例,属于静态属性)、运动模式(步频、步幅、摆臂幅度、身体起伏,属于动态属性)、外观干扰(衣服、包、鞋、光照,属于噪声)。所有步态数据集的设计动机,本质上都是想把这三层拆开——把前两层留下来当特征,把第三层单独标注成条件变量,让你能测试模型在换衣服、背包这些干扰下的鲁棒性。

1.2 一条样本的完整生命周期:从原始视频到对齐剪影序列

绝大多数公开步态数据集(尤其是剪影类的)从原始视频到最终交付给你的文件,中间经过了四道处理,理解这四道处理,比记住数据集有多少人重要得多。

第一道是前景分割。给视频逐帧做人体与背景的分离,输出前景掩膜。实验室内通常用背景建模,比如高斯混合模型对固定背景做建模再差分;野外场景就得借助人体解析或分割网络了。这一步的质量直接决定后面所有指标的上限——掩膜里带一条影子,或者腿之间连成一坨,模型学到的就是错的步态周期。

第二道是人体框裁剪与归一化。把前景的上下左右边界裁出来,然后按统一高度缩放,再把人体水平居中。这一步的目的是消除"摄像机远近"和"人在画面中的位置"带来的尺度差异。业内常见的做法是把归一化后的剪影缩到比较小的尺寸,比如实验室内小规模数据集常裁到 64×44 这种高度(高×宽),而人多、分辨率高的场景会保留到 128×88 甚至更大。

第三道是时序切分。一条完整的行走视频可能几十秒,一般会按固定帧数切成片段,常见的是每条训练片段采样 30 帧。采样方式有两种主流变体:一种是等间隔采样(业内常说的 L 型采样),按固定间隔从整段序列里均匀抽帧,保留完整周期;另一种是随机连续采样(M 型采样),从序列里随机取一段连续的 30 帧,用来模拟真实场景里"只拍到半个人走过去"的情况。

第四道是标注规范化。把"受试者编号、视角编号、行走条件、序列号"这些元信息编码到文件名或单独的标签文件里。这是最容易在读取环节翻车的地方,下一节专门讲。

1.3 剪影、骨架、点云:三种表示方式的取舍

拿到步态数据集之前,先想清楚你要用的是哪一种表示,因为不同表示对应的数据集完全不同,工具链也不通用。

表示方式数据形态优点代价典型代表
剪影序列灰度二值图(0/255)数据量小、处理快、方法成熟、复现容易丢失内部纹理,换衣干扰大,分割质量敏感CASIA-B、OU-MVLP、GREW
骨架序列每帧若干关键点坐标尺寸极小、对衣服和光照不敏感、可解释性强依赖姿态估计精度,遮挡下关节点漂移严重各类基于 OpenPose/HRNet 的衍生版本
3D 点云 / 参数化人体每帧点云或 SMPL 参数天然带深度信息,视角不变性好采集成本高、数据量大、公开集少Gait3D 等

剪影是绝对的主流。原因很现实:它把一个三维问题压成了二维二值图,存储和计算都便宜,而且二十年来积累的方法和基线足够多,你随便找个开源工具箱都能跑起来。骨架路线的优势在于抗外观干扰,缺点也明显——姿态估计本身会错,而步态识别要的恰恰是毫米级的肢体比例差异,关键点抖动几个像素就可能把两个人的距离拉近。3D 路线理论上最优,但公开可用的数据规模和场景多样性目前还撑不起工业化部署,多用于研究。

我的建议是:如果你是第一次做步态,老老实实从剪影开始。把剪影这条链路打通之后,再考虑引入骨架做多模态融合,收益会比一开始就上 3D 稳健得多。

2. 打开压缩包之前:目录结构与标注格式的读法

2.1 三种常见的目录组织范式

步态数据集的目录结构看起来五花八门,归纳起来就三种。

第一种是扁平式,所有剪影图堆在一个目录下,靠文件名编码全部信息。小规模实验室数据集多用这种,好处是加载快、不用递归遍历,坏处是文件数一多,某些文件系统在单个目录下放几十万个文件时会明显掉速。

第二种是两级或多级嵌套,先按受试者分文件夹,再按视角或条件分文件夹,最后才是帧图。这种结构人眼看着舒服,但读的时候必须写递归遍历,而且要小心不同层级的命名规则不一致。

第三种是视频直存式,数据集不给你剪影,只给原始视频片段,预处理全靠自己。野外大规模数据集常见这种形式,因为场景太复杂,官方也没法保证统一的分割质量。用这种数据集的时候,你的预处理管线质量会直接变成指标的一部分,和别人比数字时要格外小心。

不管你拿到的是哪一种,先别急着写 DataLoader,先花十分钟把目录树打出来看一眼。find . -maxdepth 3 -type d | head -50或者tree -L 3 -d,这一条命令能帮你省掉后面几个小时的调试。

2.2 文件名本身就是标签:解析规则实战

以最经典的 CASIA-B 为例,它的剪影文件名形如001-nm-01-000.png,四个字段依次是:受试者编号、行走条件、该条件下的序列序号、帧序号。行走条件用两个字母表示,nm是正常行走,bg是背包,cl是换外套。这种设计非常紧凑——一个文件名就把身份、条件、时序三件事全说清楚了。

我踩过的第一个坑就在这里:正则写太松。一开始我图省事,用split("-")拆字段,跑得好好的,直到换到另一个数据集,人家的文件名里受试者编号是五位、帧号不足位不补零,直接全乱套。后来我固定了写法:

import os, re from collections import defaultdict # 以 CASIA-B 命名风格为例:sss-cc-nn-fff.png FILE_PAT = re.compile(r"^(\d{3})-([a-z]{2})-(\d{2})-(\d{3})\.png$") def scan(root): records = [] for dirpath, _, filenames in os.walk(root): for name in filenames: m = FILE_PAT.match(name) if not m: continue # 不匹配的一律跳过并单独记录,别静默吞掉 subject, cond, seq, frame = m.groups() records.append({ "path": os.path.join(dirpath, name), "subject": subject, "condition": cond, "sequence": f"{subject}-{cond}-{seq}", "frame": int(frame), }) return records

第二个坑是编码和隐藏文件。有些数据集来自 Windows 环境,目录里会混进Thumbs.db、.DS_Store,还有中文路径的情况。正则匹配不上就跳过是安全的,但你得把跳过的文件名打印出来看一眼,否则一旦命名规则和你预期差一点点,你会以为数据集是空的。

2.3 先做一次数据体检,再动手训练

写完解析,我强烈建议先跑一遍统计,确认三件事:每个受试者有多少条序列、每条序列有多少帧、视角分布是否均匀。这三件事任何一项异常,都意味着要么你的解析错了,要么数据本身有缺失。

from collections import Counter, defaultdict def audit(records): per_seq = defaultdict(list) for r in records: per_seq[r["sequence"]].append(r["frame"]) lens = [len(v) for v in per_seq.values()] print("序列总数:", len(per_seq)) print("序列长度 min/median/max:", min(lens), sorted(lens)[len(lens)//2], max(lens)) print("受试者数:", len({r["subject"] for r in records})) print("条件分布:", Counter(r["condition"] for r in records)) # 抽查帧号是否连续,断帧往往意味着预处理脚本曾经崩过 bad = 0 for seq, frames in per_seq.items(): frames = sorted(frames) if frames != list(range(frames[0], frames[0] + len(frames))): bad += 1 print("帧号不连续的序列数:", bad)

这段代码看着朴素,但它抓到的问题都很致命。我见过序列长度中位数是 60、但最短只有 3 帧的情况——那几条短序列在训练时会因为凑不够采样帧数被反复复制,等于人为放大了它们对梯度的贡献。也见过帧号断档的,追查下去发现是当初解压的时候中断过一次。

注意:不要用"文件名排序"来代替"帧号排序"。0009.png和0010.png字符串排序没问题,但如果帧号不补零,10.png就会排在2.png前面,时序彻底乱掉。要么解析出整数帧号再排,要么一开始就统一补零重命名。

3. 公开数据集怎么选:规模、视角与条件变量的对照

3.1 小规模实验室数据集:适合跑通链路,不适合下结论

这类数据集的共同特点是人少、机位固定、背景干净、剪影质量高。它们的作用是让你在几十分钟内把整条训练链路跑通,验证代码没写错,而不是用来证明你的模型有多强。在 100 多人的规模上,rank-1 的置信区间宽得吓人——多对几个样本就能涨两个点,这种数字拿去做横向对比意义有限。

真正值得利用的是它们的条件变量设计。有的数据集会在同一个人身上采集正常行走、背包、换外套三种条件,这就给了你一个干净的对照组:模型在正常条件下的表现和背包条件下差多少,这个差值比绝对精度更有信息量,说明模型到底是在看步态还是在看轮廓外形。

使用这类数据集时,我建议把它当成单元测试:先在这个上面把数据加载、对齐、采样、损失函数、评测脚本全部打通,指标能对上论文的公开数字,再迁移到大集上。反过来做,在大集上调试 bug 会让你怀疑人生。

3.2 大规模多视角数据集:跨视角能力的试金石

多视角数据集是步态研究里最有价值的一类,因为它同时给了你"同一时刻、不同角度"的观测。这在真实场景里几乎不可能采到——你不可能在商场里围一圈摄像头拍同一个人。有了它,你才能做跨视角评测:用 90 度视角的样本做底库,用 0 度视角的样本做查询,看模型能不能认出来。

这背后的逻辑其实很朴素。步态特征里有一部分是视角相关的,比如侧面看能看到完整的步幅,正面看就只能看到肩宽和身体左右摆动。模型如果只学会了"侧面剪影长什么样",一换视角就废了。多视角数据集强迫模型去学那些视角不变的成分——身高的绝对比例、步频这类跟角度关系不大的量。

规模上,目前公开的大规模多视角数据集受试者在万人量级,视角数从早期的 11 个扩展到 14 个甚至更多。人数一多,受试者之间的体型分布就更接近真实人口,模型学到的特征才有泛化性。代价是训练成本——一万多人的数据集,光是把剪影读进内存做一次完整 epoch,就够你喝一壶的。

3.3 换衣、背包与野外场景数据集:难度阶梯的顶端

换衣服是步态识别最硬的一道坎。剪影类方法本质上是在比轮廓形状,一件长款羽绒服能把体型信息遮掉大半,一条阔腿裤能让腿型完全变样。所以专门针对换衣设计的数据集,通常会让同一批受试者穿多种差异很大的服装反复走,逼着模型去找那些衣服遮不住的线索——比如头肩比、走路时的重心起伏节奏。

野外场景数据集是另一个维度的挑战。它不做背景分离的"善意处理",直接给你真实监控环境下的视频:光照剧烈变化、有遮挡、有路人干扰、摄像机视角随意。这类数据集的规模往往很大,几百小时的视频、上万人的身份,但剪影质量参差不齐。用它的时候,你的预处理管线会变成影响指标的第一变量,和别人比结果时必须说清楚你用的是官方提供的剪影还是自己生成的。

3.4 一张对照表帮你快速定位

数据集受试者规模视角主要条件变量适合验证什么
CASIA-B百人级11 视角正常/背包/换衣链路跑通、跨视角基线
OU-MVLP万人级14 视角视角大规模跨视角泛化
USF HumanID百人级多机位视角/鞋/路面/包/时间早期方法对比、条件鲁棒性
TUM-GAID数百人单视角为主正常/背包/穿罩衣/换鞋,两次采集间隔数月时间跨度鲁棒性
CCPG百人级双视角四种服装换衣场景专项
SUSTech1K千人级11 视角视角/光照/携带/服装多因素复合干扰
GREW万人级野外无固定视角自然场景真实监控落地
Gait3D千人级数十机位三维点云/参数化人体3D 表示研究

提示:上表中的规模数字在不同论文里写法可能略有差异(有的按受试者算,有的按序列数算,有的按视频小时数算),引用前一定回到官方页面核对原始表述,别直接抄二手论文里的表格。

选型的思路可以简化为一条:先用百人级数据集验证代码正确性,再用万人级数据集验证泛化性,最后用野外数据集验证工程可行性。三个环节解决的问题完全不同,跳过任何一个都会在后面付出代价。

4. 评价协议:指标数字好看不代表模型真的好

4.1 闭集、开集与跨视角协议的区别

很多人第一次看论文表格会困惑:同样一个模型,为什么在两篇论文里的 rank-1 差了七八个点?十有八九是评测协议不一样。

闭集评测假设查询样本的身份一定在底库里。这是最简单也最乐观的设定,只需要算相似度排序,取 top-1 命中的比例。绝大多数实验室数据集论文用的是这个协议。

开集评测允许查询样本的身份不在底库里,模型必须同时判断"是不是库里的某个人"和"是谁"。它更接近真实部署,因为监控场景里绝大多数出现的人都不在目标名单上。对应的指标也变成了一对——既要看正确识别率,也要看误报率。

跨视角评测是步态特有的协议。底库和查询来自不同视角,且计算 rank-1 时要排除同视角的比对。为什么要排除?因为同视角的人和自己比几乎必然排第一,不排除的话指标会被虚高到接近 100%,这个数字毫无意义。这一条是新手最容易忽略的细节,我第一次复现论文时就因为忘了排除同视角,指标冲到 99% 还沾沾自喜,后来才发现是协议用错了。

4.2 Rank-1、mAP 与 TAR@FAR 各自说明什么

Rank-1是最常用的指标,含义是"相似度最高的那一个候选就是正确身份的样本占比"。它直观,但对底库里的"干扰项"质量很敏感——底库里如果有很多和你体型相近的人,rank-1 会明显下降。所以它更适合横向比较同一数据集上的不同方法,不适合直接当作部署能力的估计。

mAP(平均精度均值)考察的是整个排序列表的质量,而不是只看第一名。当底库很大、存在多个正确匹配时,mAP 比 rank-1 更能反映模型的整体排序能力。大规模野外数据集普遍会同时报 rank-1 和 mAP。

TAR@FAR是开集场景下的标准指标,读作"当误报率固定在某个阈值时,正确识别率能达到多少"。它必须成对出现才有意义——单独说"识别率 95%"而不说误报率,等于没说。常见的写法是 TAR@FAR=1e-3 或者 TAR@FAR=1e-4。

指标适用场景主要局限
Rank-1闭集、底库较小只看第一名,忽略排序质量
mAP闭集、底库大、多匹配对底库构成敏感,跨数据集不可比
TAR@FAR开集、真实部署评估必须成对报告,阈值选择影响大

4.3 几个常见的协议误用与复现偏差

第一类误用是训练集与测试集身份重叠。步态是身份识别任务,测试集里的人绝对不能出现在训练集里,否则模型是"背答案"而不是"学步态"。有些数据集给的划分文件里,训练和测试受试者是分开的,但如果你自己按序列随机切分,很容易把同一个人的不同序列分到两边,指标会虚高得离谱。我见过一个复现项目涨了十几个点,最后发现就是划分错了。

第二类误用是测试集调参。拿测试集的 rank-1 去选学习率、选采样帧数、选模型结构,本质上是在过拟合测试集。正确做法是从训练集里切出验证集,所有超参在验证集上定,测试集只跑一次。

第三类误用是底库构成不一致。有的协议底库每人只有一条序列,有的每人有多条,平均方式也不一样(先算每条查询的 rank-1 再平均,还是把所有查询混在一起算)。这两种算法在人数不均衡时会给出不同结果。跨论文比较时,一定要确认底库构造方式一致。

5. 自己采一套步态数据:从场地布置到标注质控

5.1 拍摄场地、机位与光照

自己采数据的第一步不是架相机,而是想清楚你要验证什么问题。如果只是验证模型在固定视角下的识别能力,一台正面或侧面的相机就够了;如果要研究跨视角,就得让受试者沿着不同方向走过同一片区域,或者布置多机位。

机位高度是个容易被忽略的细节。太高的俯角会把人体压扁,身高信息损失大;太低又容易被行人的脚遮挡。经验上相机高度在一米五到两米之间比较舒服,能保持人体轮廓的完整比例。相机到行走路径的距离要留足,让受试者在画面里的高度稳定在一个区间内,避免一会儿占满画面、一会儿只有几十像素高。

光照方面,最怕的是侧逆光造成的地面长影。影子会和人体前景连在一起,分割出来的人形就带了一条尾巴,用这种数据训练出来的模型对轮廓边界的敏感度会跑偏。如果只能在户外采,尽量选阴天或者让受试者背向光源行走。室内的话注意别让窗户直射造成的强对比出现在背景里。

5.2 采集流程与元数据记录

采集流程要标准化,否则后面做条件分析会做不下去。我的做法是准备一张采集清单表格,每录一段就打勾记录:受试者编号、性别、身高、年龄段、当次穿着的服装类型(上衣长度、裤子版型)、是否携带物品(背包/手提袋/无)、行走方向、机位编号、采集日期。这些元数据看着琐碎,但当你后面要分析"模型在长款外套下的性能退化"时,没有它们就两眼一抹黑。

行走方向上,最少要让每个人沿同一路径往返走三到五次。往返是有必要的——不同方向对应不同的起始相位,可以避免模型学到"总是从左脚开始"这种伪特征。如果条件允许,再补上几组不同步速的行走(正常、偏快、偏慢),步频是步态里判别力很强的维度,有变化的数据能让模型学到更稳的节奏表征。

元数据记录建议直接用 CSV 或 JSON,不要写在纸上。格式大致如下:

{ "subject_id": "S001", "session": "2024-03-11", "camera": "cam_front_01", "direction": "left_to_right", "clothing": "long_coat", "carrying": "backpack", "height_cm": 172, "notes": "地面有反光,已避开" }

5.3 标注、对齐与质量筛查

标注这块,如果受试者是你自己招募的,身份标签天然就是对的,问题主要出在序列切分上。一段连续录像里可能包含受试者走过去、转身、走回来的完整过程,你需要把它切成独立的序列。切分的边界建议放在"人完全离开画面"的时刻,而不是依靠时间戳硬切,否则会出现半个人、半个转身这种低质量片段。

对齐环节,下面这段代码是我常用的版本,逻辑是:先按前景的紧边界框裁紧,再等比缩放到固定高度,最后水平居中。注意缩放必须等比,如果强行把宽高都拉成固定值,胖瘦信息就被抹平了,而体型比例恰恰是步态判别的重要线索。

import cv2 import numpy as np def align_silhouette(mask, h=64, w=44): """mask: uint8 单通道,前景 255,背景 0""" ys, xs = np.where(mask > 127) if len(ys) < 50: # 前景太少,判定为无效帧 return None y0, y1, x0, x1 = ys.min(), ys.max(), xs.min(), xs.max() crop = mask[y0:y1 + 1, x0:x1 + 1] scale = h / crop.shape[0] # 按高度等比缩放,保留宽高比 new_w = max(1, int(round(crop.shape[1] * scale))) resized = cv2.resize(crop, (new_w, h), interpolation=cv2.INTER_NEAREST) canvas = np.zeros((h, w), np.uint8) if new_w >= w: # 太宽就居中裁掉两侧 start = (new_w - w) // 2 canvas[:, :] = resized[:, start:start + w] else: # 太窄就居中补零 start = (w - new_w) // 2 canvas[:, start:start + new_w] = resized return canvas

质量筛查必须做,而且要有量化标准,不能靠肉眼翻。我一般会统计三个量:前景面积占比(太小说明分割失败或人太远)、紧边界框的宽高比(正常人体在走行中大致在 0.4 到 0.8 之间波动,明显偏离说明前景里混进了非人体区域)、相邻帧之间的前景面积突变(突变超过 30% 往往意味着影子进出或分割抖动)。这三项筛一遍,能过滤掉八成以上的脏数据。

注意:不要因为追求"干净"就把所有异常帧全删掉。剪影的所谓"噪声"在一部分场景下就是真实分布,删得太狠会让模型在实际部署时完全不适应。我的做法是保留但标记,训练时通过采样权重降低它们的比例,而不是一刀切。

6. 训练阶段真正影响指标的几个细节

6.1 剪影质量与对齐误差的放大效应

剪影类的模型对对齐误差非常敏感,这一点在论文里很少被强调。原因是剪影本来就是二值图,没有纹理和颜色可供模型纠错,一旦水平居中偏了十几个像素,网络看到的"这个人"就整体位移了,学到的特征会把这个位移当成身份信息的一部分。

实测下来,水平居中的策略选择比缩放的插值方式重要得多。基于边界框中心对齐在大多数情况下够用,但当受试者背着一个单肩包的时候,边界框会偏向背包那一侧,导致人体本体偏出中心。更稳的做法是先用面积重心做粗对齐,再用边界框做微调;或者干脆在训练时加随机水平平移增强,让模型对几个像素的偏移不敏感。

另一个常见问题是脚部被裁掉。等比缩放到固定高度时,如果原始框的下边界没裁紧(比如背景里有一小块残留前景),缩放后人体的实际高度就变小了,脚可能溢出画布。这个 bug 表现得很隐蔽——损失曲线看着正常,指标就是上不去。排查方法很简单:随机抽 20 张对齐后的图存成拼图看一眼,脚是不是都在、头是不是都被切了,一眼就清楚。

6.2 时序建模与采样策略的匹配

步态的核心信息在时间轴上,所以采样策略必须和模型的时间建模方式匹配。用等间隔采样(覆盖完整行走周期)去训练一个靠局部时序建模的网络,容易浪费掉周期性信息;用随机连续采样去训练一个需要完整周期才能提取节奏特征的网络,模型会学得很吃力。

我的经验是:训练阶段用随机连续采样,测试阶段用等间隔采样。训练时随机采样能带来更强的鲁棒性,因为模型见过各种相位起点;测试时用完整周期采样,让模型的输入分布尽可能稳定,指标才会体现真实水平。这个训练/测试采样策略不一致的做法并不是"作弊",因为测试时你确实可以拿到完整序列,真实部署里也可以累积多帧再判定。

采样帧数也不是越多越好。帧数翻倍,显存和耗时基本线性上涨,但指标提升往往在某个点之后就趋于平坦。因为在固定帧数下,帧数越少,相邻帧的冗余越高;帧数越多,冗余越低但计算越贵。常见做法是在 30 帧左右找一个平衡点,然后通过实验确认增加帧数是否还有收益。

6.3 数据增强里哪些有用、哪些有害

步态数据集的增强手段和人脸、通用图像分类很不一样,照搬会出问题。

随机水平翻转是最有争议的一个。它确实能缓解过拟合,但步态是有左右手性的——一个人左右手的摆动幅度往往不对称,翻转之后相当于造了一个"镜像人"。在小数据集上做翻转增强,模型可能学到"镜像不变"的特征,这在真实场景下不一定成立。我的做法是:数据量小时保留翻转,数据量大时关掉,或者把翻转概率降到很低。

随机透视变换和轻微旋转是真正有价值的。它们模拟的是视角变化和相机安装误差,而视角变化恰恰是步态识别最难对付的干扰之一。在只有单视角的数据集上做透视增强,能明显提升跨视角泛化的表现。

随机擦除要谨慎。擦掉一块剪影,相当于人为制造了一个"断肢"样本,模型可能因此学会在不完整轮廓上做判断。如果一定要用,擦除区域要小、概率要低,并且只在训练后期开启。

时序层面的增强反而更值得投入:随机丢帧模拟丢包、随机时序裁剪模拟片段边界、轻微的时间缩放模拟步速变化。这些和真实场景的分布偏移更接近。

增强手段建议理由
随机水平翻转低概率或禁用破坏左右手性,可能引入伪不变性
随机透视变换推荐模拟视角变化,提升跨视角泛化
轻微旋转推荐模拟相机安装误差
随机擦除谨慎、小面积制造不完整轮廓,误导模型
随机丢帧/时序裁剪强烈推荐贴近真实丢包与片段边界
时间缩放推荐模拟步速差异

6.4 小样本条件下的训练技巧

很多人手里只有几百人的自有数据,这时候直接端到端训练一个大模型,几乎必然过拟合。我试过几种办法,效果从好到差排序如下。

最有效的是先在公开大规模数据集上预训练,再在自有数据上微调。步态特征的迁移性比想象中好——体型比例、步频这些底层线索是跨数据集通用的,哪怕采集设备完全不同,预训练权重也能带来明显收益。

其次是用度量学习而不是分类头。小样本下,直接做身份分类容易过拟合到具体的人;改成三元组损失或带间隔的对比损失,让模型专注在"同类靠近、异类推远"这个更泛化的目标上,验证集上的稳定性会好很多。这里有个关键细节:采样器必须保证每个 batch 里有足够的正样本对。如果随机采样导致一个 batch 里同一个人只出现一次,三元组损失根本构不成对,梯度是零,训练会静悄悄停摆。用按身份分层的采样器,每个 batch 从若干身份里各取几条序列,是最基本的配置。

第三是冻结底层、只训高层。剪影的底层特征(边缘、轮廓)跨数据集通用性很强,小数据量时冻结前面几层,只微调后面几层,收敛更快也更稳。

最后提醒一句:小样本实验里,提分最明显的往往不是模型改动,而是采样帧数和学习率调度。这两个超参值得多花时间搜一搜,收益比换主干网络来得直接。

7. 我在这条路上踩过的几个具体的坑

第一个坑是中文字符路径导致的静默失败。有次在 Windows 上整理数据,目录名带了中文,用某些库读图时返回空数组而不是抛异常,结果训练时整个 batch 的前景都是全黑的,模型照样在跑,损失照样在降——降的是随机猜的损失。后来我在数据加载里加了一条断言:assert mask.sum() > 0,一旦读到全黑图立刻报错。这个断言后来救了我好几次。

第二个坑是预处理脚本的幂等性。我写过一版对齐脚本,第一次跑正常,因为某个 bug 重跑了一次,结果把已经对齐过的图又对齐了一遍。因为输入本来就是 64×44,再裁一次边界框几乎等于没变,所以肉眼看不出来,但那些恰好被第一次裁掉边缘的样本,第二次又被裁掉了一点。这种累积误差在训练时表现为"少数样本怎么学都不对"。解决办法是给输出目录加标记文件,脚本启动时检查是否存在,存在就拒绝覆盖。

第三个坑是只看整体指标不看分组指标。有段时间模型整体 rank-1 涨了两个点,我以为进步了,拆开按条件看才发现是正常行走条件下涨得多,而换衣条件下反而跌了。整体指标掩盖了退化。从那以后我固定要按条件分组报指标,尤其是人数少的条件组,单独看一眼。

第四个坑是忽略了序列长度不均衡对损失的影响。按序列级别做对比学习时,长序列和短序列都被压缩成同样维度的表征,但短序列的序列内信息量天生就少,它的表征方差更大。如果不做长度归一化或者在采样时对齐长度,短序列会成为损失里的高噪声项,把训练方向带偏。我的处理是把长度低于一定阈值的序列在训练时降采样,测试时仍然全部评测。

第五个坑是没有固定随机种子。步态数据的采样环节有随机性(M 型采样),不同种子下同一配置的指标波动可能超过一个点。有一次我对比两个模型,A 比 B 高 0.8 个点,换了种子跑第二遍,B 又比 A 高 0.3 个点。后来我固定了三个种子取平均,实验结论才稳定下来。跑对比实验的时候,单次结果基本没有参考价值。

这五点里,我个人觉得最值得警惕的是第一个和第五个:一个会让你在错误的数据上跑出"看起来正常"的曲线,一个会让你在错误的结论上继续投入几周时间。数据加载环节的断言和实验的多种子重复,是我现在任何项目开始前都会先搭好的两道保险。

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

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

立即咨询