骨骼点动作识别-基于Paddle复现PoseC3D

本文介绍基于Paddle复现的PoseC3D模型,其以3D热图堆栈为人体骨架表示,用3D-CNN分类,较GCN方法在时空特征学习等方面更优。复现在UCF-101数据集上达87.05%的top1准确率,详述了网络结构、环境依赖、数据集、代码结构及训练测试等流程,还提及复现心得。
Revisiting Skeleton-based Action Recognition(PoseC3D 基于Paddle复现)
1.简介
人体骨架作为人类行为的一种简洁的表现形式,近年来受到越来越多的关注。许多基于骨架的动作识别方法都采用了图卷积网络(GCN)来提取人体骨架上的特征。尽管在以前的工作中取得了积极的成果,但基于GCN的方法在健壮性、互操作性和可扩展性方面受到限制。
在本文中,作者提出了一种新的基于骨架的动作识别方法PoseC3D,它依赖于3D热图堆栈而不是图形序列作为人体骨架的基本表示。与基于GCN的方法相比,PoseC3D在学习时空特征方面更有效,对姿态估计噪声更具鲁棒性,并且在跨数据集环境下具有更好的通用性。
此外,PoseC3D可以在不增加计算成本的情况下处理多人场景,其功能可以在早期融合阶段轻松与其他模式集成,这为进一步提升性能提供了巨大的设计空间。在四个具有挑战性的数据集上,PoseC3D在单独用于Keletons和与RGB模式结合使用时,持续获得优异的性能。
上图是网络架构,对于视频中的每一帧,首先使用两阶段姿势估计(检测+姿势估计)进行人体姿势提取。然后沿着时间维度堆叠关节或肢体的heatmap,并对生成的三维heatmap进行预处理。最后,我们使用3D-CNN对三维的heatmap进行分类。
2.复现精度
在UCF-101数据集上spilt1的测试效果如下表。
3.数据集
UCF-101以及预训练模型下载地址:
https://aistudio.baidu.com/aistudio/datasetdetail/140593
4.环境依赖
PaddlePaddle == 2.2.2
5.网络结构
从第一节的图中可以看到,网络的主要结构由Resnet中的Layer构成,网络结构代码如下:
class ResNet3d(nn.Layer): arch_settings = { 50: (Bottleneck3d, (3, 4, 6, 3)), 101: (Bottleneck3d, (3, 4, 23, 3)), 152: (Bottleneck3d, (3, 8, 36, 3)) }登录后复制
上述代码定义了ResNet3d的网络类,以及定义了不同层数网络的配置。
def __init__(self, depth, pretrained, stage_blocks=None, pretrained2d=True, in_channels=3, num_stages=4, base_channels=64, out_indices=(3, ), spatial_strides=(1, 2, 2, 2), temporal_strides=(1, 1, 1, 1), dilations=(1, 1, 1, 1), conv1_kernel=(3, 7, 7), conv1_stride_s=2, conv1_stride_t=1, pool1_stride_s=2, pool1_stride_t=1, with_pool1=True, with_pool2=True, style='pytorch', frozen_stages=-1, inflate=(1, 1, 1, 1), inflate_style='3x1x1', conv_cfg=dict(type='Conv3d'), norm_cfg=dict(type='BN3d', requires_grad=True), act_cfg=dict(type='ReLU', inplace=True), norm_eval=False, with_cp=False, non_local=(0, 0, 0, 0), non_local_cfg=dict(), zero_init_residual=True, **kwargs): super().__init__() if depth not in self.arch_settings: raise KeyError(f'invalid depth {depth} for resnet') # 初始化网络参数 self.depth = depth self.pretrained = pretrained self.pretrained2d = pretrained2d self.in_channels = in_channels self.base_channels = base_channels self.num_stages = num_stages assert 1 <= num_stages <= 4 self.stage_blocks = stage_blocks self.out_indices = out_indices assert max(out_indices) < num_stages self.spatial_strides = spatial_strides self.temporal_strides = temporal_strides self.dilations = dilations assert len(spatial_strides) == len(temporal_strides) == len( dilations) == num_stages if self.stage_blocks is not None: assert len(self.stage_blocks) == num_stages # 保存卷积网络参数 self.conv1_kernel = conv1_kernel self.conv1_stride_s = conv1_stride_s self.conv1_stride_t = conv1_stride_t self.pool1_stride_s = pool1_stride_s self.pool1_stride_t = pool1_stride_t self.with_pool1 = with_pool1 self.with_pool2 = with_pool2 self.style = style self.frozen_stages = frozen_stages self.stage_inflations = _ntuple(num_stages)(inflate) self.non_local_stages = _ntuple(num_stages)(non_local) self.inflate_style = inflate_style self.conv_cfg = conv_cfg self.norm_cfg = norm_cfg self.act_cfg = act_cfg self.norm_eval = norm_eval self.with_cp = with_cp self.zero_init_residual = zero_init_residual self.block, stage_blocks = self.arch_settings[depth] if self.stage_blocks is None: self.stage_blocks = stage_blocks[:num_stages] self.inplanes = self.base_channels self.non_local_cfg = non_local_cfg登录后复制
上述代码初始化网络的超参数。
# 构建第一个 stem层 self._make_stem_layer() self.res_layers = [] # 根据stage_blocks的内容构建网络。 # 这里的stage_blocks是(3, 4, 6)。 for i, num_blocks in enumerate(self.stage_blocks): spatial_stride = spatial_strides[i] temporal_stride = temporal_strides[i] dilation = dilations[i] planes = self.base_channels * 2**i res_layer = self.make_res_layer( self.block, self.inplanes, planes, num_blocks, spatial_stride=spatial_stride, temporal_stride=temporal_stride, dilation=dilation, style=self.style, norm_cfg=self.norm_cfg, conv_cfg=self.conv_cfg, act_cfg=self.act_cfg, non_local=self.non_local_stages[i], non_local_cfg=self.non_local_cfg, inflate=self.stage_inflations[i], inflate_style=self.inflate_style, with_cp=with_cp, **kwargs) self.inplanes = planes * self.block.expansion layer_name = f'layer{i + 1}' self.add_sublayer(layer_name, res_layer) self.res_layers.append(layer_name) self.feat_dim = self.block.expansion * self.base_channels * 2**( len(self.stage_blocks) - 1)登录后复制
上述为构建网络的主要的代码,首先构建一个stem层,然后根据stage_blocks的内容,使用make_res_layer方法构建网络。
def make_res_layer(block, inplanes, planes, blocks, spatial_stride=1, temporal_stride=1, dilation=1, style='pytorch', inflate=1, inflate_style='3x1x1', non_local=0, non_local_cfg=dict(), norm_cfg=None, act_cfg=None, conv_cfg=None, with_cp=False, **kwargs): inflate = inflate if not isinstance(inflate, int) else (inflate, ) * blocks non_local = non_local if not isinstance( non_local, int) else (non_local, ) * blocks assert len(inflate) == blocks and len(non_local) == blocks downsample = None # 判断是否需要进行下采样。当输入的通道和输出通道不相等,则根据planes * block.expansion缩减通道数。 if spatial_stride != 1 or inplanes != planes * block.expansion: downsample = ConvBNLayer( in_channels=inplanes, out_channels=planes * block.expansion, kernel_size=1, stride=(temporal_stride, spatial_stride, spatial_stride), bias=False, act=None )登录后复制
上述代码为make_res_layer方法,其中包含判断block是否需要进行下采样。当输入的通道和输出通道不相等,则根据planes * block.expansion缩减通道数。在block模块中会对通道数进行缩减。
layers = [] layers.append( block( inplanes, planes, spatial_stride=spatial_stride, temporal_stride=temporal_stride, dilation=dilation, downsample=downsample, style=style, inflate=(inflate[0] == 1), inflate_style=inflate_style, non_local=(non_local[0] == 1), non_local_cfg=non_local_cfg, norm_cfg=norm_cfg, conv_cfg=conv_cfg, act_cfg=act_cfg, with_cp=with_cp, **kwargs)) inplanes = planes * block.expansion登录后复制
构建网络,downsample作为参数传递进去。
for i in range(1, blocks): layers.append( block( inplanes, planes, spatial_stride=1, temporal_stride=1, dilation=dilation, style=style, inflate=(inflate[i] == 1), inflate_style=inflate_style, non_local=(non_local[i] == 1), non_local_cfg=non_local_cfg, norm_cfg=norm_cfg, conv_cfg=conv_cfg, act_cfg=act_cfg, with_cp=with_cp, **kwargs)) return nn.Sequential(*layers)登录后复制
上述根据blocks中的数量构建网络,这里的block为Bottleneck3d层。主要是用来构建一个在通道数上类似瓶颈的一个层,与ResNet系列网络一致。
class ConvBNLayer(nn.Layer): def __init__( self, in_channels, out_channels, kernel_size, padding=0, stride=1, dilation=1, groups=1, act=None, bias=None, ): super(ConvBNLayer, self).__init__() self._conv = nn.Conv3D( in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups, bias_attr=bias) self._batch_norm = nn.BatchNorm3D(out_channels, momentum=0.1) self.act = act if act is not None: self._act_op = nn.ReLU()登录后复制
上述代码为网络的卷积的基本单元,通过该方法可以构建一个Conv3D+BN+Relu的结构。该结构也是Bottleneck3d模块的重要组成部分。
6.快速开始
训练:
In [ ]%cd /home/aistudio/PaddlePoseC3D!python train.py --dataset_root ../data/data140593/ucf101.pkl \--pretrained ../data/data140593/res3d_k400.pdparams --max_epochs 12 \--batch_size 16 --log_iters 100登录后复制
dataset_root: 训练集路径
pretrained: 预训练模型路径
max_epochs: 最大epoch数量
batch_size: 批次大小
测试:
使用最优模型进行评估.
最优模型下载地址:
链接: https://pan.baidu.com/s/1J9_X_CNkXQbhBhj-xHHBDw
提取码: uq9m
In [ ]!python -u test.py --dataset_root ucf101.pkl --pretrained best_model/model.pdparams登录后复制
dataset_root: 训练集路径
pretrained: 预训练模型路径
单张图片预测
输入文件v_BaseballPitch_g07_c01.pkl的视频如下图所示,同时可视化v_BaseballPitch_g07_c01.pkl文件。通过predict.py可预测出该文件的所属分类。
In [3]%cd /home/aistudio/PaddlePoseC3D!python predict.py --input_file v_BaseballPitch_g07_c01.pkl \--pretrained best_model/model.pdparams登录后复制
/home/aistudio/PaddlePoseC3D/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/setuptools/depends.py:2: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses import impLoading pretrained model from best_model/model.pdparamsThere are 217/217 variables loaded into Recognizer3D.File v_BaseballPitch_g07_c01 is class BaseballPitch登录后复制


原始频与关节点可视化
参数说明:
input_file: 输入文件,按照ucf-101.pkl格式。可以使用test_tipc/data中的predict_example.pkl数据进行测试。
pretrained: 训练好的模型
模型导出
模型导出可执行以下命令:
In [ ]!python export_model.py --model_path best_model.pdparams --save_dir ./output/登录后复制
参数说明:
model_path: 模型路径
save_dir: 输出图片保存路径
Inference推理
可使用以下命令进行模型推理。该脚本依赖auto_log, 请参考下面TIPC部分先安装auto_log。infer命令运行如下:
In [ ]!python infer.py --use_gpu=False --enable_mkldnn=False \--cpu_threads=2 --model_file=output/model.pdmodel --batch_size=2 \--input_file=validation/BSD300/test --enable_benchmark=True --precision=fp32 \--params_file=output/model.pdiparams --save_dir output/inference_img登录后复制
参数说明:
use_gpu:是否使用GPU
enable_mkldnn:是否使用mkldnn
cpu_threads: cpu线程数
model_file: 模型路径
batch_size: 批次大小
input_file: 输入文件路径
enable_benchmark: 是否开启benchmark
precision: 运算精度
params_file: 模型权重文件,由export_model.py脚本导出。
save_dir: 保存推理预测图片的路径
TIPC基础链条测试
该部分依赖auto_log,需要进行安装,安装方式如下:
auto_log的详细介绍参考https://github.com/LDOUBLEV/AutoLog。
In [ ]%cd /home/aistudio/!git clone https://gitee.com/Double_V/AutoLog!cd AutoLog/!pip3 install -r requirements.txt!python3 setup.py bdist_wheel!pip3 install ./dist/auto_log-1.2.0-py3-none-any.whl!bash test_tipc/prepare.sh test_tipc/configs/posec3d/train_infer_python.txt 'lite_train_lite_infer'!bash test_tipc/test_train_inference_python.sh test_tipc/configs/posec3d/train_infer_python.txt 'lite_train_lite_infer'登录后复制
测试结果如截图所示:
7.代码结构与详细说明
PaddlePoseC3D├── README.md # 使用说明├── datasets # 数据集包│ ├── __init__.py│ ├── base.py #数据集基类│ ├── file_client.py # 文件处理类│ ├── pipelines│ │ └── transforms.py # 数据增强类│ ├── pose_dataset.py # 数据集类│ ├── dataset_wrappers.py # 数据集类│ └── utils.py #数据集工具类├── models│ ├── __init__.py│ ├── base.py # 模型基类│ ├── resnet3d.py # backbone│ ├── resnet3d_slowfast.py # backbone│ └── resnet3d_slowonly.py # backbone│ ├── i3d_head.py # c3d模型头部实现│ └── recognizer3d.py # 识别模型框架├── progress_bar.py #进度条工具├── test.py # 评估程序├── test_tipc # TIPC脚本│ ├── README.md│ ├── common_func.sh # 通用脚本程序│ ├── configs│ │ └── posec3d│ │ └── train_infer_python.txt # 单机单卡配置│ ├── data│ │ ├── example.npy # 推理用样例数据│ │ └── mini_ucf.zip # 训练用小规模数据集│ ├── output│ ├── prepare.sh # 数据准备脚本│ └── test_train_inference_python.sh # 训练推理测试脚本├── timer.py # 时间工具类├── train.log # 训练日志├── test.log # 测试日志├── train.py # 训练脚本└── utils.py # 训练工具包登录后复制
8.模型信息
9.复现心得
在之前的复现赛中复现过C3D,这次看到了PoseC3D的复现就参加了。复用了部分之前部分C3D的代码,所以这篇论文代码完成的速度比较快。参考repo使用的是8卡,我使用的Notebook的1卡V100环境,所以每个batch是参考repo的1/8,所以学习率也调整为原来的1/8。最终精度为87.05%跟源repo的87%基本一致,也是符合预期的。最后感谢飞桨举办本次比赛,也感谢AI Stuido提供算力支持。
免责声明
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
iQOO 15首销30分钟超上代全天销量,旗舰性能引燃市场
10月21日消息,iQOO 15开启首销就创下令人瞩目的销售纪录。据iQOO产品经理戈蓝发布的战报显示,新机开售仅30分钟,销量就已
企业级AI大模型落地指南:现状挑战、架构方法与未来趋势
近日,安全牛发布的《企业级AI大模型落地实战技术应用指南(2025版)》引发行业广泛关注。该报告以176页的篇幅,系统梳理了企业级AI大模型从技术架构到落地实践的全流程,为企业数字化转型提供了可操作
阿里“C计划”聚焦对话AI,直指字节豆包竞争新局
近日,有消息人士透露,阿里巴巴旗下智能搜索平台夸克正在低调推进一项名为“C计划”的AI业务项目。该项目由夸克核心研发团队牵头,并邀请通义实验室多位资深专家参与,主要聚焦对话式AI领域的创新应用。据知
马斯克:Grok 5实现AGI概率达10%,2025年底前将持续提升
埃隆・马斯克近日在社交平台X上透露了关于其旗下xAI公司新产品的关键预测。据他描述,该公司正在研发的大型语言模型Grok 5,有10%的概率能够达成通用人工智能(AGI)的目标,且这一概率呈现上升趋
国产数据平台如何借力Databricks+OpenAI破局?
在人工智能技术飞速发展的当下,企业如何高效、安全地接入AI,成为业界关注的焦点。近日,全球领先的数据智能平台Databricks与知名大模型公司OpenAI宣布达成多年期战略合作,这一举动不仅标志着
相关攻略
热门教程
更多- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程



















