骨骼识别-复现PoseC3D
转自AI Studio,原文链接:骨骼点动作识别-基于Paddle复现PoseC3D - 飞桨AI StudioRevisiting Skeleton-based Action Recognition(PoseC3D 基于Paddle复现)1.简介人体骨架作为人类行为的一种简洁的表现形式,近年来受到越来越多的关注。许多基于骨架的动作识别方法都采用了图卷积网络(GCN)来提取人体骨架上的
转自AI Studio,原文链接:骨骼点动作识别-基于Paddle复现PoseC3D - 飞桨AI Studio
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的测试效果如下表。
NetWork | epochs | opt | image_size | batch_size | dataset | top1 acc |
---|---|---|---|---|---|---|
PoseC3D | 12 | SGD | 56x56 | 16 | UCF-101 | 87.05% |
3.数据集
UCF-101以及预训练模型下载地址:
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 imp Loading pretrained model from best_model/model.pdparams There 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.模型信息
信息 | 描述 |
---|---|
模型名称 | PoseC3D |
框架版本 | PaddlePaddle==2.2.2 |
应用场景 | 骨骼识别 |
9.复现心得
在之前的复现赛中复现过C3D,这次看到了PoseC3D的复现就参加了。复用了部分之前部分C3D的代码,所以这篇论文代码完成的速度比较快。参考repo使用的是8卡,我使用的Notebook的1卡V100环境,所以每个batch是参考repo的1/8,所以学习率也调整为原来的1/8。最终精度为87.05%跟源repo的87%基本一致,也是符合预期的。最后感谢飞桨举办本次比赛,也感谢AI Stuido提供算力支持。
更多推荐
所有评论(0)