Skip to content

Commit

Permalink
[Doc] Translate some advanced guides (#2499)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZS520L authored Jun 28, 2023
1 parent b9bc067 commit 940349f
Show file tree
Hide file tree
Showing 4 changed files with 434 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/en/advanced_guides/customize_dataset.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ In this tutorial, we will introduce some methods about how to customize your own

## General understanding of the Dataset in MMAction2

MMAction2 provides task-specific `Dataset` class, e.g. `VideoDataset`/`RawframeDataset` for action recognition, `AVADataset` for spatio-temporal action detection, `PoseDataset` for skeleton-based action recognition. These task-specific datasets only require the implementation of `load_data_list(self)` for generating a data list from the annotation file. The remaining functions are automatically handled by the superclass (i.e., `BaseActionDataset` and `BaseDataset`). The following table shows the inherent relationship and the main method of the modules.
MMAction2 provides task-specific `Dataset` class, e.g. `VideoDataset`/`RawframeDataset` for action recognition, `AVADataset` for spatio-temporal action detection, `PoseDataset` for skeleton-based action recognition. These task-specific datasets only require the implementation of `load_data_list(self)` for generating a data list from the annotation file. The remaining functions are automatically handled by the superclass (i.e., `BaseActionDataset` and `BaseDataset`). The following table shows the inheritance relationship and the main method of the modules.

| Class Name | Class Method |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand Down
126 changes: 126 additions & 0 deletions docs/zh_cn/advanced_guides/customize_dataset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# 自定义数据集

在本教程中,我们将介绍如何通过在线转换来自定义你的数据集。

- [自定义数据集](#自定义数据集)
- [MMAction2 数据集概述](#mmaction2-数据集概述)
- [定制新的数据集](#定制新的数据集)
- [为 PoseDataset 自定义关键点格式](#为-posedataset-自定义关键点格式)

## MMAction2 数据集概述

MMAction2 提供了任务特定的 `Dataset` 类,例如用于动作识别的 `VideoDataset`/`RawframeDataset`,用于时空动作检测的 `AVADataset`,用于基于骨骼的动作识别的`PoseDataset`。这些任务特定的数据集只需要实现 `load_data_list(self)` 来从注释文件生成数据列表。剩下的函数由超类(即 `BaseActionDataset``BaseDataset`)自动处理。下表显示了模块的继承关系和主要方法。

| 类名 | 类方法 |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MMAction2::VideoDataset` | `load_data_list(self)` <br> 从注释文件中构建数据列表。 |
| `MMAction2::BaseActionDataset` | `get_data_info(self, idx)` <br> 给定 `idx`,从数据列表中返回相应的数据样本。 |
| `MMEngine::BaseDataset` | `__getitem__(self, idx)` <br> 给定 `idx`,调用 `get_data_info` 获取数据样本,然后调用 `pipeline``train_pipeline``val_pipeline` 中执行数据变换和增强。 |

## 定制新的数据集类

大多数情况下,把你的数据集离线转换成指定格式是首选方法,但 MMAction2 提供了一个方便的过程来创建一个定制的 `Dataset` 类。如前所述,任务特定的数据集只需要实现 `load_data_list(self)` 来从注释文件生成数据列表。请注意,`data_list` 中的元素是包含后续流程中必要字段的 `dict`

`VideoDataset` 为例,`train_pipeline`/`val_pipeline``DecordInit` 中需要 `'filename'`,在 `PackActionInputs` 中需要 `'label'`。因此,`data_list` 中的数据样本必须包含2个字段:`'filename'``'label'`
请参考[定制数据流水线](customize_pipeline.md)以获取有关 `pipeline` 的更多详细信息。

```
data_list.append(dict(filename=filename, label=label))
```

`AVADataset` 会更加复杂,`data_list` 中的数据样本包含有关视频数据的几个字段。此外,它重写了 `get_data_info(self, idx)` 以转换在时空动作检测数据流水线中需要用的字段。

```python

class AVADataset(BaseActionDataset):
...

def load_data_list(self) -> List[dict]:
...
video_info = dict(
frame_dir=frame_dir,
video_id=video_id,
timestamp=int(timestamp),
img_key=img_key,
shot_info=shot_info,
fps=self._FPS,
ann=ann)
data_list.append(video_info)
data_list.append(video_info)
return data_list

def get_data_info(self, idx: int) -> dict:
...
ann = data_info.pop('ann')
data_info['gt_bboxes'] = ann['gt_bboxes']
data_info['gt_labels'] = ann['gt_labels']
data_info['entity_ids'] = ann['entity_ids']
return data_info
```

## 为 PoseDataset 自定义关键点格式

MMAction2 目前支持三种关键点格式:`coco``nturgb+d``openpose`。如果你使用其中一种格式,你可以简单地在以下模块中指定相应的格式:

对于图卷积网络,如 AAGCN,STGCN,...

- `pipeline`:在 `JointToBone` 中的参数 `dataset`
- `backbone`:在图卷积网络中的参数 `graph_cfg`

对于 PoseC3D:

- `pipeline`:在 `Flip` 中,根据关键点的对称关系指定 `left_kp``right_kp`
- `pipeline`:在 `GeneratePoseTarget` 中,如果 `with_limb``True`,指定`skeletons``left_limb``right_limb`,如果 `with_kp``True`,指定`left_kp``right_kp`

如果使用自定义关键点格式,需要在 `backbone``pipeline` 中都包含一个新的图布局。这个布局将定义关键点及其连接关系。

`coco` 数据集为例,我们在 `Graph` 中定义了一个名为 `coco` 的布局。这个布局的 `inward` 连接包括所有节点连接,每个**向心**连接由一个节点元组组成。`coco`的额外设置包括将节点数指定为 `17`,将 `node 0` 设为中心节点。

```python

self.num_node = 17
self.inward = [(15, 13), (13, 11), (16, 14), (14, 12), (11, 5),
(12, 6), (9, 7), (7, 5), (10, 8), (8, 6), (5, 0),
(6, 0), (1, 0), (3, 1), (2, 0), (4, 2)]
self.center = 0
```

同样,我们在 `JointToBone` 中定义了 `pairs`,添加了一个 bone `(0, 0)` 以使 bone 的数量对齐到 joint。coco数据集的 `pairs` 如下所示,`JointToBone` 中的 `pairs` 的顺序无关紧要。

```python

self.pairs = ((0, 0), (1, 0), (2, 0), (3, 1), (4, 2),
(5, 0), (6, 0), (7, 5), (8, 6), (9, 7),
(10, 8), (11, 0), (12, 0), (13, 11), (14, 12),
(15, 13), (16, 14))
```

要使用你的自定义关键点格式,只需定义上述设置为你的图结构,并在你的配置文件中指定它们,如下所示。在这个例子中,我们将使用 `STGCN`,其中 `n` 表示类别的数量,`custom_dataset``Graph``JointToBone` 中定义。

```python
model = dict(
type='RecognizerGCN',
backbone=dict(
type='STGCN', graph_cfg=dict(layout='custom_dataset', mode='stgcn_spatial')),
cls_head=dict(type='GCNHead', num_classes=n, in_channels=256))

train_pipeline = [
...
dict(type='GenSkeFeat', dataset='custom_dataset'),
...]

val_pipeline = [
...
dict(type='GenSkeFeat', dataset='custom_dataset'),
...]

test_pipeline = [
...
dict(type='GenSkeFeat', dataset='custom_dataset'),
...]

```

只需简单地指定自定义布局,你就可以使用你自己的关键点格式进行训练和测试了。通过这种方式,MMAction2 为用户提供了很大的灵活性,允许用户自定义他们的数据集和关键点格式,以满足他们特定的需求。

以上就是关于如何自定义你的数据集的一些方法。希望这个教程能帮助你理解MMAction2的数据集结构,并教给你如何根据自己的需求创建新的数据集。虽然这可能需要一些编程知识,但是 MMAction2 试图使这个过程尽可能简单。通过了解这些基本概念,你将能够更好地控制你的数据,从而改进你的模型性能。
163 changes: 163 additions & 0 deletions docs/zh_cn/advanced_guides/customize_logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# 自定义日志

MMAction2 在运行过程中会产生大量的日志,如损失、迭代时间、学习率等。在这一部分,我们将向你介绍如何输出自定义日志。有关日志系统的更多详细信息,请参考 [MMEngine 教程](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/logging.html)

- [自定义日志](#自定义日志)
- [灵活的日志系统](#灵活的日志系统)
- [定制日志](#定制日志)
- [导出调试日志](#导出调试日志)

## 灵活的日志系统

默认情况下,MMAction2 的日志系统由 [default_runtime](/configs/_base_/default_runtime.py) 中的 `LogProcessor` 配置:

```python
log_processor = dict(type='LogProcessor', window_size=20, by_epoch=True)
```

默认情况下,`LogProcessor` 捕获 `model.forward` 返回的所有以 `loss` 开头的字段。例如,在以下模型中,`loss1``loss2` 将在没有任何额外配置的情况下自动记录到日志。

```python
from mmengine.model import BaseModel

class ToyModel(BaseModel):
def __init__(self) -> None:
super().__init__()
self.linear = nn.Linear(1, 1)

def forward(self, img, label, mode):
feat = self.linear(img)
loss1 = (feat - label).pow(2)
loss2 = (feat - label).abs()
return dict(loss1=loss1, loss2=loss2)
```

输出日志遵循以下格式:

```
08/21 02:58:41 - mmengine - INFO - Epoch(train) [1][10/25] lr: 1.0000e-02 eta: 0:00:00 time: 0.0019 data_time: 0.0004 loss1: 0.8381 loss2: 0.9007 loss: 1.7388
08/21 02:58:41 - mmengine - INFO - Epoch(train) [1][20/25] lr: 1.0000e-02 eta: 0:00:00 time: 0.0029 data_time: 0.0010 loss1: 0.1978 loss2: 0.4312 loss: 0.6290
```

`LogProcessor` 将按以下格式输出日志:

- 日志的前缀:
- epoch 模式(`by_epoch=True`):`Epoch(train) [{current_epoch}/{current_iteration}]/{dataloader_length}`
- iteration 模式(`by_epoch=False`):`Iter(train) [{current_iteration}/{max_iteration}]`
- 学习率 (`lr`):最后一次迭代的学习率。
- 时间:
- `time`:过去 `window_size` 次迭代的推理平均时间。
- `data_time`:过去 `window_size` 次迭代的数据加载平均时间。
- `eta`:完成训练的预计到达时间。
- 损失:过去 `window_size` 次迭代中模型输出的平均损失。

```{warning}
默认情况下,log_processor 输出基于 epoch 的日志(`by_epoch=True`)。要得到与 `train_cfg` 匹配的预期日志,我们应在 `train_cfg` 和 `log_processor` 中设置相同的 `by_epoch` 值。
```

根据以上规则,代码片段将每20次迭代计算 loss1 和 loss2 的平均值。更多类型的统计方法,请参考 [mmengine.runner.LogProcessor](mmengine.runner.LogProcessor)

## 定制日志

日志系统不仅可以记录 `loss``lr` 等,还可以收集和输出自定义日志。例如,如果我们想要统计中间损失:

`ToyModel` 在 forward 中计算 `loss_tmp`,但不将其保存到返回字典中。

```python
from mmengine.logging import MessageHub

class ToyModel(BaseModel):

def __init__(self) -> None:
super().__init__()
self.linear = nn.Linear(1, 1)

def forward(self, img, label, mode):
feat = self.linear(img)
loss_tmp = (feat - label).abs()
loss = loss_tmp.pow(2)

message_hub = MessageHub.get_current_instance()
# 在消息中心更新中间的 `loss_tmp`
message_hub.update_scalar('train/loss_tmp', loss_tmp.sum())
return dict(loss=loss)
```

`loss_tmp` 添加到配置中:

```python
log_processor = dict(
type='LogProcessor',
window_size=20,
by_epoch=True,
custom_cfg=[
# 使用平均值统计 loss_tmp
dict(
data_src='loss_tmp',
window_size=20,
method_name='mean')
])
```

`loss_tmp` 将被添加到输出日志中:

```
08/21 03:40:31 - mmengine - INFO - Epoch(train) [1][10/25] lr: 1.0000e-02 eta: 0:00:00 time: 0.0026 data_time: 0.0008 loss_tmp: 0.0097 loss: 0.0000
08/21 03:40:31 - mmengine - INFO - Epoch(train) [1][20/25] lr: 1.0000e-02 eta: 0:00:00 time: 0.0028 data_time: 0.0013 loss_tmp: 0.0065 loss: 0.0000
```

## 导出调试日志

要将调试日志导出到 `work_dir`,你可以在配置文件中设置日志级别如下:

```
log_level='DEBUG'
```

```
08/21 18:16:22 - mmengine - DEBUG - Get class `LocalVisBackend` from "vis_backend" registry in "mmengine"
08/21 18:16:22 - mmengine - DEBUG - An `LocalVisBackend` instance is built from registry, its implementation can be found in mmengine.visualization.vis_backend
08/21 18:16:22 - mmengine - DEBUG - Get class `RuntimeInfoHook` from "hook" registry in "mmengine"
08/21 18:16:22 - mmengine - DEBUG - An `RuntimeInfoHook` instance is built from registry, its implementation can be found in mmengine.hooks.runtime_info_hook
08/21 18:16:22 - mmengine - DEBUG - Get class `IterTimerHook` from "hook" registry in "mmengine"
...
```

此外,如果你正在使用共享存储训练你的模型,那么在 `debug` 模式下,不同排名的日志将被保存。日志的层级结构如下:

```text
./tmp
├── tmp.log
├── tmp_rank1.log
├── tmp_rank2.log
├── tmp_rank3.log
├── tmp_rank4.log
├── tmp_rank5.log
├── tmp_rank6.log
└── tmp_rank7.log
...
└── tmp_rank63.log
```

在具有独立存储的多台机器上的日志:

```text
# 设备:0:
work_dir/
└── exp_name_logs
├── exp_name.log
├── exp_name_rank1.log
├── exp_name_rank2.log
├── exp_name_rank3.log
...
└── exp_name_rank7.log
# 设备:7:
work_dir/
└── exp_name_logs
├── exp_name_rank56.log
├── exp_name_rank57.log
├── exp_name_rank58.log
...
└── exp_name_rank63.log
```
Loading

0 comments on commit 940349f

Please sign in to comment.