继续上一篇骨骼关键点识别算法相关内容的学习,今天学习通用领域的工具使用以及网络的DIY
目前正在实习,具体的内容方向是基于骨骼关键点的动作识别(skeleton based action recognition)。 在经过了多多的调研之后,觉得目前还是mmaction2平台最香,因此希望后续工作(包括效果的验证、实验、对比等等)包括前期的端到端工作都在mmaction2工具箱上展开。
学习mmaction2的目的是能够借助该平台对数据集、算法、演示、流程、搭建等等环节都能够理解的更加清晰和透彻(毕竟目前是一个小白),为后续自己的真正实际问题解决做准备。
本篇文章主要是基于mmaction2的中文教程(链接见文末),此外还有一些参考的文章以及github库中的注释性教程。
mmaction2是商汤和港中文联合研发的一个基于pytorch框架的人体动作识别的深度学习开源工具库,可以提供包括行为识别(分类)、时序动作检测、时空动作检测、骨骼动作识别(分类)等等多种子类问题的算法框架,包括数据集等等,可以非常方便的使用。
mmaction2 和广为人知的检测工具库mmdetection 一样,都属于open-mmlab 工具箱下属的一个模块,目前仍在不断更新与拓展功能及算法,可以预见的是,其在人体行为识别方面将被更多人使用。
《先放参考链接》
在本教程中,我们将介绍一些有关如何为该项目定制优化器,开发新组件,以及添加新的学习率调整器(更新器)的方法。
CopyOfSGD 是自定义优化器的一个例子,写在 mmaction/core/optimizer/copy_of_sgd.py
文件中。 更一般地,可以根据如下方法自定义优化器。
假设添加的优化器名为 MyOptimizer
,它有 a
,b
和 c
三个参数。 用户需要首先实现一个新的优化器文件,如 mmaction/core/optimizer/my_optimizer.py
:
from mmcv.runner import OPTIMIZERS
from torch.optim import Optimizer
@OPTIMIZERS.register_module()
class MyOptimizer(Optimizer):
def __init__(self, a, b, c):
然后添加这个模块到 mmaction/core/optimizer/__init__.py
中,从而让注册器可以找到这个新的模块并添加它:
from .my_optimizer import MyOptimizer
之后,用户便可以在配置文件的 optimizer 字段中使用 MyOptimizer
。 在配置中,优化器由 optimizer
字段所定义,如下所示:
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
用户可以直接根据 PyTorch API 文档 对参数进行直接设置。
某些模型可能对不同层的参数有特定的优化设置,例如 BatchNorm
层的梯度衰减。 用户可以通过自定义优化器构造函数来进行那些细粒度的参数调整。
用户可以编写一个基于 DefaultOptimizerConstructor
的新的优化器构造器, 并且重写 add_params(self, params, module)
方法。
一个自定义优化器构造器的例子是 TSMOptimizerConstructor
。 更具体地,可以如下定义定制的优化器构造器。
在 mmaction/core/optimizer/my_optimizer_constructor.py
:
from mmcv.runner import OPTIMIZER_BUILDERS, DefaultOptimizerConstructor
@OPTIMIZER_BUILDERS.register_module()
class MyOptimizerConstructor(DefaultOptimizerConstructor):
在 mmaction/core/optimizer/__init__.py
:
from .my_optimizer_constructor import MyOptimizerConstructor
之后便可在配置文件的 optimizer
域中使用 MyOptimizerConstructor
。
# 优化器
optimizer = dict(
type='SGD',
constructor='MyOptimizerConstructor',
paramwise_cfg=dict(fc_lr5=True),
lr=0.02,
momentum=0.9,
weight_decay=0.0001)
MMAction2 将模型组件分为 4 种基础模型:
识别器(recognizer):整个识别器模型流水线,通常包含一个主干网络(backbone)和分类头(cls_head)。
主干网络(backbone):通常为一个用于提取特征的 FCN 网络,例如 ResNet,BNInception。
分类头(cls_head):用于分类任务的组件,通常包括一个带有池化层的 FC 层。
时序检测器(localizer):用于时序检测的模型,目前有的检测器包含 BSN,BMN,SSN。
mmaction/models/backbones/resnet.py
import torch.nn as nn
from ..builder import BACKBONES
@BACKBONES.register_module()
class ResNet(nn.Module):
def __init__(self, arg1, arg2):
pass
def forward(self, x): # 应该返回一个元组
pass
def init_weights(self, pretrained=None):
pass
2- 在 mmaction/models/backbones/__init__.py
中导入模型
from .resnet import ResNet
3- 在配置文件中使用它
model = dict(
...
backbone=dict(
type='ResNet',
arg1=xxx,
arg2=xxx),
)
mmaction/models/heads/tsn_head.py
BaseHead
编写一个新的分类头, 并重写 init_weights(self)
和 forward(self, x
) 方法from ..builder import HEADS
from .base import BaseHead
@HEADS.register_module()
class TSNHead(BaseHead):
def __init__(self, arg1, arg2):
pass
def forward(self, x):
pass
def init_weights(self):
pass
2- 在 mmaction/models/heads/__init__.py
中导入模型
from .tsn_head import TSNHead
3- 在配置文件中使用它
model = dict(
...
cls_head=dict(
type='TSNHead',
num_classes=400,
in_channels=2048,
arg1=xxx,
arg2=xxx),
MyLoss
。为了添加一个新的损失函数,需要在 mmaction/models/losses/my_loss.py
下进行实现。import torch
import torch.nn as nn
from ..builder import LOSSES
def my_loss(pred, target):
assert pred.size() == target.size() and target.numel() > 0
loss = torch.abs(pred - target)
return loss
@LOSSES.register_module()
class MyLoss(nn.Module):
def forward(self, pred, target):
loss = my_loss(pred, target)
return loss
之后,用户需要把它添加进 mmaction/models/losses/__init__.py
from .my_loss import MyLoss, my_loss
为了使用它,需要修改 loss_xxx
域。由于 MyLoss
用户识别任务,可以把它作为边界框损失 loss_bbox
loss_bbox=dict(type='MyLoss'))
构造学习率更新器(即 PyTorch 中的 “scheduler”)的默认方法是修改配置,例如:
...
lr_config = dict(policy='step', step=[20, 40])
...
在 train.py
的 api 中,它会在以下位置注册用于学习率更新的钩子:
...
runner.register_training_hooks(
cfg.lr_config,
optimizer_config,
cfg.checkpoint_config,
cfg.log_config,
cfg.get('momentum_config', None))
...
到目前位置,所有支持的更新器可参考 mmcv, 但如果用户想自定义学习率更新器,则需要遵循以下步骤:
1- 首先,在 $MMAction2/mmaction/core/scheduler
编写自定义的学习率更新钩子(LrUpdaterHook)。以下片段是自定义学习率更新器的例子,它使用基于特定比率的学习率 lrs
,并在每个 steps
处进行学习率衰减。以下代码段是自定义学习率更新器的例子:
# 在此注册
@HOOKS.register_module()
class RelativeStepLrUpdaterHook(LrUpdaterHook):
# 该类应当继承于 mmcv.LrUpdaterHook
def __init__(self, steps, lrs, **kwargs):
super().__init__(**kwargs)
assert len(steps) == (len(lrs))
self.steps = steps
self.lrs = lrs
def get_lr(self, runner, base_lr):
# 仅需要重写该函数
# 该函数在每个训练周期之前被调用, 并返回特定的学习率.
progress = runner.epoch if self.by_epoch else runner.iter
for i in range(len(self.steps)):
if progress < self.steps[i]:
return self.lrs[i]
2- 修改配置
在配置文件下替换原先的 lr_config
变量
lr_config = dict(policy='RelativeStep', steps=[20, 40, 60], lrs=[0.1, 0.01, 0.001])
《参考链接》
开放式神经网络交换格式(Open Neural Network Exchange,即 ONNX)是一个开放的生态系统,使 AI 开发人员能够随着项目的发展选择正确的工具。
到目前为止,MMAction2 支持将训练的 pytorch 模型中进行 onnx 导出。支持的模型有:
I3D
TSN
TIN
TSM
R(2+1)D
SLOWFAST
SLOWONLY
BMN
BSN(tem, pem)
对于简单的模型导出,用户可以使用这里的 脚本添加链接描述。 注意,需要安装 onnx
和 onnxruntime
包以进行导出后的验证。
pip install onnx onnxruntime
MMAction2 提供了一个 python 脚本,用于将 MMAction2 训练的 pytorch 模型导出到 ONNX。
python tools/deployment/pytorch2onnx.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--shape ${SHAPE}] \
[--verify] [--show] [--output-file ${OUTPUT_FILE}] [--is-localizer] [--opset-version ${VERSION}]
可选参数:
--shape
: 模型输入张量的形状。对于 2D 模型(如 TSN),输入形状应当为 $batch $clip $channel $height $width (例如,1 1 3 224 224);对于 3D 模型(如 I3D),输入形状应当为 $batch $clip $channel $time $height $width (如,1 1 3 32 224 224);对于时序检测器如 BSN,每个模块的数据都不相同,请查看对应的 forward 函数。如果没有被指定,它将被置为 1 1 3 224 224。
--verify
: 决定是否对导出模型进行验证,验证项包括是否可运行,数值是否正确等。如果没有被指定,它将被置为 False。
--show
: 决定是否打印导出模型的结构。如果没有被指定,它将被置为 False。
--output-file
: 导出的 onnx 模型名。如果没有被指定,它将被置为 tmp.onnx。
--is-localizer
:决定导出的模型是否为时序检测器。如果没有被指定,它将被置为 False。
--opset-version
:决定 onnx 的执行版本,MMAction2 推荐用户使用高版本(例如 11 版本)的 onnx 以确保稳定性。如果没有被指定,它将被置为 11。
--softmax
: 是否在行为识别器末尾添加 Softmax。如果没有指定,将被置为 False。目前仅支持行为识别器,不支持时序动作检测器。
python tools/deployment/pytorch2onnx.py $CONFIG_PATH $CHECKPOINT_PATH --shape $SHAPE --verify
python tools/deployment/pytorch2onnx.py $CONFIG_PATH $CHECKPOINT_PATH --is-localizer --shape $SHAPE --verify
《参考链接》
在本教程中,我们将介绍如何在运行自定义模型时,进行自定义参数优化方法,学习率调整策略,工作流和钩子的方法。
optimizer = dict(type='Adam', lr=0.0003, weight_decay=0.0001)
要修改模型的学习率,用户只需要在优化程序的配置中修改 “lr” 即可。 用户可根据 PyTorch API 文档 进行参数设置
例如,如果想使用 Adam
并设置参数为 torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
, 则需要进行如下修改:
optimizer = dict(type='Adam', lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
具体可见本文第二章中的 “自定义优化器”部分
具体可见本文第二章中的 “自定义优化器构造器”部分
使用梯度裁剪来稳定训练 一些模型需要使用梯度裁剪来剪辑渐变以稳定训练过程。 一个例子如下:
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
使用动量调整来加速模型收敛 MMAction2 支持动量调整器根据学习率修改模型的动量,从而使模型收敛更快。 动量调整程序通常与学习率调整器一起使用,例如,以下配置用于3D检测以加速收敛。 更多细节可参考 CyclicLrUpdater 和 CyclicMomentumUpdater。
lr_config = dict(
policy='cyclic',
target_ratio=(10, 1e-4),
cyclic_times=1,
step_ratio_up=0.4,
)
momentum_config = dict(
policy='cyclic',
target_ratio=(0.85 / 0.95, 1),
cyclic_times=1,
step_ratio_up=0.4,
)
在配置文件中使用默认值的逐步学习率调整,它调用 MMCV 中的 StepLRHook。 此外,也支持其他学习率调整方法,如 CosineAnnealing
和 Poly
。 详情可见 这里
Poly:
lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False)
ConsineAnnealing:
lr_config = dict(
policy='CosineAnnealing',
warmup='linear',
warmup_iters=1000,
warmup_ratio=1.0 / 10,
min_lr_ratio=1e-5)
默认情况下,MMAction2 推荐用户在训练周期中使用 “EvalHook” 进行模型验证,也可以选择 “val” 工作流模型进行模型验证。
工作流是一个形如 (工作流名, 周期数) 的列表,用于指定运行顺序和周期。其默认设置为:
workflow = [('train', 1)]
其代表要进行一轮周期的训练。 有时,用户可能希望检查有关验证集中模型的某些指标(例如,损失,准确性)。 在这种情况下,可以将工作流程设置为
[('train', 1), ('val', 1)]
从而将迭代运行1个训练时间和1个验证时间。
值得注意的是:
1.在验证周期时不会更新模型参数。
2.配置文件内的关键词 total_epochs
控制训练时期数,并且不会影响验证工作流程。
3.工作流 [('train', 1), ('val', 1)]
和 [('train', 1)]
不会改变 EvalHook
的行为。 因为 EvalHook
由 after_train_epoch
调用,而验证工作流只会影响 after_val_epoch
调用的钩子。 因此,[('train', 1), ('val', 1)]
和 [('train', 1)]
的区别在于,runner 在完成每一轮训练后,会计算验证集上的损失。
创建一个新钩子
这里举一个在 MMAction2 中创建一个新钩子,并在训练中使用它的示例:
from mmcv.runner import HOOKS, Hook
@HOOKS.register_module()
class MyHook(Hook):
def __init__(self, a, b):
pass
def before_run(self, runner):
pass
def after_run(self, runner):
pass
def before_epoch(self, runner):
pass
def after_epoch(self, runner):
pass
def before_iter(self, runner):
pass
def after_iter(self, runner):
pass
根据钩子的功能,用户需要指定钩子在训练的每个阶段将要执行的操作,比如 before_run
,after_run
,before_epoch
,after_epoch
,before_iter
和 after_iter
。
注册新钩子
之后,需要导入 MyHook
。假设该文件在 mmaction/core/utils/my_hook.py
,有两种办法导入它:
1-修改 mmaction/core/utils/__init__.py
进行导入
新定义的模块应导入到mmaction/core/utils/__init__py
中,以便注册表能找到并添加新模块:
from .my_hook import MyHook
2- 使用配置文件中的 custom_imports
变量手动导入
custom_imports = dict(imports=['mmaction.core.utils.my_hook'], allow_failed_imports=False)
修改配置
custom_hooks = [
dict(type='MyHook', a=a_value, b=b_value)
]
还可通过 priority 参数(可选参数值包括 'NORMAL'
或 'HIGHEST'
)设置钩子优先级,如下所示:
custom_hooks = [
dict(type='MyHook', a=a_value, b=b_value, priority='NORMAL')
]
默认情况下,在注册过程中,钩子的优先级设置为 “NORMAL”。
如果该钩子已在 MMCV 中实现,则可以直接修改配置以使用该钩子,如下所示
mmcv_hooks = [
dict(type='MMCVHook', a=a_value, b=b_value, priority='NORMAL')
]
有一些常见的钩子未通过 custom_hooks
注册,但在导入 MMCV 时已默认注册,它们是:
log_config
checkpoint_config
evaluation
lr_config
optimizer_config
momentum_config
在这些钩子中,只有 log_config 具有 “VERY_LOW” 优先级,其他钩子具有 “NORMAL” 优先级。 上述教程已经介绍了如何修改 “optimizer_config”,“momentum_config” 和 “lr_config”。 下面介绍如何使用 log_config,checkpoint_config,以及 evaluation 能做什么。
模型权重文件配置
MMCV 的 runner 在这里插入代码片
使用 checkpoint_config
来初始化 CheckpointHook
checkpoint_config = dict(interval=1)
用户可以设置 “max_keep_ckpts” 来仅保存少量模型权重文件,或者通过 “save_optimizer” 决定是否存储优化器的状态字典。 更多细节可参考 这里。
日志配置
log_config 包装了多个记录器钩子,并可以设置间隔。 目前,MMCV 支持 WandbLoggerHook
,MlflowLoggerHook
和 TensorboardLoggerHook
。 更多细节可参考这里。
log_config = dict(
interval=50,
hooks=[
dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook')
])
验证配置
评估的配置将用于初始化 EvalHook。 除了键 interval
外,其他参数,如 “metrics” 也将传递给 dataset.evaluate()
。
evaluation = dict(interval=1, metrics='bbox')
mmaction2 github 官网:https://github.com/open-mmlab/mmaction2
官方中文文档教程:https://mmaction2.readthedocs.io/zh_CN/latest/index.html
官方知乎介绍文章 :https://zhuanlan.zhihu.com/p/347705276
mmaction2: 使用自定义数据集训练 PoseC3D 的教程 :https://github.com/open-mmlab/mmaction2/blob/master/configs/skeleton/posec3d/custom_dataset_training.md