Pyskl训练避坑指南:如何解决自定义数据集训练中的常见错误

Pyskl实战避坑指南:从零构建自定义数据集训练流程

如果你正在尝试用Pyskl训练自己的动作识别模型,大概率已经踩过不少坑了。从数据准备到最终模型训练,这个看似简单的流程里藏着无数个可能让你卡住几天的陷阱。我花了整整两周时间,在多个数据集上反复试验,才把整个流程彻底跑通。这篇文章不会重复那些官方文档里已有的基础步骤,而是聚焦于那些真正让你头疼的实际问题——路径配置、分布式训练失败、PKL文件生成错误、环境依赖冲突等等。

Pyskl作为基于骨骼点的动作识别工具箱,确实为研究者提供了强大的算法支持,包括ST-GCN、ST-GCN++、PoseC3D等多种模型。但当你真正开始用自定义数据集训练时,会发现官方示例和实际应用之间存在巨大的鸿沟。下面我将按照实际操作的顺序,逐一拆解每个环节的常见问题和解决方案。

1. 环境配置:版本兼容性这个隐形杀手

环境配置是Pyskl训练的第一个拦路虎。官方文档给出的安装命令看似简单,但当你真正执行时,各种版本冲突会让你怀疑人生。

1.1 关键依赖版本锁定

Pyskl对各个依赖包的版本极其敏感。我测试过多个组合,最终确定了一套稳定的版本配置:

# 核心依赖版本
torch==1.10.0+cu113
torchvision==0.11.1+cu113
mmcv-full==1.3.18
mmdet==2.23.0
mmpose==0.24.0
mmcv==1.3.18

注意:这里有个关键细节——mmcvmmcv-full需要同时安装,但版本必须完全一致。很多教程只提mmcv-full,实际上mmcv也是必需的。

如果你使用conda环境,建议按以下顺序安装:

# 1. 创建并激活环境
conda create -n pyskl python=3.8
conda activate pyskl

# 2. 安装PyTorch(注意CUDA版本)
pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html

# 3. 安装MMCV系列
pip install mmcv==1.3.18
pip install mmcv-full==1.3.18 -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10.0/index.html

# 4. 安装其他MM系列
pip install mmdet==2.23.0
pip install mmpose==0.24.0

# 5. 安装Pyskl
git clone https://github.com/kennymckormick/pyskl.git
cd pyskl
pip install -r requirements.txt
pip install -e .

1.2 验证安装是否成功

安装完成后不要急着跑训练,先验证几个关键功能:

# 测试脚本:check_installation.py
import torch
import mmcv
import mmdet
import mmpose
import pyskl

print(f"PyTorch版本: {torch.__version__}")
print(f"CUDA可用: {torch.cuda.is_available()}")
print(f"GPU数量: {torch.cuda.device_count()}")

# 测试MMDetection
from mmdet.apis import init_detector
print("MMDetection导入成功")

# 测试MMPose
from mmpose.apis import init_pose_model
print("MMPose导入成功")

# 测试Pyskl
from pyskl.datasets import PoseDataset
print("Pyskl导入成功")

运行这个脚本,确保所有导入都不报错。如果出现ImportError,通常是版本不匹配或安装不完整。

1.3 常见环境问题及解决方案

我在不同机器上部署时遇到过各种奇怪的问题,这里整理了几个典型的:

问题1:libcudart.so.11.0: cannot open shared object file

# 解决方案:检查CUDA版本并设置环境变量
echo $CUDA_HOME  # 应该显示CUDA安装路径
export LD_LIBRARY_PATH=/usr/local/cuda-11.3/lib64:$LD_LIBRARY_PATH

问题2:ModuleNotFoundError: No module named 'mmcv._ext'

这是mmcv-full编译失败导致的。重新安装时指定正确的版本:

pip uninstall mmcv mmcv-full -y
pip install mmcv==1.3.18
pip install mmcv-full==1.3.18 --no-cache-dir

问题3:分布式训练相关错误

如果你不打算用多GPU训练,可以在代码中禁用分布式相关功能。但如果你需要多GPU训练,必须确保NCCL正确安装:

# 检查NCCL
python -c "import torch.distributed as dist; print('NCCL可用:', dist.is_nccl_available())"

2. 数据集准备:结构决定成败

数据集的组织方式直接影响后续所有流程。Pyskl对数据格式有严格的要求,但文档中的说明往往不够详细。

2.1 推荐的文件结构

经过多次试验,我推荐以下目录结构:

custom_dataset/
├── videos/
│   ├── train/
│   │   ├── class_0/
│   │   │   ├── video_0001.mp4
│   │   │   ├── video_0002.mp4
│   │   │   └── ...
│   │   ├── class_1/
│   │   │   ├── video_0001.mp4
│   │   │   └── ...
│   │   └── ...
│   └── val/
│       ├── class_0/
│       │   ├── video_0001.mp4
│       │   └── ...
│       └── ...
├── annotations/
│   ├── train.json
│   ├── val.json
│   └── video_list.txt
└── processed/
    ├── train.pkl
    └── custom_xsub.pkl

这种结构的好处是清晰分离原始视频、标注文件和中间处理结果。当某个环节出错时,你可以快速定位问题所在。

2.2 标注文件生成脚本

官方提供的custom_2d_skeleton.py需要输入一个视频列表文件。我写了一个更通用的生成脚本:

# generate_annotation_files.py
import os
import json
from pathlib import Path

def generate_annotations(video_root, output_dir):
    """
    生成Pyskl所需的标注文件
    
    Args:
        video_root: 视频根目录,包含train/和val/子目录
        output_dir: 输出目录
    """
    video_root = Path(video_root)
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)
    
    # 生成train.json和val.json
    for split in ['train', 'val']:
        split_dir = video_root / split
        annotations = []
        
        # 遍历所有类别
        for class_idx, class_dir in enumerate(sorted(split_dir.iterdir())):
            if not class_dir.is_dir():
                continue
                
            # 遍历该类别的所有视频
            for video_file in sorted(class_dir.glob('*.mp4')):
                annotation = {
                    'frame_dir': video_file.stem,  # 视频文件名(不含扩展名)
                    'label': class_idx,
                    'vid_name': str(video_file.relative_to(video_root.parent)),
                    'total_frames': 0,  # 稍后填充
                    'img_shape': (0, 0),  # 稍后填充
                    'original_shape': (0, 0),
                    'num_person_raw': 0,
                    'keypoint': [],
                    'keypoint_score': []
                }
                annotations.append(annotation)
        
        # 保存为JSON
        output_file = output_dir / f'{split}.json'
        with open(output_file, 'w') as f:
            json.dump(annotations, f, indent=2)
        
        print(f"生成 {split}.json: {len(annotations)} 个样本")
    
    # 生成video_list.txt(用于custom_2d_skeleton.py)
    video_list = []
    for split in ['train', 'val']:
        split_dir = video_root / split
        for class_dir in sorted(split_dir.iterdir()):
            if not class_dir.is_dir():
                continue
            for video_file in sorted(class_dir.glob('*.mp4')):
                # 格式: 视频路径 标签
                video_list.append(f"{video_file} {class_dir.name}")
    
    list_file = output_dir / 'video_list.txt'
    with open(list_file, 'w') as f:
        f.write('\n'.join(video_list))
    
    print(f"生成 video_list.txt: {len(video_list)} 个视频")

if __name__ == '__main__':
    # 使用示例
    generate_annotations(
        video_root='/path/to/your/videos',
        output_dir='/path/to/output/annotations'
    )

这个脚本会自动处理类别标签,并生成符合Pyskl要求的格式。

2.3 视频预处理注意事项

在准备视频数据时,有几个细节需要特别注意:

  1. 视频格式统一:尽量使用MP4格式,编码为H.264。其他格式可能导致decord库读取失败。
  2. 分辨率一致:建议将所有视频调整为相同分辨率,如256x256或320x240。不一致的分辨率会导致骨架提取时出现问题。
  3. 帧率处理:如果视频帧率差异较大,建议统一采样到30fps。
  4. 视频长度:过短的视频(少于16帧)可能无法有效训练,建议过滤掉。

我写了一个简单的视频检查脚本:

# check_videos.py
import cv2
from pathlib import Path

def check_video_properties(video_path):
    """检查视频属性"""
    cap = cv2.VideoCapture(str(video_path))
    if not cap.isOpened():
        return False, "无法打开视频"
    
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    
    cap.release()
    
    if fps < 10:
        return False, f"帧率过低: {fps}"
    if frame_count < 16:
        return False, f"帧数过少: {frame_count}"
    
    return True, {
        'fps': fps,
        'width': width,
        'height': height,
        'frame_count': frame_count
    }

def batch_check_videos(video_dir):
    """批量检查视频"""
    video_dir = Path(video_dir)
    issues = []
    
    for video_file in video_dir.rglob('*.mp4'):
        ok, result = check_video_properties(video_file)
        if not ok:
            issues.append(f"{video_file}: {result}")
        else:
            print(f"✓ {video_file}: {result}")
    
    if issues:
        print("\n发现以下问题:")
        for issue in issues:
            print(f"  - {issue}")
    else:
        print("\n所有视频检查通过")

3. 骨架提取:custom_2d_skeleton.py的陷阱与修复

custom_2d_skeleton.py是Pyskl中用于从视频提取2D骨架的关键脚本,但原始版本存在多个问题。

3.1 分布式训练问题的根本解决

原始脚本默认启用分布式处理,但在单GPU或CPU环境下会报错。以下是修改后的关键部分:

# 修改custom_2d_skeleton.py中的main函数
def main():
    args = parse_args()
    assert args.out.endswith('.pkl')
    
    lines = mrlines(args.video_list)
    lines = [x.split() for x in lines]
    
    # 解析视频列表
    if len(lines[0]) == 1:
        annos = [dict(frame_dir=osp.basename(x[0]).split('.')[0], 
                     filename=x[0]) for x in lines]
    else:
        annos = [dict(frame_dir=osp.basename(x[0]).split('.')[0], 
                     filename=x[0], 
                     label=int(x[1])) for x in lines]
    
    # 关键修改:禁用分布式
    rank = 0
    world_size = 1
    
    # 注释掉所有分布式相关代码
    # if 'RANK' not in os.environ:
    #     os.environ['RANK'] = str(args.local_rank)
    #     os.environ['WORLD_SIZE'] = str(1)
    #     os.environ['MASTER_ADDR'] = 'localhost'
    #     os.environ['MASTER_PORT'] = '12345'
    # init_dist('pytorch', backend='nccl')
    # rank, world_size = get_dist_info()
    
    # 创建临时目录
    if rank == 0:
        os.makedirs(args.tmpdir, exist_ok=True)
    
    # 处理所有数据(单进程)
    my_part = annos
    
    # 初始化检测和姿态估计模型
    print("初始化检测模型...")
    det_model = init_detector(args.det_config, args.det_ckpt, 'cuda')
    assert det_model.CLASSES[0] == 'person', '需要COCO预训练的人体检测器'
    
    print("初始化姿态估计模型...")
    pose_model = init_pose_model(args.pose_config, args.pose_ckpt, 'cuda')
    
    # 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值