资讯详情

ST-GCN骨骼动作识别实战:从NTU数据预处理到训练推理全流程

📅 2026/10/1 10:36:45 | 华诺云谱 👁 阅读
ST-GCN骨骼动作识别实战:从NTU数据预处理到训练推理全流程
简介这份资源面向计算机、数学、电子信息等专业的学生与研究者提供基于时空图卷积ST-GCN的骨骼动作识别完整Python实现可直接用于课程设计、期末大作业或毕业设计也适合作为深度学习与图神经网络方向的学习参考。压缩包共91个文件约52.6MB包含29个py源码、13个yaml配置、12个pyc编译文件、11个gif演示动图、9个txt说明、5个png图片、3个pt权重、3个mp4视频及prototxt、sh脚本等覆盖模型定义、数据生成、训练配置与推理演示各环节。目录中可见st_gcn与st_gcn_twostream网络结构、feeder数据加载、processor处理流程、demo_offline与demo_realtime演示脚本以及NTU-RGB-D和Kinetics骨骼数据生成工具并附带预训练权重与参考模型说明。已有275人学习下载读者可借此理解骨骼动作识别的数据组织、图卷积建模与双流网络设计并在此基础上调试、扩展自己的实验方案。1. 骨骼动作识别为什么值得用 ST-GCN 拆一遍如果你手头有一段人体骨架序列——不管是 Kinect 采的、姿态估计器吐的还是 NTU RGBD 里现成的——想把它分类成挥手坐下喝水这类动作最直接的想法是把每帧关节坐标拉平成一个长向量丢给 LSTM 或全连接。我最早也这么干过结果在跨被试测试上准确率掉得很难看同一个动作换个人做、换个机位拍关节坐标的绝对数值全变了模型学到的其实是这个人站在画面哪个位置而不是他在做什么。ST-GCN 换了个思路。它把骨架看成一张图关节是节点骨骼是边再在时间维度上把相邻帧的同一关节连起来于是整段动作变成一张时空图。图卷积负责在空间上聚合相邻关节的信息时间卷积负责在帧之间滑动聚合两层交替堆叠模型关注的就是哪些关节在怎么动而不是关节在画面里的绝对坐标。这个性质让它在 NTU RGBD 这类数据集上跨被试X-Sub和跨视角X-View两个协议下都能拿到比早期方法高出一截的结果也是它成为骨骼动作识别基线的原因。这份资源就是 ST-GCN 的一套完整 Python 实现带训练、推理、离线 demo 和实时 demo还附了 NTU-RGB-D 和 Kinetics 两套数据生成脚本。适合两类人一类是毕业设计或课程设计要交东西、需要能跑起来看到结果的另一类是想拿它当骨架做二次开发、换自己的数据集或改网络结构的。下面我按先看懂结构、再跑通流程、最后避坑的顺序拆。2. 拆开源码包目录结构与 ST-GCN 的数据流2.1 从文件树反推模块职责拿到一个陌生源码包我习惯先看目录再动手不然跑起来报错都不知道该改哪。这份包的顶层大致分几块目录/文件职责net/网络定义st_gcn.py是单流版本st_gcn_twostream.py是双流版本feeder/数据加载feeder.py读 NTU 格式feeder_kinetics.py读 Kinetics 格式processor/训练与推理主循环recognition.py是核心demo_offline.py、demo_realtime.py是演示入口tools/数据预处理ntu_gendata.py、kinetics_gendata.py把原始数据转成训练用的 npytorchlight/训练辅助io.py管日志和 checkpointgpu.py管设备models/预训练权重OriginSTGCN.pt、AddEdgeSTGCN12345.pt、kinetics-st_gcn.ptconfig/配置文件st_gcn和st_gcn.twostream两套main.py统一入口按配置决定训练还是推理resource/和media/里放的是 demo 用的样例骨架和可视化素材work_dir/是训练输出目录logData/里那个AddEdgeWeight_2.txt是边权重的记录文件属于作者调参时留下的痕迹不影响主流程。2.2 ST-GCN 单元到底在算什么打开net/st_gcn.py核心是ST_GCN这个模块它由两部分串起来一个空间图卷积GCN一个时间卷积。空间部分的关键是邻接矩阵——它定义了哪些关节算邻居。原始 ST-GCN 用的是人体自然连接的骨架边加上自连接再按距离分成几个子集通常三分自身、向心、离心每个子集配一套可学习权重。这就是为什么它比普通 GCN 强它区分了关节自己靠近重心的邻居远离重心的邻居动作识别里这个方向性很重要。时间部分就是沿时间轴做kernel_size为 9 的一维卷积配合 stride 做下采样。整个网络堆 10 层左右每层通道数递增最后全局池化接全连接分类。输入张量的形状是(N, C, T, V, M)N 是 batchC 是通道坐标 x,y 加置信度T 是帧数V 是关节数NTU 是 25M 是人数通常 1 或 2。这个五维张量是理解整份代码的钥匙feeder 负责产出它net 负责消费它中间任何一步维度对不上都会报错。2.3 双流版本多出来的那一路st_gcn_twostream.py在单流基础上加了第二路输入关节流用坐标骨骼流用相邻关节的坐标差也就是骨向量。两路各训一个模型推理时把 softmax 分数相加。骨骼流的好处是它对尺度变化更鲁棒——坐标会随人离镜头远近缩放但骨长基本不变。代价是训练时间和显存翻倍毕业设计如果只是要个能跑的结果单流够用想冲准确率再上双流。3. 把 NTU 骨架数据喂进网络预处理与训练全流程3.1 环境准备与依赖确认先看requirements.txt这份代码基于 PyTorch常见做法是建一个独立虚拟环境避免和系统里的包打架。我一般用 condaconda create -n stgcn python3.8 -y conda activate stgcn # 按 requirements.txt 装依赖PyTorch 版本要和 CUDA 匹配 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt这里有个参数要盯PyTorch 的 CUDA 版本必须和本机驱动兼容装错了会在torch.cuda.is_available()返回 False训练直接掉到 CPU 上一个 epoch 能跑几小时。装完先跑一句验证import torch print(torch.__version__, torch.cuda.is_available())返回True才继续。如果只有 CPU也能跑但要把 batch size 调小、预期训练时间拉长。3.2 用 ntu_gendata.py 生成训练数据NTU RGBD 原始数据是.skeleton文本文件每个文件一段动作里面是每帧每个关节的坐标。tools/ntu_gendata.py负责把它转成 npy。常见做法是先下载 NTU 数据集解压后把路径填进脚本# 生成训练集和验证集--ignored_sample_path 是官方给的坏样本列表 python tools/ntu_gendata.py \ --data_path /path/to/nturgbd_skeletons_s001_to_s017 \ --out_folder ./data/ntu \ --ignored_sample_path ./resource/samples_with_missing_skeletons.txt参数说明--data_path指向解压后的骨架目录--out_folder是输出 npy 的位置--ignored_sample_path很关键——NTU 里有少量样本骨架缺失不排除会在训练时触发 NaN。脚本跑完会生成train_data.npy、train_label.pkl、val_data.npy、val_label.pkl四个文件。数据量大的话这一步要等十几分钟别以为卡死了。3.3 配置文件和训练启动config/下的st_gcn是训练配置里面定义了数据路径、batch size、学习率、epoch 数、网络层数等。我一般先改这几项# config/st_gcn 里典型字段示意以实际文件为准 dataset ntu # 数据集类型 batch_size 64 # 显存不够就降到 16 或 32 num_epoch 80 # 毕业设计跑 50 左右也能看到收敛 learning_rate 0.1 # 原始配置配合 warmup 用 window_size 300 # 时间窗口NTU 动作最长 300 帧启动训练python main.py recognition -c config/st_gcn/trainmain.py会根据-c后面的配置决定走训练还是推理分支。训练日志和 checkpoint 默认落在work_dir/下torchlight/io.py负责写日志和存模型。第一次跑建议把num_epoch改成 2确认整条链路通了再放开跑全量——这是我踩过坑之后养成的习惯不然跑到第 30 个 epoch 才发现数据路径写错时间全白费。3.4 推理与 demo 验证训练完拿到.pt权重后用processor/recognition.py做测试或者直接跑 demo# 离线 demo读一段骨架序列输出预测类别 python processor/demo_offline.py --config config/st_gcn/demo_offline # 实时 demo需要摄像头和姿态估计前端配合 python processor/demo_realtime.py离线 demo 是最稳的验证方式它不依赖摄像头直接喂一段预处理好的骨架输出 top-k 类别和分数。如果离线 demo 结果正常说明网络和数据管线都没问题再去折腾实时 demo。实时 demo 依赖外部姿态估计器把视频转成骨架这一步的延迟和抖动会直接影响识别效果属于另一个话题。4. 训练不收敛、维度报错、显存爆ST-GCN 常见问题排查4.1 loss 一直不降准确率卡在随机水平现象训练几个 epoch 后 loss 在某个值附近震荡验证准确率约等于类别数的倒数。原因通常是学习率太大或 warmup 没配好ST-GCN 原始配置用了 0.1 的学习率但配合了 warmup直接砍掉 warmup 会发散。解决确认配置文件里 warmup 相关字段存在或者把初始学习率降到 0.01 再试。另一个常见原因是标签没对齐——train_label.pkl里的类别索引和网络输出维度不一致检查 feeder 里num_class是否等于实际类别数。4.2 张量维度不匹配报错现象RuntimeError: Expected 4-dimensional input for 4-dimensional weight之类。原因ST-GCN 输入是五维(N,C,T,V,M)但某些预处理步骤或自定义 feeder 产出的是四维。解决在 feeder 的__getitem__里打印 shape确认M维度存在如果只有单人数据用np.expand_dims补一个维度。这个坑在换自己数据集时几乎必踩。4.3 显存不够batch size 降了还是爆现象CUDA out of memory。原因ST-GCN 的显存占用和T帧数成正比NTU 的 300 帧窗口本身就吃显存双流版本再翻倍。解决先把window_size从 300 降到 150或者用torch.cuda.empty_cache()在 epoch 之间清缓存双流版本可以改成先训一路、存权重、再训另一路而不是同时加载两个模型。4.4 预训练权重加载失败现象Missing key(s) in state_dict或Unexpected key(s)。原因models/里的.pt可能是用不同层数或不同版本代码训的键名对不上。解决用torch.load加载后打印state_dict.keys()和当前模型的state_dict.keys()对比只加载匹配的部分或者用strictFalse跳过不匹配的键。别硬套键名对不上说明结构有差异强行加载反而引入错误权重。4.5 实时 demo 延迟高、预测跳变现象实时 demo 里预测类别频繁跳或者画面卡顿。原因姿态估计前端每帧独立估计关节抖动大ST-GCN 吃的是连续序列抖动会被放大。解决在骨架输入前加一个滑动平均滤波或者把预测结果做多数投票平滑。实时 demo 的瓶颈往往不在 ST-GCN 本身而在前端姿态估计的速度这个要有预期。5. 换自己的数据集与双流调优两个能立刻上手的技巧5.1 把自定义骨架接进 feeder毕业设计里最常见的需求是我有一套自己采的骨架怎么用这份代码训。核心是让 feeder 产出和 NTU 一样的五维张量。假设你的骨架是每帧 17 个关节COCO 格式存在一个 npy 里形状(样本数, 帧数, 17, 2)可以写一个轻量转换import numpy as np def to_stgcn_format(skeleton, max_frames300, num_joints17): # skeleton: (T, V, 2)补成 (C, T, V, M) T, V, C skeleton.shape[0], skeleton.shape[1], skeleton.shape[2] data np.zeros((3, max_frames, num_joints, 1), dtypenp.float32) # 通道 0、1 放 x、y通道 2 放置信度没有就填 1 length min(T, max_frames) data[0, :length, :V, 0] skeleton[:length, :, 0] data[1, :length, :V, 0] skeleton[:length, :, 1] data[2, :length, :V, 0] 1.0 return data # 形状 (C, T, V, M)逻辑说明ST-GCN 期望的输入是(C, T, V, M)这里把坐标拆到通道 0、1置信度通道填 1。参数max_frames要和配置里的window_size一致短于它的序列补零长于它的截断。补零的帧在训练时会被 mask 掉但前提是 feeder 里实现了 mask 逻辑换数据集时要确认这一点否则补零会被当成真实动作学进去。5.2 双流融合的权重怎么调双流版本推理时把关节流和骨骼流的分数相加但两路权重不一定相等。原始实现是直接相加实践中骨骼流在跨视角场景下往往更稳可以给它更高权重# 伪代码双流分数融合 score_joint model_joint(data_joint) # (N, num_class) score_bone model_bone(data_bone) # (N, num_class) alpha 0.6 # 骨骼流权重跨视角场景可调到 0.6~0.7 final_score (1 - alpha) * score_joint alpha * score_bone pred final_score.argmax(dim1)参数alpha需要在验证集上试我一般从 0.5 开始按 0.1 步长扫一遍看 X-View 协议下的准确率变化。注意别在测试集上调这个权重那是作弊验证集调完再在测试集上跑一次确认。5.3 一个验证训练是否正常的小技巧训练启动后别只盯着 loss。我习惯在第一个 epoch 结束后手动拿几条训练样本喂进模型看预测类别是否和标签一致——如果连训练样本都预测错说明网络根本没学进去问题出在数据管线而不是超参。这个检查比等 loss 曲线快得多能省下大量瞎调参的时间。从那以后我每次换数据集或改 feeder都强制先跑一遍这个过拟合小样本的检查确认链路通了再开全量训练。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑