Human3.6M环境搭建与数据加载实战:从零跑通3D姿态估计 简介这份资源面向计算机视觉与机器学习方向的开发者尤其是正在入门或实践3D人体姿态估计的研究者与学生围绕Human3.6M这一标准数据集提供用Python获取与处理数据的完整工程代码。压缩包共14个文件约34KB以4个Python脚本为核心覆盖数据下载、处理与提取等环节另含txt校验与依赖说明、Dockerfile与docker-compose.yml容器配置、yml、xml元数据、md说明文档及license等便于快速搭建可复现的运行环境。资源已有3634人学习下载热度较高。读者可借助脚本完成HDF5数据读取、3D关节位置提取、格式转换与标准化并参考说明进行训练集、验证集、测试集划分为CNN、RNN或seq2seq等姿态估计模型的构建、训练与评估打好数据基础同时理解多视角、多动作数据的组织方式与工程化处理思路。1. 从一次数据加载失败说起Human3.6M 环境搭建到底卡在哪第一次在实验室服务器上跑 Human3.6M 的姿态估计训练我信心满满地装完 PyTorch结果卡在了数据加载这一步。报错信息翻来覆去就那几句找不到h36m目录、.npz文件解析失败、torch.utils.data的collate_fn直接抛异常。折腾了大半天才反应过来——问题根本不在模型代码而是这套数据集的环境搭建本身就有一堆前置条件目录结构要对、标注文件要转格式、Python 版本和依赖库版本还得对得上。Human3.6M 是 3D 人体姿态估计领域最常用的基准数据集之一做机器学习方向的同学几乎绕不开它但它的原始数据格式和主流框架之间隔着一层不薄的适配工作。这篇笔记记录的就是我从零搭好这套环境、跑通第一个数据加载脚本的完整过程适合刚接触 3D 姿态估计、正准备在本地或服务器上部署 Human3.6M 的开发者。2. 环境准备Python 版本、依赖库与目录结构2.1 版本选择与依赖清单Human3.6M 本身是 MATLAB 格式的标注文件Python 这边要读它核心依赖是h5py和numpy。但真正决定你能不能跑通的是深度学习框架的版本。我试过三种组合最后稳定下来的是 Python 3.8 PyTorch 1.10 CUDA 11.3。Python 3.9 以上在装h5py时偶尔会遇到 wheel 不匹配的问题Python 3.7 又太老部分新版的numpy已经不支持了。下面是我实际用的依赖清单直接写进requirements.txt# requirements.txt numpy1.21.6 h5py3.6.0 torch1.10.0 torchvision0.11.1 matplotlib3.5.3 tqdm4.64.1numpy锁在 1.21.6 是因为 1.22 之后np.float被彻底移除而 Human3.6M 的一些旧版处理脚本里还在用这个别名。h5py3.6.0 是我测下来读写.h5标注文件最稳的版本再新的版本在读取某些旧格式的 Human3.6M 标注时会报OSError: Unable to open file。torch和torchvision的版本要对应1.10 配 0.11.1 是官方验证过的组合。安装命令# 创建虚拟环境避免污染系统 Python python3.8 -m venv h36m_env source h36m_env/bin/activate # 安装 PyTorchCUDA 11.3 版本 pip install torch1.10.0cu113 torchvision0.11.1cu113 -f https://download.pytorch.org/whl/torch_stable.html # 安装其余依赖 pip install -r requirements.txt这里有个细节PyTorch 的 CUDA 版本要和服务器驱动匹配。我用的服务器驱动是 465.19支持 CUDA 11.3所以选了cu113的 wheel。如果你的驱动版本更低比如 450 系列那就得降到 CUDA 11.0 对应的 PyTorch 1.7.1。查驱动版本用nvidia-smi右上角那行CUDA Version就是上限。2.2 目录结构规划Human3.6M 原始数据集下载下来是一堆压缩包解压后目录很乱。我一般会先规划好一个固定的目录结构后面写 DataLoader 的时候直接按这个路径来省得每次改代码# 我用的目录结构 data/ ├── h36m/ │ ├── annotations/ │ │ ├── h36m_train.npz │ │ └── h36m_test.npz │ ├── images/ │ │ ├── s_01_act_02_subact_01_ca_01/ │ │ ├── s_01_act_02_subact_01_ca_02/ │ │ └── ... │ └── metadata.xml └── scripts/ ├── prepare_data.py └── dataset.pyannotations放转换后的.npz文件images放原始图像序列metadata.xml是数据集自带的相机参数和动作标签。这个结构不是官方强制的但社区里大部分开源项目的 DataLoader 都默认按这个来比如VideoPose3D和MotionBERT的预处理脚本。提前对齐能省掉后面改路径的麻烦。创建目录的命令mkdir -p data/h36m/annotations data/h36m/images data/scripts提示如果你的数据盘空间紧张images目录可以放到挂载的大容量盘上然后用软链接链回来。Human3.6M 的原始图像有几十 GB全量下载前先确认磁盘余量。2.3 验证 Python 环境是否就绪装完依赖别急着跑数据先验证一下核心库能不能正常导入# check_env.py import numpy as np import h5py import torch print(fnumpy version: {np.__version__}) print(fh5py version: {h5py.__version__}) print(ftorch version: {torch.__version__}) print(fCUDA available: {torch.cuda.is_available()}) print(fCUDA device: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU only})跑python check_env.py如果CUDA available输出True说明 PyTorch 和显卡驱动对上了。如果输出False先别往下走回去检查 CUDA 版本和驱动版本是否匹配。这一步偷懒后面训练时会在model.cuda()那里直接崩掉。3. 数据准备从原始标注到可加载的 npz 文件3.1 原始数据下载与解压Human3.6M 的原始数据需要从官方渠道获取下载下来是多个压缩包按受试者subject和动作action分卷。我一般会先下载标注文件和几个受试者的图像数据做测试不用一上来就拉全量。标注文件是h36m_annotations.rar或类似的压缩包解压后得到.h5文件和metadata.xml。解压命令# 假设下载的压缩包放在 ~/Downloads 下 cd ~/Downloads unzip h36m_annotations.zip -d ./h36m_raw # 如果压缩包是分卷的先合并再解压 cat h36m_images_part_* h36m_images_full.zip unzip h36m_images_full.zip -d ./h36m_raw/images解压后检查一下文件完整性标注文件应该有h36m_train.h5和h36m_test.h5两个图像目录下每个动作序列对应一个文件夹里面是.jpg格式的帧图像。3.2 标注文件格式转换原始.h5文件的结构比较绕直接读也能读但写 DataLoader 的时候不方便。我一般会先转成.npz格式把关键字段提取出来# prepare_data.py import h5py import numpy as np import os def convert_h36m_annotations(h5_path, output_path): 将 Human3.6M 的 h5 标注文件转换为 npz 格式 提取 3D 关节点坐标、2D 投影坐标和相机参数 with h5py.File(h5_path, r) as f: # 遍历所有受试者和动作 for subject in f.keys(): for action in f[subject].keys(): # 3D 关节点坐标形状为 (帧数, 32, 3) poses_3d f[subject][action][3d_joints][:] # 2D 投影坐标形状为 (帧数, 32, 2) poses_2d f[subject][action][2d_joints][:] # 相机外参 camera_param f[subject][action][camera_param][:] # 保存为 npz每个动作序列一个文件 save_name f{subject}_{action}.npz np.savez_compressed( os.path.join(output_path, save_name), poses_3dposes_3d, poses_2dposes_2d, camera_paramcamera_param ) print(fSaved {save_name}, 3D shape: {poses_3d.shape}) if __name__ __main__: convert_h36m_annotations( h5_pathdata/h36m/annotations/h36m_train.h5, output_pathdata/h36m/annotations/ )这段代码的核心逻辑是遍历.h5文件里的层级结构把每个动作序列的 3D 坐标、2D 坐标和相机参数分别提取出来存成独立的.npz文件。poses_3d的形状是(帧数, 32, 3)32 是关节点数量3 是 XYZ 坐标。poses_2d是投影到图像平面的坐标训练 2D 到 3D 的提升网络时会用到。camera_param包含相机的内外参数做多视角融合或者坐标变换时要用。注意不同版本的 Human3.6M 标注文件里字段名可能不一样有的用3d_joints有的用S。跑之前先用h5py打开文件看一眼结构f[subject][action].keys()打印出来确认字段名。3.3 写一个可复用的 Dataset 类转换完数据下一步是写 PyTorch 的Dataset类。这个类要能按索引读取单个样本返回 3D 坐标和对应的 2D 坐标# dataset.py import numpy as np import torch from torch.utils.data import Dataset import os class Human36MDataset(Dataset): def __init__(self, data_dir, splittrain, seq_len1): data_dir: npz 文件所在目录 split: train 或 test seq_len: 每个样本包含的连续帧数 self.data_dir data_dir self.seq_len seq_len self.file_list sorted([ f for f in os.listdir(data_dir) if f.endswith(.npz) and split in f ]) # 预加载所有数据到内存数据量不大时可行 self.data [] for fname in self.file_list: npz np.load(os.path.join(data_dir, fname)) self.data.append({ poses_3d: npz[poses_3d], poses_2d: npz[poses_2d], camera_param: npz[camera_param] }) def __len__(self): return len(self.data) def __getitem__(self, idx): sample self.data[idx] poses_3d torch.from_numpy(sample[poses_3d]).float() poses_2d torch.from_numpy(sample[poses_2d]).float() return { poses_3d: poses_3d, poses_2d: poses_2d }__init__里把每个.npz文件加载成一个字典存到self.data列表里。__getitem__返回单个样本的 3D 和 2D 坐标转成torch.float32张量。这里没有做序列切分如果要做时序模型可以在__getitem__里按seq_len截取连续帧。测试 Dataset 是否能正常加载from dataset import Human36MDataset ds Human36MDataset(data_dirdata/h36m/annotations/, splittrain) print(fDataset size: {len(ds)}) sample ds[0] print(f3D shape: {sample[poses_3d].shape}) print(f2D shape: {sample[poses_2d].shape})如果输出3D shape: torch.Size([帧数, 32, 3])说明数据加载链路已经通了。4. 避坑记录Human3.6M 环境搭建中最容易翻车的五个点4.1 h5py 读取报 OSError: Unable to open file现象用h5py.File(h36m_train.h5, r)打开标注文件时直接抛OSError文件路径没问题文件大小也正常。原因h5py3.7 及以上版本对某些旧版 HDF5 格式的兼容性变差了Human3.6M 的标注文件恰好是用旧版 HDF5 库生成的。解决降级到h5py3.6.0命令是pip install h5py3.6.0。如果已经装了新版先pip uninstall h5py再装。装完用python -c import h5py; print(h5py.__version__)确认版本。4.2 numpy 版本过高导致 np.float 报错现象跑数据预处理脚本时报AttributeError: module numpy has no attribute float。原因numpy1.22 开始移除了np.float、np.int这些别名但 Human3.6M 的一些社区处理脚本还在用。解决把numpy锁在 1.21.6或者手动把脚本里的np.float改成np.float64。我一般选前者因为改脚本容易漏。命令是pip install numpy1.21.6。4.3 CUDA 版本和 PyTorch 不匹配现象torch.cuda.is_available()返回False但nvidia-smi能看到显卡。原因装的 PyTorch wheel 是 CPU 版本或者 CUDA 版本和驱动不兼容。解决先用nvidia-smi查驱动支持的 CUDA 上限然后去 PyTorch 官网找对应版本的安装命令。比如驱动支持 CUDA 11.3就装torch1.10.0cu113。装完在 Python 里跑import torch; print(torch.version.cuda)确认。4.4 图像路径和标注文件对不上现象DataLoader 能加载标注但读图像时报FileNotFoundError。原因Human3.6M 的图像目录命名规则和标注文件里的动作名称不是一一对应的中间需要做一次映射。解决检查metadata.xml里的动作标签和图像目录名做对照。我一般会写一个映射字典把标注里的action名称转成图像目录的实际名称。这个映射关系在社区脚本里能找到参考但不同下载渠道的文件命名可能有差异以实际目录为准。4.5 内存不够导致加载中断现象__init__里预加载所有数据时进程被系统 kill 掉终端显示Killed。原因Human3.6M 全量数据加载到内存后占用几个 GB如果服务器内存紧张就会被 OOM killer 干掉。解决改成懒加载__getitem__里再读文件而不是在__init__里全读进来。或者用np.load的mmap_moder参数做内存映射不实际加载数据到内存。我一般会先估算一下数据量超过 2GB 就用懒加载。5. 装完怎么验证跑通一个最小训练循环环境搭好、数据加载通了最后一步是验证整条链路能不能跑起来。我一般会写一个最小训练循环用几行代码确认前向传播和反向传播都没问题# verify_pipeline.py import torch import torch.nn as nn from torch.utils.data import DataLoader from dataset import Human36MDataset # 加载数据集 ds Human36MDataset(data_dirdata/h36m/annotations/, splittrain) loader DataLoader(ds, batch_size4, shuffleTrue, num_workers0) # 定义一个极简的 2D 到 3D 提升网络 class SimpleLiftingNet(nn.Module): def __init__(self): super().__init__() self.fc nn.Sequential( nn.Linear(32 * 2, 256), nn.ReLU(), nn.Linear(256, 32 * 3) ) def forward(self, x): # x: (batch, 帧数, 32, 2) batch, frames, joints, _ x.shape x x.view(batch * frames, joints * 2) out self.fc(x) return out.view(batch, frames, joints, 3) # 初始化模型和优化器 device torch.device(cuda if torch.cuda.is_available() else cpu) model SimpleLiftingNet().to(device) optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.MSELoss() # 跑一个 epoch model.train() for batch_idx, batch in enumerate(loader): poses_2d batch[poses_2d].to(device) poses_3d batch[poses_3d].to(device) optimizer.zero_grad() pred_3d model(poses_2d) loss criterion(pred_3d, poses_3d) loss.backward() optimizer.step() if batch_idx % 5 0: print(fBatch {batch_idx}, Loss: {loss.item():.4f}) # 只跑几个 batch 验证流程 if batch_idx 20: break print(Pipeline verification passed.)这段代码的关键验证点有三个第一DataLoader能正常迭代说明数据加载链路通了第二model(poses_2d)能跑通前向传播说明输入输出维度对上了第三loss.backward()和optimizer.step()不报错说明反向传播和参数更新正常。如果这三步都过了环境就算搭好了。跑的时候注意num_workers先设成 0避免多进程加载数据时出一些玄学问题。等确认流程通了再改成 4 或 8 加速。另外batch_size设小一点4 就行验证阶段不需要跑全量数据。我一般还会额外检查一下poses_3d和pred_3d的数值范围如果 loss 一开始就是nan多半是数据里有异常值或者输入没归一化。Human3.6M 的 3D 坐标单位是毫米数值范围在几百到几千之间如果看到poses_3d里有inf或nan回去检查标注文件转换那一步是不是漏了字段。从那以后我每次搭新环境都会先跑一遍这个最小验证脚本确认数据加载、模型前向、反向传播三条链路都通了再开始写正式的训练代码。这个习惯帮我省掉了无数次「训练跑了一晚上才发现数据没加载对」的后悔药。希望帮到你。本文还有配套的精品资源点击获取