config.md 28.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 教程 1: 学习配置文件

我们在配置文件中支持了继承和模块化来方便进行各种实验。
如果需要检查配置文件,可以通过运行 `python tools/misc/print_config.py /PATH/TO/CONFIG` 来查看完整的配置。
你也可以传入 `--options xxx.yyy=zzz` 参数来查看更新后的配置。

## 配置文件结构

`config/_base_` 文件夹下有 4 个基本组件类型,分别是:数据集 (dataset),模型 (model),训练策略 (schedule) 和运行时的默认设置 (default runtime)。
通过从上述每个文件夹中选取一个组件进行组合,许多方法如 SECOND、PointPillars、PartA2 和 VoteNet 都能够很容易地构建出来。
`_base_` 下的组件组成的配置,被我们称为 _原始配置 (primitive)_。

对于同一文件夹下的所有配置,推荐**只有一个**对应的 _原始配置_ 文件,所有其他的配置文件都应该继承自这个 _原始配置_ 文件,这样就能保证配置文件的最大继承深度为 3。

为了便于理解,我们建议贡献者继承现有方法。
例如,如果在 PointPillars 的基础上做了一些修改,用户首先可以通过指定 `_base_ = ../pointpillars/hv_pointpillars_fpn_sbn-all_4x8_2x_nus-3d.py` 来继承基础的 PointPillars 结构,然后修改配置文件中的必要参数以完成继承。

如果你在构建一个与任何现有方法不共享结构的全新方法,可以在 `configs` 文件夹下创建一个新的例如 `xxx_rcnn` 文件夹。

更多细节请参考 [MMCV](https://mmcv.readthedocs.io/en/latest/understand_mmcv/config.html) 文档。

## 配置文件名称风格

我们遵循以下样式来命名配置文件,并建议贡献者遵循相同的风格。

```
{model}_[model setting]_{backbone}_{neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{schedule}_{dataset}
```

`{xxx}` 是被要求填写的字段而 `[yyy]` 是可选的。

- `{model}`:模型种类,例如 `hv_pointpillars` (Hard Voxelization PointPillars)、`VoteNet` 等。
- `[model setting]`:某些模型的特殊设定。
- `{backbone}`: 主干网络种类例如 `regnet-400mf``regnet-1.6gf` 等。
- `{neck}`:模型颈部的种类包括 `fpn``secfpn` 等。
- `[norm_setting]`:如无特殊声明,默认使用 `bn` (Batch Normalization),其他类型可以有 `gn` (Group Normalization)、`sbn` (Synchronized Batch Normalization) 等。
37
  `gn-head`/`gn-neck` 表示 GN 仅应用于网络的头部或颈部,而 `gn-all` 表示 GN 用于整个模型,例如主干网络、颈部和头部。
38
39
40
- `[misc]`:模型中各式各样的设置/插件,例如 `strong-aug` 意味着在训练过程中使用更强的数据增广策略。
- `[batch_per_gpu x gpu]`:每个 GPU 的样本数和 GPU 数量,默认使用 `4x8`
- `{schedule}`:训练方案,选项是 `1x``2x``20e` 等。
41
42
43
  `1x``2x` 分别代表训练 12 和 24 轮。
  `20e` 在级联模型中使用,表示训练 20 轮。
  对于 `1x`/`2x`,初始学习率在第 8/16 和第 11/22 轮衰减 10 倍;对于 `20e`,初始学习率在第 16 和第 19 轮衰减 10 倍。
44
- `{dataset}`:数据集,例如 `nus-3d``kitti-3d``lyft-3d``scannet-3d``sunrgbd-3d` 等。
45
  当某一数据集存在多种设定时,我们也标记下所使用的类别数量,例如 `kitti-3d-3class``kitti-3d-car` 分别意味着在 KITTI 的所有三类上和单独车这一类上进行训练。
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

## 弃用的 train_cfg/test_cfg

遵循 MMDetection 的做法,我们在配置文件中弃用 `train_cfg``test_cfg`,请在模型配置中指定它们。
原始的配置结构如下:

```python
# 已经弃用的形式
model = dict(
   type=...,
   ...
)
train_cfg=dict(...)
test_cfg=dict(...)
```

迁移后的配置结构如下:

```python
# 推荐的形式
model = dict(
   type=...,
   ...
   train_cfg=dict(...),
   test_cfg=dict(...),
)
```

## VoteNet 配置文件示例

```python
model = dict(
    type='VoteNet',  # 检测器的类型,更多细节请参考 mmdet3d.models.detectors
    backbone=dict(
        type='PointNet2SASSG',  # 主干网络的类型,更多细节请参考 mmdet3d.models.backbones
        in_channels=4,  # 点云输入通道数
        num_points=(2048, 1024, 512, 256),  # 每个 SA 模块采样的中心点的数量
        radius=(0.2, 0.4, 0.8, 1.2),  # 每个 SA 层的半径
        num_samples=(64, 32, 16, 16),  # 每个 SA 层聚集的点的数量
        sa_channels=((64, 64, 128), (128, 128, 256), (128, 128, 256),
                     (128, 128, 256)),  # SA 模块中每个多层感知器的输出通道数
        fp_channels=((256, 256), (256, 256)),  # FP 模块中每个多层感知器的输出通道数
        norm_cfg=dict(type='BN2d'),  # 归一化层的配置
        sa_cfg=dict(  # 点集抽象 (SA) 模块的配置
            type='PointSAModule',  # SA 模块的类型
            pool_mod='max',  # SA 模块的池化方法 (最大池化或平均池化)
            use_xyz=True,  # 在特征聚合中是否使用 xyz 坐标
            normalize_xyz=True)),  # 在特征聚合中是否使用标准化的 xyz 坐标
    bbox_head=dict(
        type='VoteHead',  # 检测框头的类型,更多细节请参考 mmdet3d.models.dense_heads
        num_classes=18,  # 分类的类别数量
        bbox_coder=dict(
            type='PartialBinBasedBBoxCoder',  # 框编码层的类型,更多细节请参考 mmdet3d.core.bbox.coders
            num_sizes=18,  # 尺寸聚类的数量
            num_dir_bins=1,   # 编码方向角的间隔数
            with_rot=False,  # 框是否带有旋转角度
            mean_sizes=[[0.76966727, 0.8116021, 0.92573744],
                        [1.876858, 1.8425595, 1.1931566],
                        [0.61328, 0.6148609, 0.7182701],
                        [1.3955007, 1.5121545, 0.83443564],
                        [0.97949594, 1.0675149, 0.6329687],
                        [0.531663, 0.5955577, 1.7500148],
                        [0.9624706, 0.72462326, 1.1481868],
                        [0.83221924, 1.0490936, 1.6875663],
                        [0.21132214, 0.4206159, 0.5372846],
                        [1.4440073, 1.8970833, 0.26985747],
                        [1.0294262, 1.4040797, 0.87554324],
                        [1.3766412, 0.65521795, 1.6813129],
                        [0.6650819, 0.71111923, 1.298853],
                        [0.41999173, 0.37906948, 1.7513971],
                        [0.59359556, 0.5912492, 0.73919016],
                        [0.50867593, 0.50656086, 0.30136237],
                        [1.1511526, 1.0546296, 0.49706793],
                        [0.47535285, 0.49249494, 0.5802117]]),  # 每一类的平均尺寸,其顺序与类名顺序相同
zhangshilong's avatar
zhangshilong committed
120
        vote_moudule_cfg=dict(  # 投票 (vote) 模块的配置,更多细节请参考 mmdet3d.models.layers
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
            in_channels=256,  # 投票模块的输入通道数
            vote_per_seed=1,  # 对于每个种子点生成的投票数
            gt_per_seed=3,  # 每个种子点的真实标签个数
            conv_channels=(256, 256),  # 卷积通道数
            conv_cfg=dict(type='Conv1d'),  # 卷积配置
            norm_cfg=dict(type='BN1d'),  # 归一化层配置
            norm_feats=True,  # 是否标准化特征
            vote_loss=dict(  # 投票分支的损失函数配置
                type='ChamferDistance',  # 投票分支的损失函数类型
                mode='l1',  # 投票分支的损失函数模式
                reduction='none',  # 设置对损失函数输出的聚合方法
                loss_dst_weight=10.0)),  # 投票分支的目标损失权重
        vote_aggregation_cfg=dict(  # 投票聚合分支的配置
            type='PointSAModule',  # 投票聚合模块的类型
            num_point=256,  # 投票聚合分支中 SA 模块的点的数量
            radius=0.3,  # 投票聚合分支中 SA 模块的半径
            num_sample=16,  # 投票聚合分支中 SA 模块的采样点的数量
            mlp_channels=[256, 128, 128, 128],  # 投票聚合分支中 SA 模块的多层感知器的通道数
            use_xyz=True,  # 是否使用 xyz 坐标
            normalize_xyz=True),  # 是否使用标准化后的 xyz 坐标
        feat_channels=(128, 128),  # 特征卷积的通道数
        conv_cfg=dict(type='Conv1d'),  # 卷积的配置
        norm_cfg=dict(type='BN1d'),  # 归一化层的配置
        objectness_loss=dict(  # 物体性 (objectness) 损失函数的配置
            type='CrossEntropyLoss',  # 损失函数类型
            class_weight=[0.2, 0.8],  # 损失函数对每一类的权重
            reduction='sum',  # 设置损失函数输出的聚合方法
            loss_weight=5.0),  # 损失函数权重
        center_loss=dict(  # 中心 (center) 损失函数的配置
            type='ChamferDistance',  # 损失函数类型
            mode='l2',  # 损失函数模式
            reduction='sum',  # 设置损失函数输出的聚合方法
            loss_src_weight=10.0,  # 源损失权重
            loss_dst_weight=10.0),  # 目标损失权重
        dir_class_loss=dict(  # 方向分类损失函数的配置
            type='CrossEntropyLoss',  # 损失函数类型
            reduction='sum',  # 设置损失函数输出的聚合方法
            loss_weight=1.0),  # 损失函数权重
        dir_res_loss=dict(  # 方向残差 (residual) 损失函数的配置
            type='SmoothL1Loss',  # 损失函数类型
            reduction='sum',  # 设置损失函数输出的聚合方法
            loss_weight=10.0),  # 损失函数权重
        size_class_loss=dict(  # 尺寸分类损失函数的配置
            type='CrossEntropyLoss',  # 损失函数类型
            reduction='sum',  # 设置损失函数输出的聚合方法
            loss_weight=1.0),  # 损失函数权重
        size_res_loss=dict(  # 尺寸残差损失函数的配置
            type='SmoothL1Loss',  # 损失函数类型
            reduction='sum',  # 设置损失函数输出的聚合方法
            loss_weight=3.3333333333333335),  # 损失函数权重
        semantic_loss=dict(  # 语义损失函数的配置
            type='CrossEntropyLoss',  # 损失函数类型
            reduction='sum',  # 设置损失函数输出的聚合方法
            loss_weight=1.0)),  # 损失函数权重
    train_cfg = dict(  # VoteNet 训练的超参数配置
        pos_distance_thr=0.3,  # 距离 >= 0.3 阈值的样本将被视为正样本
        neg_distance_thr=0.6,  # 距离 < 0.6 阈值的样本将被视为负样本
        sample_mod='vote'),  # 采样方法的模式
    test_cfg = dict(  # VoteNet 测试的超参数配置
        sample_mod='seed',  # 采样方法的模式
        nms_thr=0.25,  # NMS 中使用的阈值
        score_thr=0.8,  # 剔除框的阈值
        per_class_proposal=False))  # 是否使用逐类提议框 (proposal)
dataset_type = 'ScanNetDataset'  # 数据集类型
data_root = './data/scannet/'  # 数据路径
class_names = ('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', 'window',
               'bookshelf', 'picture', 'counter', 'desk', 'curtain',
               'refrigerator', 'showercurtrain', 'toilet', 'sink', 'bathtub',
               'garbagebin')  # 类的名称
zhangshilong's avatar
zhangshilong committed
190
train_pipeline = [  # 训练流水线,更多细节请参考 mmdet3d.datasets.transforms
191
    dict(
zhangshilong's avatar
zhangshilong committed
192
        type='LoadPointsFromFile',  # 第一个流程,用于读取点,更多细节请参考 mmdet3d.datasets.transforms.indoor_loading
193
194
195
196
        shift_height=True,  # 是否使用变换高度
        load_dim=6,  # 读取的点的维度
        use_dim=[0, 1, 2]),  # 使用所读取点的哪些维度
    dict(
zhangshilong's avatar
zhangshilong committed
197
        type='LoadAnnotations3D',  # 第二个流程,用于读取标注,更多细节请参考 mmdet3d.datasets.transforms.indoor_loading
198
199
200
201
202
        with_bbox_3d=True,  # 是否读取 3D 框
        with_label_3d=True,  # 是否读取 3D 框对应的类别标签
        with_mask_3d=True,  # 是否读取 3D 实例分割掩码
        with_seg_3d=True),  # 是否读取 3D 语义分割掩码
    dict(
zhangshilong's avatar
zhangshilong committed
203
        type='PointSegClassMapping',  # 选取有效的类别,更多细节请参考 mmdet3d.datasets.transforms.point_seg_class_mapping
204
205
206
        valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
                       36, 39),  # 所有有效类别的编号
        max_cat_id=40),  # 输入语义分割掩码中可能存在的最大类别编号
zhangshilong's avatar
zhangshilong committed
207
    dict(type='PointSample',  # 室内点采样,更多细节请参考 mmdet3d.datasets.transforms.indoor_sample
208
209
210
211
212
            num_points=40000),  # 采样的点的数量
    dict(type='IndoorFlipData',  # 数据增广流程,随机翻转点和 3D 框
        flip_ratio_yz=0.5,  # 沿着 yz 平面被翻转的概率
        flip_ratio_xz=0.5),  # 沿着 xz 平面被翻转的概率
    dict(
zhangshilong's avatar
zhangshilong committed
213
        type='IndoorGlobalRotScale',  # 数据增广流程,旋转并放缩点和 3D 框,更多细节请参考 mmdet3d.datasets.transforms.indoor_augment
214
215
216
217
        shift_height=True,  # 读取的点是否有高度这一属性
        rot_range=[-0.027777777777777776, 0.027777777777777776],  # 旋转角范围
        scale_range=None),  # 缩放尺寸范围
    dict(
zhangshilong's avatar
zhangshilong committed
218
        type='DefaultFormatBundle3D',  # 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.transforms.formatting
219
220
221
222
223
        class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door',
                     'window', 'bookshelf', 'picture', 'counter', 'desk',
                     'curtain', 'refrigerator', 'showercurtrain', 'toilet',
                     'sink', 'bathtub', 'garbagebin')),
    dict(
zhangshilong's avatar
zhangshilong committed
224
        type='Collect3D',  # 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.transforms.formatting
225
226
227
228
229
        keys=[
            'points', 'gt_bboxes_3d', 'gt_labels_3d', 'pts_semantic_mask',
            'pts_instance_mask'
        ])
]
zhangshilong's avatar
zhangshilong committed
230
test_pipeline = [  # 测试流水线,更多细节请参考 mmdet3d.datasets.transforms
231
    dict(
zhangshilong's avatar
zhangshilong committed
232
        type='LoadPointsFromFile',  # 第一个流程,用于读取点,更多细节请参考 mmdet3d.datasets.transforms.indoor_loading
233
234
235
        shift_height=True,  # 是否使用变换高度
        load_dim=6,  # 读取的点的维度
        use_dim=[0, 1, 2]),  # 使用所读取点的哪些维度
zhangshilong's avatar
zhangshilong committed
236
    dict(type='PointSample',  # 室内点采样,更多细节请参考 mmdet3d.datasets.transforms.indoor_sample
237
238
            num_points=40000),  # 采样的点的数量
    dict(
zhangshilong's avatar
zhangshilong committed
239
        type='DefaultFormatBundle3D',  # 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.transforms.formatting
240
241
242
243
        class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door',
                     'window', 'bookshelf', 'picture', 'counter', 'desk',
                     'curtain', 'refrigerator', 'showercurtrain', 'toilet',
                     'sink', 'bathtub', 'garbagebin')),
zhangshilong's avatar
zhangshilong committed
244
    dict(type='Collect3D',  # 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.transforms.formatting
245
246
        keys=['points'])
]
zhangshilong's avatar
zhangshilong committed
247
eval_pipeline = [  # 模型验证或可视化所使用的流水线,更多细节请参考 mmdet3d.datasets.transforms
248
    dict(
zhangshilong's avatar
zhangshilong committed
249
        type='LoadPointsFromFile',  # 第一个流程,用于读取点,更多细节请参考 mmdet3d.datasets.transforms.indoor_loading
250
251
252
253
        shift_height=True,  # 是否使用变换高度
        load_dim=6,  # 读取的点的维度
        use_dim=[0, 1, 2]),  # 使用所读取点的哪些维度
    dict(
zhangshilong's avatar
zhangshilong committed
254
        type='DefaultFormatBundle3D',  # 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.transforms.formatting
255
256
257
258
259
        class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door',
                     'window', 'bookshelf', 'picture', 'counter', 'desk',
                     'curtain', 'refrigerator', 'showercurtrain', 'toilet',
                     'sink', 'bathtub', 'garbagebin')),
        with_label=False),
zhangshilong's avatar
zhangshilong committed
260
    dict(type='Collect3D',  # 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.transforms.formatting
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
        keys=['points'])
]
data = dict(
    samples_per_gpu=8,  # 单张 GPU 上的样本数
    workers_per_gpu=4,  # 每张 GPU 上用于读取数据的进程数
    train=dict(  # 训练数据集配置
        type='RepeatDataset',  # 数据集嵌套,更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/master/mmdet/datasets/dataset_wrappers.py
        times=5,  # 重复次数
        dataset=dict(
            type='ScanNetDataset',  # 数据集类型
            data_root='./data/scannet/',  # 数据路径
            ann_file='./data/scannet/scannet_infos_train.pkl',  # 数据标注文件的路径
            pipeline=[  # 流水线,这里传入的就是上面创建的训练流水线变量
                dict(
                    type='LoadPointsFromFile',
                    shift_height=True,
                    load_dim=6,
                    use_dim=[0, 1, 2]),
                dict(
                    type='LoadAnnotations3D',
                    with_bbox_3d=True,
                    with_label_3d=True,
                    with_mask_3d=True,
                    with_seg_3d=True),
                dict(
                    type='PointSegClassMapping',
                    valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24,
                                   28, 33, 34, 36, 39),
                    max_cat_id=40),
290
                dict(type='PointSample', num_points=40000),
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
                dict(
                    type='IndoorFlipData',
                    flip_ratio_yz=0.5,
                    flip_ratio_xz=0.5),
                dict(
                    type='IndoorGlobalRotScale',
                    shift_height=True,
                    rot_range=[-0.027777777777777776, 0.027777777777777776],
                    scale_range=None),
                dict(
                    type='DefaultFormatBundle3D',
                    class_names=('cabinet', 'bed', 'chair', 'sofa', 'table',
                                 'door', 'window', 'bookshelf', 'picture',
                                 'counter', 'desk', 'curtain', 'refrigerator',
                                 'showercurtrain', 'toilet', 'sink', 'bathtub',
                                 'garbagebin')),
                dict(
                    type='Collect3D',
                    keys=[
                        'points', 'gt_bboxes_3d', 'gt_labels_3d',
                        'pts_semantic_mask', 'pts_instance_mask'
                    ])
            ],
            filter_empty_gt=False,  # 是否过滤掉空的标签框
            classes=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door',
                     'window', 'bookshelf', 'picture', 'counter', 'desk',
                     'curtain', 'refrigerator', 'showercurtrain', 'toilet',
                     'sink', 'bathtub', 'garbagebin'))),  # 类别名称
    val=dict(  # 验证数据集配置
        type='ScanNetDataset',  # 数据集类型
        data_root='./data/scannet/',  # 数据路径
        ann_file='./data/scannet/scannet_infos_val.pkl',  # 数据标注文件的路径
        pipeline=[  # 流水线,这里传入的就是上面创建的测试流水线变量
            dict(
                type='LoadPointsFromFile',
                shift_height=True,
                load_dim=6,
                use_dim=[0, 1, 2]),
329
            dict(type='PointSample', num_points=40000),
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
            dict(
                type='DefaultFormatBundle3D',
                class_names=('cabinet', 'bed', 'chair', 'sofa', 'table',
                             'door', 'window', 'bookshelf', 'picture',
                             'counter', 'desk', 'curtain', 'refrigerator',
                             'showercurtrain', 'toilet', 'sink', 'bathtub',
                             'garbagebin')),
            dict(type='Collect3D', keys=['points'])
        ],
        classes=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', 'window',
                 'bookshelf', 'picture', 'counter', 'desk', 'curtain',
                 'refrigerator', 'showercurtrain', 'toilet', 'sink', 'bathtub',
                 'garbagebin'),  # 类别名称
        test_mode=True),  # 是否开启测试模式
    test=dict(  # 测试数据集配置
        type='ScanNetDataset',  # 数据集类型
        data_root='./data/scannet/',  # 数据路径
        ann_file='./data/scannet/scannet_infos_val.pkl',  # 数据标注文件的路径
        pipeline=[  # 流水线,这里传入的就是上面创建的测试流水线变量
            dict(
                type='LoadPointsFromFile',
                shift_height=True,
                load_dim=6,
                use_dim=[0, 1, 2]),
354
            dict(type='PointSample', num_points=40000),
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
            dict(
                type='DefaultFormatBundle3D',
                class_names=('cabinet', 'bed', 'chair', 'sofa', 'table',
                             'door', 'window', 'bookshelf', 'picture',
                             'counter', 'desk', 'curtain', 'refrigerator',
                             'showercurtrain', 'toilet', 'sink', 'bathtub',
                             'garbagebin')),
            dict(type='Collect3D', keys=['points'])
        ],
        classes=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', 'window',
                 'bookshelf', 'picture', 'counter', 'desk', 'curtain',
                 'refrigerator', 'showercurtrain', 'toilet', 'sink', 'bathtub',
                 'garbagebin'),  # 类别名称
        test_mode=True))  # 是否开启测试模式
evaluation = dict(pipeline=[  # 流水线,这里传入的就是上面创建的验证流水线变量
    dict(
        type='LoadPointsFromFile',
        coord_type='DEPTH',
        shift_height=False,
        load_dim=6,
        use_dim=[0, 1, 2]),
    dict(
        type='DefaultFormatBundle3D',
        class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door',
                     'window', 'bookshelf', 'picture', 'counter', 'desk',
                     'curtain', 'refrigerator', 'showercurtrain', 'toilet',
                     'sink', 'bathtub', 'garbagebin'),
        with_label=False),
    dict(type='Collect3D', keys=['points'])
])
lr = 0.008  # 优化器的学习率
optimizer = dict(  # 构建优化器所使用的配置,我们支持所有 PyTorch 中支持的优化器,并且拥有相同的参数名称
    type='Adam',  # 优化器类型,更多细节请参考 https://github.com/open-mmlab/mmcv/blob/v1.3.7/mmcv/runner/optimizer/default_constructor.py#L12
    lr=0.008)  # 优化器的学习率,用户可以在 PyTorch 文档中查看这些参数的详细使用方法
optimizer_config = dict(  # 构建优化器钩子的配置,更多实现细节可参考 https://github.com/open-mmlab/mmcv/blob/v1.3.7/mmcv/runner/hooks/optimizer.py#L22
    grad_clip=dict(  # 梯度裁剪的配置
    max_norm=10,  # 梯度的最大模长
    norm_type=2))  # 所使用的 p-范数的类型,可以设置成 'inf' 则指代无穷范数
lr_config = dict(  # 学习率策略配置,用于注册学习率更新的钩子
    policy='step',  # 学习率调整的策略,支持 CosineAnnealing、Cyclic 等,更多支持的种类请参考 https://github.com/open-mmlab/mmcv/blob/v1.3.7/mmcv/runner/hooks/lr_updater.py#L9
    warmup=None,  # Warmup 策略,同时也支持 `exp` 和 `constant`
    step=[24, 32])  # 学习率衰减的步数
checkpoint_config = dict(  # 设置保存模型权重钩子的配置,具体实现请参考 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/checkpoint.py
    interval=1)  # 保存模型权重的间隔是 1 轮
log_config = dict(  # 用于注册输出记录信息钩子的配置
    interval=50,  # 输出记录信息的间隔
    hooks=[dict(type='TextLoggerHook'),
           dict(type='TensorboardLoggerHook')])  # 用于记录训练过程的信息记录机制
runner = dict(type='EpochBasedRunner', max_epochs=36) # 程序运行器,将会运行 `workflow` `max_epochs` 次
dist_params = dict(backend='nccl')  # 设置分布式训练的配置,通讯端口值也可被设置
log_level = 'INFO'  # 输出记录信息的等级
find_unused_parameters = True  # 是否查找模型中未使用的参数
work_dir = None  # 当前实验存储模型权重和输出信息的路径
load_from = None # 从指定路径读取一个预训练的模型权重,这将不会继续 (resume) 训练
resume_from = None  # 从一个指定路径读入模型权重并继续训练,这意味着训练轮数、优化器状态等都将被读取
workflow = [('train', 1)]  # 要运行的工作流。[('train', 1)] 意味着只有一个名为 'train' 的工作流,它只会被执行一次。这一工作流依据 `max_epochs` 的值将会训练模型 36 轮。
gpu_ids = range(0, 1)  # 所使用的 GPU 编号
```

## 常问问题 (FAQ)

### 忽略基础配置文件里的部分内容

有时,您也许会需要通过设置 `_delete_=True` 来忽略基础配置文件里的一些域内容。
请参照 [mmcv](https://mmcv.readthedocs.io/en/latest/understand_mmcv/config.html#inherit-from-base-config-with-ignored-fields) 来获得一些简单的指导。

例如在 MMDetection3D 中,为了改变如下所示 PointPillars FPN 模块的某些配置:

```python
model = dict(
    type='MVXFasterRCNN',
    pts_voxel_layer=dict(...),
    pts_voxel_encoder=dict(...),
    pts_middle_encoder=dict(...),
    pts_backbone=dict(...),
    pts_neck=dict(
        type='FPN',
        norm_cfg=dict(type='naiveSyncBN2d', eps=1e-3, momentum=0.01),
        act_cfg=dict(type='ReLU'),
        in_channels=[64, 128, 256],
        out_channels=256,
        start_level=0,
        num_outs=3),
    pts_bbox_head=dict(...))
```

`FPN``SECONDFPN` 使用不同的关键词来构建。

```python
_base_ = '../_base_/models/hv_pointpillars_fpn_nus.py'
model = dict(
    pts_neck=dict(
        _delete_=True,
        type='SECONDFPN',
        norm_cfg=dict(type='naiveSyncBN2d', eps=1e-3, momentum=0.01),
        in_channels=[64, 128, 256],
        upsample_strides=[1, 2, 4],
        out_channels=[128, 128, 128]),
    pts_bbox_head=dict(...))
```

`_delete_=True` 的标识将会使用新的键值覆盖掉 `pts_neck` 中的所有旧键值。

### 使用配置文件里的中间变量

配置文件里会使用一些中间变量,例如数据集中的 `train_pipeline`/`test_pipeline`
值得注意的是,当修改子配置文件中的中间变量后,用户还需再次将其传入相应字段。
例如,我们想在训练和测试中,对 PointPillars 使用多尺度策略 (multi scale strategy),那么 `train_pipeline`/`test_pipeline` 就是我们想要修改的中间变量。

```python
_base_ = './nus-3d.py'
train_pipeline = [
    dict(
        type='LoadPointsFromFile',
        load_dim=5,
        use_dim=5,
        file_client_args=file_client_args),
    dict(
        type='LoadPointsFromMultiSweeps',
        sweeps_num=10,
        file_client_args=file_client_args),
    dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
    dict(
        type='GlobalRotScaleTrans',
        rot_range=[-0.3925, 0.3925],
        scale_ratio_range=[0.95, 1.05],
        translation_std=[0, 0, 0]),
    dict(type='RandomFlip3D', flip_ratio_bev_horizontal=0.5),
    dict(type='PointsRangeFilter', point_cloud_range=point_cloud_range),
    dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range),
    dict(type='ObjectNameFilter', classes=class_names),
    dict(type='PointShuffle'),
    dict(type='DefaultFormatBundle3D', class_names=class_names),
    dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
]
test_pipeline = [
    dict(
        type='LoadPointsFromFile',
        load_dim=5,
        use_dim=5,
        file_client_args=file_client_args),
    dict(
        type='LoadPointsFromMultiSweeps',
        sweeps_num=10,
        file_client_args=file_client_args),
    dict(
        type='MultiScaleFlipAug3D',
        img_scale=(1333, 800),
        pts_scale_ratio=[0.95, 1.0, 1.05],
        flip=False,
        transforms=[
            dict(
                type='GlobalRotScaleTrans',
                rot_range=[0, 0],
                scale_ratio_range=[1., 1.],
                translation_std=[0, 0, 0]),
            dict(type='RandomFlip3D'),
            dict(
                type='PointsRangeFilter', point_cloud_range=point_cloud_range),
            dict(
                type='DefaultFormatBundle3D',
                class_names=class_names,
                with_label=False),
            dict(type='Collect3D', keys=['points'])
        ])
]
data = dict(
    train=dict(pipeline=train_pipeline),
    val=dict(pipeline=test_pipeline),
    test=dict(pipeline=test_pipeline))
```

这里,我们首先定义了新的 `train_pipeline`/`test_pipeline`,然后将其传入 `data`