Commit b6df0d33 authored by limm's avatar limm
Browse files

add resources part

parent cbc25585
Pipeline #2802 canceled with stages
This source diff could not be displayed because it is too large. You can view the blob instead.
# 迁移文档
我们在 MMPretrain 1.x 版本中引入了一些修改,可能会产生兼容性问题。请按照本教程从 MMClassification 0.x 或是 MMSelfSup 0.x 迁移您的项目。
## 新的依赖
```{warning}
MMPretrain 1.x 版本依赖于一些新的代码包,您应该根据 [安装教程](./get_started.md) 来创建新的环境,尽管你可能已经拥有了一个可以正常运行 MMClassification 0.x 或 MMSelfSup 0.x 的环境。请参考[安装文档](./get_started.md) 对依赖库进行对应的安装。
```
1. [MMEngine](https://github.com/open-mmlab/mmengine):MMEngine 是 OpenMMLab 2.0 架构的核心库,我们将许多与计算机视觉无关的组件从 MMCV 拆分到了 MMEngine。
2. [MMCV](https://github.com/open-mmlab/mmcv):OpenMMLab 计算机视觉基础库,这不是一个新的依赖,但你需要将其升级到 `2.0.0rc1` 版本以上。
3. [rich](https://github.com/Textualize/rich):一个命令行美化库,用以在命令行中呈现更美观的输出。
# 配置文件的通用改变
在这个部分,我们将介绍一些旧版本 (**MMClassification 0.x****MMSelfSup 0.x**) 和 **MMPretrain 1.x** 之间通用的变化规范。
## 训练策略设置
| MMCls or MMSelfSup 0.x | MMPretrain 1.x | 备注 |
| ---------------------- | --------------- | -------------------------------------------------------------------------------------------------------- |
| optimizer_config | / | `optimizer_config` 已经被**移除**。 |
| / | optim_wrapper | `optim_wrapper` 提供了参数更新的相关字段。 |
| lr_config | param_scheduler | `param_scheduler` 是一个列表设置学习率或者是其它参数,这将比之前更加灵活。 |
| runner | train_cfg | `train_cfg` 中的循环设置(如 `EpochBasedTrainLoop``IterBasedTrainLoop`)将控制模型训练过程中的工作流。 |
**`optimizer`****`optimizer_config`** 字段的变化:
- 现在我们使用 `optim_wrapper` 字段指定与优化过程有关的所有配置。而 `optimizer` 字段是 `optim_wrapper` 的一个
子字段。
- `paramwise_cfg` 字段不再是 `optimizer` 的子字段,而是 `optim_wrapper` 的子字段。
- `optimizer_config` 字段被移除,其配置项被移入 `optim_wrapper` 字段。
- `grad_clip` 被重命名为 `clip_grad`
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
optimizer = dict(
type='AdamW',
lr=0.0015,
weight_decay=0.3,
paramwise_cfg = dict(
norm_decay_mult=0.0,
bias_decay_mult=0.0,
))
optimizer_config = dict(grad_clip=dict(max_norm=1.0))
```
</td>
<tr>
<td>新配置</td>
<td>
```python
optim_wrapper = dict(
optimizer=dict(type='AdamW', lr=0.0015, weight_decay=0.3),
paramwise_cfg = dict(
norm_decay_mult=0.0,
bias_decay_mult=0.0,
),
clip_grad=dict(max_norm=1.0),
)
```
</td>
</tr>
</table>
**`lr_config`** 字段的变化:
- `lr_config` 字段被移除,我们使用新的 `param_scheduler` 配置取代。
- `warmup` 相关的字段都被移除,因为学习率预热可以通过多个学习率规划器的组合来实现,因此不再单独实现。
新的优化器参数规划器组合机制非常灵活,你可以使用它来设计多种学习率、动量曲线,详见{external+mmengine:doc}`MMEngine 中的教程 <tutorials/param_scheduler>`
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
lr_config = dict(
policy='CosineAnnealing',
min_lr=0,
warmup='linear',
warmup_iters=5,
warmup_ratio=0.01,
warmup_by_epoch=True)
```
</td>
<tr>
<td>新配置</td>
<td>
```python
param_scheduler = [
# 学习率预热
dict(
type='LinearLR',
start_factor=0.01,
by_epoch=True,
end=5,
# 每轮迭代都更新学习率,而不是每个 epoch
convert_to_iter_based=True),
# 主学习率规划器
dict(type='CosineAnnealingLR', by_epoch=True, begin=5),
]
```
</td>
</tr>
</table>
**`runner`** 字段的变化:
`runner` 字段被拆分为 `train_cfg``val_cfg``test_cfg` 三个字段,分别配置训练、验证和测试循环。
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
runner = dict(type='EpochBasedRunner', max_epochs=100)
```
</td>
<tr>
<td>新配置</td>
<td>
```python
# `val_interval` 字段来自原配置中 `evaluation.interval` 字段
train_cfg = dict(by_epoch=True, max_epochs=100, val_interval=1)
val_cfg = dict() # 空字典表示使用默认验证配置
test_cfg = dict() # 空字典表示使用默认测试配置
```
</td>
</tr>
</table>
在 OpenMMLab 2.0 中,我们引入了“循环控制器”来控制训练、验证和测试行为,而原先 `Runner` 功能也相应地发生了变化。详细介绍参见 MMEngine 中的{external+mmengine:doc}`执行器教程 <design/runner>`
## 运行设置
**`checkpoint_config`****`log_config`** 字段的变化:
`checkpoint_config` 被移动至 `default_hooks.checkpoint``log_config` 被移动至 `default_hooks.logger`。同时,
我们将很多原先在训练脚本中隐式定义的钩子移动到了 `default_hooks` 字段。
```python
default_hooks = dict(
# 记录每轮迭代的耗时
timer=dict(type='IterTimerHook'),
# 每 100 轮迭代打印一次日志
logger=dict(type='LoggerHook', interval=100),
# 启用优化器参数规划器
param_scheduler=dict(type='ParamSchedulerHook'),
# 每个 epoch 保存一次模型权重文件,并且自动保存最优权重文件
checkpoint=dict(type='CheckpointHook', interval=1, save_best='auto'),
# 在分布式环境中设置采样器种子
sampler_seed=dict(type='DistSamplerSeedHook'),
# 可视化验证结果,将 `enable` 设为 True 来启用这一功能。
visualization=dict(type='VisualizationHook', enable=False),
)
```
此外,我们将原来的日志功能拆分为日志记录和可视化器。日志记录负责按照指定间隔保存日志数据,以及进行数据平滑等处理,可视化器用于在不同的后端记录日志,如终端、TensorBoard 和 WandB。
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
log_config = dict(
interval=100,
hooks=[
dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook'),
])
```
</td>
<tr>
<td>新配置</td>
<td>
```python
default_hooks = dict(
...
logger=dict(type='LoggerHook', interval=100),
)
visualizer = dict(
type='UniversalVisualizer',
vis_backends=[dict(type='LocalVisBackend'), dict(type='TensorboardVisBackend')],
)
```
</td>
</tr>
</table>
**`load_from`****`resume_from`** 字段的变动:
- `resume_from` 字段被移除。我们现在使用 `resume``load_from` 字段实现以下功能:
-`resume=True``load_from` 不为 None,从 `load_from` 指定的权重文件恢复训练。
-`resume=True``load_from` 为 None,尝试从工作目录中最新的权重文件恢复训练。
-`resume=False``load_from` 不为 None,仅加载指定的权重文件,不恢复训练。
-`resume=False``load_from` 为 None,不进行任何操作。
**`dist_params`** 字段的变动:`dist_params` 字段被移动为 `env_cfg` 字段的一个子字段。以下为 `env_cfg` 字段的所
有配置项:
```python
env_cfg = dict(
# 是否启用 cudnn benchmark
cudnn_benchmark=False,
# 设置多进程相关参数
mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
# 设置分布式相关参数
dist_cfg=dict(backend='nccl'),
)
```
**`workflow`** 字段的变动:`workflow` 相关的功能现已被移除。
新字段 **`visualizer`**:可视化器是 OpenMMLab 2.0 架构中的新设计,我们使用可视化器进行日志、结果的可视化与多后
端的存储。详见 MMEngine 中的{external+mmengine:doc}`可视化教程 <advanced_tutorials/visualization>`
```python
visualizer = dict(
type='UniversalVisualizer',
vis_backends=[
dict(type='LocalVisBackend'),
# 将下行取消注释,即可将日志和可视化结果保存至 TesnorBoard
# dict(type='TensorboardVisBackend')
]
)
```
新字段 **`default_scope`**:指定所有注册器进行模块搜索默认的起点。MMPretrain 中的 `default_scope` 字段为 `mmpretrain`,大部分情况下不需要修改。详见 MMengine 中的{external+mmengine:doc}`注册器教程 <advanced_tutorials/registry>`
## 其他变动
我们将所有注册器的定义从各个包移动到了 `mmpretrain.registry`
# 从 MMClassification 0.x 迁移
## 配置文件
在 MMPretrain 1.x 中,我们重构了配置文件的结构,绝大部分原来的配置文件无法直接使用。
在本节中,我们将介绍配置文件的所有变化。我们假设您已经对[配置文件](./user_guides/config.md)有所了解。
### 模型设置
`model.backbone``model.neck``model.head` 字段没有变化。
**`model.train_cfg`** 字段的变化:
- `BatchMixup` 被重命名为 [`Mixup`](mmpretrain.models.utils.batch_augments.Mixup)
- `BatchCutMix` 被重命名为 [`CutMix`](mmpretrain.models.utils.batch_augments.CutMix)
- `BatchResizeMix` 被重命名为 [`ResizeMix`](mmpretrain.models.utils.batch_augments.ResizeMix)
- 以上增强中的 `prob` 参数均被移除,现在在 `train_cfg` 中使用一个统一的 `probs` 字段指定每个增强的概率。如果没
有指定 `probs` 字段,现在将均匀地随机选择一种增强。
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
model = dict(
...
train_cfg=dict(augments=[
dict(type='BatchMixup', alpha=0.8, num_classes=1000, prob=0.5),
dict(type='BatchCutMix', alpha=1.0, num_classes=1000, prob=0.5)
]
)
```
</td>
<tr>
<td>新配置</td>
<td>
```python
model = dict(
...
train_cfg=dict(augments=[
dict(type='Mixup', alpha=0.8),
dict(type='CutMix', alpha=1.0),
]
)
```
</td>
</tr>
</table>
### 数据设置
**`data`** 字段的变化:
- 原先的 `data` 字段被拆分为 `train_dataloader``val_dataloader``test_dataloader` 字段。这允许我们进行更
加细粒度的配置。比如在训练和测试中指定不同的采样器、批次大小等。
- `samples_per_gpu` 字段被重命名为 `batch_size`
- `workers_per_gpu` 字段被重命名为 `num_workers`
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
data = dict(
samples_per_gpu=32,
workers_per_gpu=2,
train=dict(...),
val=dict(...),
test=dict(...),
)
```
</td>
<tr>
<td>新配置</td>
<td>
```python
train_dataloader = dict(
batch_size=32,
num_workers=2,
dataset=dict(...),
sampler=dict(type='DefaultSampler', shuffle=True) # 必要的
)
val_dataloader = dict(
batch_size=32,
num_workers=2,
dataset=dict(...),
sampler=dict(type='DefaultSampler', shuffle=False) # 必要的
)
test_dataloader = val_dataloader
```
</td>
</tr>
</table>
**`pipeline`** 字段的变化:
- 原先的 **`ToTensor`****`ImageToTensor`****`Collect`** 被合并为 [`PackInputs`](mmpretrain.datasets.transforms.PackInputs)
- 我们建议去除数据集流水线中的 **`Normalize`** 变换,转而使用 `data_preprocessor` 字段进行归一化预处理。
- [**`RandomFlip`**](mmcv.transforms.RandomFlip) 中的 `flip_prob` 参数被重命名为 `prob`
- [**`RandomCrop`**](mmpretrain.datasets.transforms.RandomCrop) 中的 `size` 参数被重命名为 `crop_size`
- [**`RandomResizedCrop`**](mmpretrain.datasets.transforms.RandomResizedCrop) 中的 `size` 参数被重命名为 `scale`
- [**`Resize`**](mmcv.transforms.Resize) 中的 `size` 参数被重命名为 `scale`。并且不再支持形如 `(256, -1)` 的尺寸,请使用 [`ResizeEdge`](mmpretrain.datasets.transforms.ResizeEdge)
- [**`AutoAugment`**](mmpretrain.datasets.transforms.AutoAugment)[**`RandAugment`**](mmpretrain.datasets.transforms.RandAugment) 中的 `policies` 参数现在支持使用字符串来指定某些预设的策略集,`AutoAugment` 支持 "imagenet",`RandAugment` 支持 "timm_increasing"
- **`RandomResizedCrop`****`CenterCrop`** 不再支持 `efficientnet_style` 参数,请使用 [`EfficientNetRandomCrop`](mmpretrain.datasets.transforms.EfficientNetRandomCrop)[`EfficientNetCenterCrop`](mmpretrain.datasets.transforms.EfficientNetCenterCrop)
```{note}
我们将一些数据变换工作移至数据预处理器进行,如归一化,请参阅[文档](mmpretrain.models.utils.data_preprocessor)了解更多详细信息。
```
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='RandomResizedCrop', size=224),
dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='ToTensor', keys=['gt_label']),
dict(type='Collect', keys=['img', 'gt_label'])
]
```
</td>
<tr>
<td>新配置</td>
<td>
```python
data_preprocessor = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='RandomResizedCrop', scale=224),
dict(type='RandomFlip', prob=0.5, direction='horizontal'),
dict(type='PackInputs'),
]
```
</td>
</tr>
</table>
**`evaluation`** 字段的变化:
- 原先的 **`evaluation`** 字段被拆分为 `val_evaluator``test_evaluator`,并且不再支持 `interval``save_best`
参数。`interval` 参数被移动至 `train_cfg.val_interval` 字段,详见[训练策略配置](./user_guides/config.md#训练策略)。而 `save_best` 参数被移动至 `default_hooks.checkpoint.save_best` 字段,详见 [运行设置](./user_guides/config.md#运行设置)
- 'accuracy' 指标被重命名为 [`Accuracy`](mmpretrain.evaluation.Accuracy)
- 'precision','recall','f1-score' 和 'support' 指标被组合为 [`SingleLabelMetric`](mmpretrain.evaluation.SingleLabelMetric),并使用 `items` 参数指定具体计算哪些指标。
- 'mAP' 指标被重命名为 [`AveragePrecision`](mmpretrain.evaluation.AveragePrecision)
- 'CP','CR','CF1','OP','OR' 和 'OF1' 指标被组合为 [`MultiLabelMetric`](mmpretrain.evaluation.MultiLabelMetric),并使用 `items``average` 参数指定具体计算哪些指标。
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
evaluation = dict(
interval=1,
metric='accuracy',
metric_options=dict(topk=(1, 5))
)
```
</td>
<tr>
<td>新配置</td>
<td>
```python
val_evaluator = dict(type='Accuracy', topk=(1, 5))
test_evaluator = val_evaluator
```
</td>
</tr>
</table>
<table class="docutils">
<tr>
<td>原配置</td>
<td>
```python
evaluation = dict(
interval=1,
metric=['mAP', 'CP', 'OP', 'CR', 'OR', 'CF1', 'OF1'],
metric_options=dict(thr=0.5),
)
```
</td>
<tr>
<td>新配置</td>
<td>
```python
val_evaluator = [
dict(type='AveragePrecision'),
dict(type='MultiLabelMetric',
items=['precision', 'recall', 'f1-score'],
average='both',
thr=0.5),
]
test_evaluator = val_evaluator
```
</td>
</tr>
</table>
## 模块变动
### `mmpretrain.apis`
详见[包文档](mmpretrain.apis)
| 函数 | 变动 |
| :------------------: | :-------------------------------------------------------------------------------------------------------------------------------- |
| `init_model` | 无变动 |
| `inference_model` | 无变动,但我们推荐使用功能更强的 [`mmpretrain.ImageClassificationInferencer`](mmpretrain.apis.ImageClassificationInferencer)。 |
| `train_model` | 移除,直接使用 `runner.train` 进行训练。 |
| `multi_gpu_test` | 移除,直接使用 `runner.test` 进行测试。 |
| `single_gpu_test` | 移除,直接使用 `runner.test` 进行测试。 |
| `show_result_pyplot` | 移除,使用 [`mmpretrain.ImageClassificationInferencer`](mmpretrain.apis.ImageClassificationInferencer) 进行模型推理和结果可视化。 |
| `set_random_seed` | 移除,使用 `mmengine.runner.set_random_seed`. |
| `init_random_seed` | 移除,使用 `mmengine.dist.sync_random_seed`. |
### `mmpretrain.core`
`mmpretrain.core` 包被重命名为 [`mmpretrain.engine`](mmpretrain.engine)
| 子包 | 变动 |
| :-------------: | :-------------------------------------------------------------------------------------------------------------------------------- |
| `evaluation` | 移除,使用 [`mmpretrain.evaluation`](mmpretrain.evaluation) |
| `hook` | 移动至 [`mmpretrain.engine.hooks`](mmpretrain.engine.hooks) |
| `optimizers` | 移动至 [`mmpretrain.engine.optimizers`](mmpretrain.engine.optimizers) |
| `utils` | 移除,分布式环境相关的函数统一至 [`mmengine.dist`](api/dist) 包 |
| `visualization` | 移除,其中可视化相关的功能被移动至 [`mmpretrain.visualization.UniversalVisualizer`](mmpretrain.visualization.UniversalVisualizer) |
`hooks` 包中的 `MMClsWandbHook` 尚未实现。
`hooks` 包中的 `CosineAnnealingCooldownLrUpdaterHook` 被移除。我们现在支持使用学习率规划器的组合实现该功能。详见[自定义训练优化策略](./advanced_guides/schedule.md)
### `mmpretrain.datasets`
详见[包文档](mmpretrain.datasets)
| 数据集类 | 变动 |
| :---------------------------------------------------------------------------------------: | :----------------------------------------------------------------------- |
| [`CustomDataset`](mmpretrain.datasets.CustomDataset) | 增加了 `data_root` 参数,作为 `data_prefix``ann_file` 的共同根路径。 |
| [`ImageNet`](mmpretrain.datasets.ImageNet) | 与 `CustomDataset` 相同。 |
| [`ImageNet21k`](mmpretrain.datasets.ImageNet21k) | 与 `CustomDataset` 相同。 |
| [`CIFAR10`](mmpretrain.datasets.CIFAR10) & [`CIFAR100`](mmpretrain.datasets.CIFAR100) | `test_mode` 参数目前是必要参数。 |
| [`MNIST`](mmpretrain.datasets.MNIST) & [`FashionMNIST`](mmpretrain.datasets.FashionMNIST) | `test_mode` 参数目前是必要参数。 |
| [`VOC`](mmpretrain.datasets.VOC) | 现在需要指定 `data_root``image_set_path``test_mode` 参数。 |
| [`CUB`](mmpretrain.datasets.CUB) | 现在需要指定 `data_root``test_mode` 参数。 |
`mmpretrain.datasets.pipelines` 包被重命名为 `mmpretrain.datasets.transforms`
| 数据变换类 | 变动 |
| :-----------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `LoadImageFromFile` | 移除,使用 [`mmcv.transforms.LoadImageFromFile`](mmcv.transforms.LoadImageFromFile) |
| `RandomFlip` | 移除,使用 [`mmcv.transforms.RandomFlip`](mmcv.transforms.RandomFlip),其中 `flip_prob` 参数被重命名为 `prob` |
| `RandomCrop` | `size` 参数被重命名为 `crop_size` |
| `RandomResizedCrop` | `size` 参数被重命名为 `scale``scale` 参数被重命名为 `crop_ratio_range`;不再支持 `efficientnet_style`,请使用 [`EfficientNetRandomCrop`](mmpretrain.datasets.transforms.EfficientNetRandomCrop) |
| `CenterCrop` | 移除,使用 [`mmcv.transforms.CenterCrop`](mmcv.transforms.CenterCrop);不再支持 `efficientnet_style`,请使用 [`EfficientNetCenterCrop`](mmpretrain.datasets.transforms.EfficientNetCenterCrop) |
| `Resize` | 移除,使用 [`mmcv.transforms.Resize`](mmcv.transforms.Resize)`size` 参数被重命名为 `scale`,且不再支持形如 `(256, -1)` 参数,使用 [`ResizeEdge`](mmpretrain.datasets.transforms.ResizeEdge) |
| `AutoAugment` & `RandomAugment` | `policies` 参数现在支持使用字符串指定预设的策略集。 |
| `Compose` | 移除,使用 [`mmcv.transforms.Compose`](mmcv.transforms.Compose) |
### `mmpretrain.models`
详见[包文档](mmpretrain.models)**backbones****necks****losses** 的结构没有变动。
[`ImageClassifier`](mmpretrain.models.classifiers.ImageClassifier) 的变动:
| 分类器的方法 | 变动 |
| :-------------: | :---------------------------------------------------------------------------------------------------------------------- |
| `extract_feat` | 无变动 |
| `forward` | 现在需要三个输入:`inputs``data_samples``mode`。详见[文档](mmpretrain.models.classifiers.ImageClassifier.forward) |
| `forward_train` | 变更为 `loss` 方法。 |
| `simple_test` | 变更为 `predict` 方法。 |
| `train_step` | `optimizer` 参数被修改为 `optim_wrapper`,接受 [`OptimWrapper`](mmengine.optim.OptimWrapper) |
| `val_step` | 原先的 `val_step``train_step` 一致,现在该方法将会调用 `predict` |
| `test_step` | 新方法,与 `val_step` 一致。 |
[heads](mmpretrain.models.heads) 中的变动:
| 分类头的方法 | 变动 |
| :-------------: | :--------------------------------------------------------------------------------------------------------------------------------------- |
| `pre_logits` | 无变动 |
| `forward_train` | 变更为 `loss` 方法。 |
| `simple_test` | 变更为 `predict` 方法。 |
| `loss` | 现在接受 `data_samples` 参数,而不是 `gt_labels``data_samples` 参数应当接受 [ClsDataSample](mmpretrain.structures.DataSample) 的列表。 |
| `forward` | 新方法,它将返回分类头的输出,不会进行任何后处理(包括 softmax 或 sigmoid)。 |
### `mmpretrain.utils`
详见[包文档](mmpretrain.utils)
| 函数 | 变动 |
| :--------------------------: | :------------------------------------------------------------------------------------------------------------ |
| `collect_env` | 无变动 |
| `get_root_logger` | 移除,使用 [`mmengine.logging.MMLogger.get_current_instance`](mmengine.logging.MMLogger.get_current_instance) |
| `load_json_log` | 输出格式发生变化。 |
| `setup_multi_processes` | 移除,使用 [`mmengine.utils.dl_utils.set_multi_processing`](mmengine.utils.dl_utils.set_multi_processing) |
| `wrap_non_distributed_model` | 移除,现在 runner 会自动包装模型。 |
| `wrap_distributed_model` | 移除,现在 runner 会自动包装模型。 |
| `auto_select_device` | 移除,现在 runner 会自动选择设备。 |
# 从 MMSelfSup 0.x 迁移
## 配置文件
本章节将介绍 `_base_` 文件夹中的配置文件的变化,主要包含以下三个部分:
- 数据集:`configs/_base_/datasets`
- 模型:`configs/_base_/models`
- 优化器及调度:`configs/_base_/schedules`
### 数据集
**MMSelfSup 0.x** 中,我们使用字段 `data` 来整合数据相关信息, 例如 `samples_per_gpu``train``val` 等。
**MMPretrain 1.x** 中,我们分别使用字段 `train_dataloader`, `val_dataloader` 整理训练和验证的数据相关信息,并且 `data` 字段已经被 **移除**
<table class="docutils">
<tr>
<td>旧版本</td>
<td>
```python
data = dict(
samples_per_gpu=32, # total 32*8(gpu)=256
workers_per_gpu=4,
train=dict(
type=dataset_type,
data_source=dict(
type=data_source,
data_prefix='data/imagenet/train',
ann_file='data/imagenet/meta/train.txt',
),
num_views=[1, 1],
pipelines=[train_pipeline1, train_pipeline2],
prefetch=prefetch,
),
val=...)
```
</td>
<tr>
<td>新版本</td>
<td>
```python
train_dataloader = dict(
batch_size=32,
num_workers=4,
persistent_workers=True,
sampler=dict(type='DefaultSampler', shuffle=True),
collate_fn=dict(type='default_collate'),
dataset=dict(
type=dataset_type,
data_root=data_root,
ann_file='meta/train.txt',
data_prefix=dict(img_path='train/'),
pipeline=train_pipeline))
val_dataloader = ...
```
</td>
</tr>
</table>
另外,我们 **移除** 了字段 `data_source`,以此来保证我们项目和其它 OpenMMLab 项目数据流的一致性。请查阅 [Config](user_guides/config.md) 获取更详细的信息。
**`pipeline`** 中的变化:
以 MAE 的 `pipeline` 作为例子,新的写法如下:
```python
train_pipeline = [
dict(type='LoadImageFromFile'),
dict(
type='RandomResizedCrop',
size=224,
scale=(0.2, 1.0),
backend='pillow',
interpolation='bicubic'),
dict(type='RandomFlip', prob=0.5),
dict(type='PackSelfSupInputs', meta_keys=['img_path'])
]
```
### 模型
在模型的配置文件中,和 MMSeflSup 0.x 版本相比,主要有两点不同。
1. 有一个新的字段 `data_preprocessor`,主要负责对数据进行预处理,例如归一化,通道转换等。例子如下:
```python
data_preprocessor=dict(
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
bgr_to_rgb=True)
model = dict(
type='MAE',
data_preprocessor=dict(
mean=[127.5, 127.5, 127.5],
std=[127.5, 127.5, 127.5],
bgr_to_rgb=True),
backbone=...,
neck=...,
head=...,
init_cfg=...)
```
2. 在新版本的 `head` 字段中,我们新增加了 `loss`,主要负责损失函数的构建。例子如下:
```python
model = dict(
type='MAE',
backbone=...,
neck=...,
head=dict(
type='MAEPretrainHead',
norm_pix=True,
patch_size=16,
loss=dict(type='MAEReconstructionLoss')),
init_cfg=...)
```
## 模块变动
下列表格记录了代码模块、文件夹的主要改变。
| MMSelfSup 0.x | MMPretrain 1.x | Remark |
| ------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apis | / | 目前 `apis` 文件夹已暂时被**移除**,在未来可能会再添加回来。 |
| core | engine | `core` 文件夹重命名为 `engine`,包含了 `hooks``opimizers`。([API link](mmpretrain.engine)) |
| datasets | datasets | 数据集相关类主要基于不同的数据集实现,例如 ImageNet,Places205。([API link](mmpretrain.datasets)) |
| datasets/data_sources | / | `data_sources` 已经被**移除**,并且现在 `datasets` 的逻辑和 OpenMMLab 其它项目保持一致。 |
| datasets/pipelines | datasets/transforms | `pipelines` 文件夹已经重命名为 `transforms`。([API link](mmpretrain.datasets.transforms)) |
| / | evaluation | `evaluation` 主要负责管理一些评测函数或者是类。([API link](mmpretrain.evaluation)) |
| models/algorithms | selfsup | 算法文件移动至 `selfsup` 文件夹。([API link](mmpretrain.models.selfsup)) |
| models/backbones | selfsup | 自监督学习算法对应的,重新实现的主干网络移动到算法的 `.py` 文件中。([API link](mmpretrain.models.selfsup)) |
| models/target_generators | selfsup | 目标生成器的实现移动到算法的 `.py` 文件中。([API link](mmpretrain.models.selfsup)) |
| / | models/losses | `losses` 文件夹提供了各种不同损失函数的实现。([API link](mmpretrain.models.losses)) |
| / | structures | `structures` 文件夹提供了数据结构的实现。在 MMPretrain 中,我们实现了一种新的数据结构,`DataSample`,在训练/验证过程中来传输和接受数据信息。([API link](mmpretrain.structures)) |
| / | visualization | `visualization` 文件夹包含了 visualizer,主要负责一些可视化的工作,例如数据增强的可视化。([API link](mmpretrain.visualization)) |
../../en/notes/changelog.md
\ No newline at end of file
# 参与贡献 OpenMMLab
欢迎任何类型的贡献,包括但不限于
- 修改拼写错误或代码错误
- 添加文档或将文档翻译成其他语言
- 添加新功能和新组件
## 工作流程
1. fork 并 pull 最新的 OpenMMLab 仓库 (MMPreTrain)
2. 签出到一个新分支(不要使用 master 分支提交 PR)
3. 进行修改并提交至 fork 出的自己的远程仓库
4. 在我们的仓库中创建一个 PR
```{note}
如果你计划添加一些新的功能,并引入大量改动,请尽量首先创建一个 issue 来进行讨论。
```
## 代码风格
### Python
我们采用 [PEP8](https://www.python.org/dev/peps/pep-0008/) 作为统一的代码风格。
我们使用下列工具来进行代码风格检查与格式化:
- [flake8](https://github.com/PyCQA/flake8): Python 官方发布的代码规范检查工具,是多个检查工具的封装
- [isort](https://github.com/timothycrosley/isort): 自动调整模块导入顺序的工具
- [yapf](https://github.com/google/yapf): 一个 Python 文件的格式化工具。
- [codespell](https://github.com/codespell-project/codespell): 检查单词拼写是否有误
- [mdformat](https://github.com/executablebooks/mdformat): 检查 markdown 文件的工具
- [docformatter](https://github.com/myint/docformatter): 一个 docstring 格式化工具。
yapf 和 isort 的格式设置位于 [setup.cfg](https://github.com/open-mmlab/mmpretrain/blob/main/setup.cfg)
我们使用 [pre-commit hook](https://pre-commit.com/) 来保证每次提交时自动进行代
码检查和格式化,启用的功能包括 `flake8`, `yapf`, `isort`, `trailing whitespaces`, `markdown files`, 修复 `end-of-files`, `double-quoted-strings`,
`python-encoding-pragma`, `mixed-line-ending`, 对 `requirments.txt`的排序等。
pre-commit hook 的配置文件位于 [.pre-commit-config](https://github.com/open-mmlab/mmpretrain/blob/main/.pre-commit-config.yaml)
在你克隆仓库后,你需要按照如下步骤安装并初始化 pre-commit hook。
```shell
pip install -U pre-commit
```
在仓库文件夹中执行
```shell
pre-commit install
```
在此之后,每次提交,代码规范检查和格式化工具都将被强制执行。
```{important}
在创建 PR 之前,请确保你的代码完成了代码规范检查,并经过了 yapf 的格式化。
```
### C++ 和 CUDA
C++ 和 CUDA 的代码规范遵从 [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
# 常见问题
我们在这里列出了一些常见问题及其相应的解决方案。如果您发现任何常见问题并有方法
帮助解决,欢迎随时丰富列表。如果这里的内容没有涵盖您的问题,请按照
[提问模板](https://github.com/open-mmlab/mmpretrain/issues/new/choose)
在 GitHub 上提出问题,并补充模板中需要的信息。
## 安装
- MMEngine, MMCV 与 MMPretrain 的兼容问题
这里我们列举了各版本 MMPretrain 对 MMEngine 和 MMCV 版本的依赖,请选择合适的 MMEngine 和 MMCV 版本来避免安装和使用中的问题。
| MMPretrain 版本 | MMEngine 版本 | MMCV 版本 |
| :-------------: | :---------------: | :--------------: |
| 1.2.0 (main) | mmengine >= 0.8.3 | mmcv >= 2.0.0 |
| 1.1.1 | mmengine >= 0.8.3 | mmcv >= 2.0.0 |
| 1.0.0 | mmengine >= 0.8.0 | mmcv >= 2.0.0 |
| 1.0.0rc8 | mmengine >= 0.7.1 | mmcv >= 2.0.0rc4 |
| 1.0.0rc7 | mmengine >= 0.5.0 | mmcv >= 2.0.0rc4 |
```{note}
由于 `dev` 分支处于频繁开发中,MMEngine 和 MMCV 版本依赖可能不准确。如果您在使用
`dev` 分支时遇到问题,请尝试更新 MMEngine 和 MMCV 到最新版。
```
- 使用 Albumentations
如果你希望使用 `albumentations` 相关的功能,我们建议使用 `pip install -r requirements/optional.txt` 或者
`pip install -U albumentations>=0.3.2 --no-binary qudida,albumentations` 命令进行安装。
如果你直接使用 `pip install albumentations>=0.3.2` 来安装,它会同时安装 `opencv-python-headless`
(即使你已经安装了 `opencv-python`)。具体细节可参阅
[官方文档](https://albumentations.ai/docs/getting_started/installation/#note-on-opencv-dependencies)
## 通用问题
### 如果我对源码进行了改动,需要重新安装以使改动生效吗?
如果你遵照[最佳实践](../get_started.md#最佳实践)的指引,从源码安装 mmpretrain,那么任何本地修改都不需要重新安装即可生效。
### 如何在多个 MMPretrain 版本下进行开发?
通常来说,我们推荐通过不同虚拟环境来管理多个开发目录下的 MMPretrain。
但如果你希望在不同目录(如 mmpretrain-0.21, mmpretrain-0.23 等)使用同一个环境进行开发,
我们提供的训练和测试 shell 脚本会自动使用当前目录的 mmpretrain,其他 Python 脚本
则可以在命令前添加 `` PYTHONPATH=`pwd` `` 来使用当前目录的代码。
反过来,如果你希望 shell 脚本使用环境中安装的 MMPretrain,而不是当前目录的,
则可以去掉 shell 脚本中如下一行代码:
```shell
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH
```
### `load_from` 和 `init_cfg` 之间的关系是什么?
- `load_from`: 如果`resume=False`,只导入模型权重,主要用于加载训练过的模型;
如果 `resume=True` ,加载所有的模型权重、优化器状态和其他训练信息,主要用于恢复中断的训练。
- `init_cfg`: 你也可以指定`init=dict(type="Pretrained", checkpoint=xxx)`来加载权重,
表示在模型权重初始化时加载权重,通常在训练的开始阶段执行。
主要用于微调预训练模型,你可以在骨干网络的配置中配置它,还可以使用 `prefix` 字段来只加载对应的权重,例如:
```python
model = dict(
backbone=dict(
type='ResNet',
depth=50,
init_cfg=dict(type='Pretrained', checkpoints=xxx, prefix='backbone'),
)
...
)
```
参见 [微调模型](./finetune_custom_dataset.md) 以了解更多关于模型微调的细节。
### `default_hooks` 和 `custom_hooks` 之间有什么区别?
几乎没有区别。通常,`default_hooks` 字段用于指定几乎所有实验都会使用的钩子,
`custom_hooks` 字段指部分实验特有的钩子。
另一个区别是 `default_hooks` 是一个字典,而 `custom_hooks` 是一个列表,请不要混淆。
### 在训练期间,我没有收到训练日志,这是什么原因?
如果你的训练数据集很小,而批处理量却很大,我们默认的日志间隔可能太大,无法记录你的训练日志。
你可以缩减日志间隔,再试一次,比如:
```python
default_hooks = dict(
...
logger=dict(type='LoggerHook', interval=10),
...
)
```
### 如何基于其它数据集训练,例如我自己的数据集或者是 COCO 数据集?
我们提供了 [具体示例](./pretrain_custom_dataset.md) 来展示如何在其它数据集上进行训练。
# 如何在自定义数据集上微调模型
在很多场景下,我们需要快速地将模型应用到新的数据集上,但从头训练模型通常很难快速收敛,这种不确定性会浪费额外的时间。
通常,已有的、在大数据集上训练好的模型会比随机初始化提供更为有效的先验信息,粗略来讲,在此基础上的学习我们称之为模型微调。
已经证明,在 ImageNet 数据集上预训练的模型对于其他数据集和其他下游任务有很好的效果。
因此,该教程提供了如何将 [Model Zoo](../modelzoo_statistics.md) 中提供的预训练模型用于其他数据集,已获得更好的效果。
在本教程中,我们提供了一个实践示例和一些关于如何在自己的数据集上微调模型的技巧。
## 第一步:准备你的数据集
按照 [准备数据集](../user_guides/dataset_prepare.md) 准备你的数据集。
假设我们的数据集根文件夹路径为 `data/custom_dataset/`
假设我们想进行有监督图像分类训练,并使用子文件夹格式的 `CustomDataset` 来组织数据集:
```text
data/custom_dataset/
├── train
│   ├── class_x
│   │ ├── x_1.png
│ │ ├── x_2.png
│ │ ├── x_3.png
│ │ └── ...
│ ├── class_y
│   └── ...
└── test
   ├── class_x
   │ ├── test_x_1.png
│ ├── test_x_2.png
│ ├── test_x_3.png
│ └── ...
├── class_y
   └── ...
```
## 第二步:选择一个配置文件作为模板
在这里,我们使用 `configs/resnet/resnet50_8xb32_in1k.py` 作为示例。
首先在同一文件夹下复制一份配置文件,并将其重命名为 `resnet50_8xb32-ft_custom.py`
```{tip}
按照惯例,配置名称的最后一个字段是数据集,例如,`in1k` 表示 ImageNet-1k,`coco` 表示 coco 数据集
```
这个配置的内容是:
```python
_base_ = [
'../_base_/models/resnet50.py', # 模型设置
'../_base_/datasets/imagenet_bs32.py', # 数据设置
'../_base_/schedules/imagenet_bs256.py', # 训练策略设置
'../_base_/default_runtime.py', # 运行设置
]
```
## 第三步:修改模型设置
在进行模型微调时,我们通常希望在主干网络(backbone)加载预训练模型,再用我们的数据集训练一个新的分类头(head)。
为了在主干网络加载预训练模型,我们需要修改主干网络的初始化设置,使用
`Pretrained` 类型的初始化函数。另外,在初始化设置中,我们使用 `prefix='backbone'`
来告诉初始化函数需要加载的子模块的前缀,`backbone`即指加载模型中的主干网络。
方便起见,我们这里使用一个在线的权重文件链接,它
会在训练前自动下载对应的文件,你也可以提前下载这个模型,然后使用本地路径。
接下来,新的配置文件需要按照新数据集的类别数目来修改分类头的配置。只需要修改分
类头中的 `num_classes` 设置即可。
另外,当新的小数据集和原本预训练的大数据集中的数据分布较为类似的话,我们在进行微调时会希望
冻结主干网络前面几层的参数,只训练后面层以及分类头的参数,这么做有助于在后续训练中,
保持网络从预训练权重中获得的提取低阶特征的能力。在 MMPretrain 中,
这一功能可以通过简单的一个 `frozen_stages` 参数来实现。比如我们需要冻结前两层网
络的参数,只需要在上面的配置中添加一行:
```{note}
注意,目前并非所有的主干网络都支持 `frozen_stages` 参数。请检查[文档](https://mmpretrain.readthedocs.io/en/latest/api.html#module-mmpretrain.models.backbones)
确认使用的主干网络是否支持这一参数。
```
```python
_base_ = [
'../_base_/models/resnet50.py', # 模型设置
'../_base_/datasets/imagenet_bs32.py', # 数据设置
'../_base_/schedules/imagenet_bs256.py', # 训练策略设置
'../_base_/default_runtime.py', # 运行设置
]
# >>>>>>>>>>>>>>> 在这里重载模型相关配置 >>>>>>>>>>>>>>>>>>>
model = dict(
backbone=dict(
frozen_stages=2,
init_cfg=dict(
type='Pretrained',
checkpoint='https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth',
prefix='backbone',
)),
head=dict(num_classes=10),
)
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
```
```{tip}
这里我们只需要设定我们想要修改的部分配置,其他配置将会自动从我们的基配置文件中获取。
```
## 第四步:修改数据集设置
为了在新数据集上进行微调,我们需要覆盖一些数据集设置,例如数据集类型、数据流水线等。
```python
_base_ = [
'../_base_/models/resnet50.py', # 模型设置
'../_base_/datasets/imagenet_bs32.py', # 数据设置
'../_base_/schedules/imagenet_bs256.py', # 训练策略设置
'../_base_/default_runtime.py', # 运行设置
]
# 模型设置
model = dict(
backbone=dict(
frozen_stages=2,
init_cfg=dict(
type='Pretrained',
checkpoint='https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth',
prefix='backbone',
)),
head=dict(num_classes=10),
)
# >>>>>>>>>>>>>>> 在这里重载数据配置 >>>>>>>>>>>>>>>>>>>
data_root = 'data/custom_dataset'
train_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='', # 我们假定使用子文件夹格式,因此需要将标注文件置空
data_prefix='train',
))
val_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='', # 我们假定使用子文件夹格式,因此需要将标注文件置空
data_prefix='test',
))
test_dataloader = val_dataloader
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
```
## 第五步:修改训练策略设置(可选)
微调所使用的训练超参数一般与默认的超参数不同,它通常需要更小的学习率和更快的学习率衰减。
```python
_base_ = [
'../_base_/models/resnet50.py', # 模型设置
'../_base_/datasets/imagenet_bs32.py', # 数据设置
'../_base_/schedules/imagenet_bs256.py', # 训练策略设置
'../_base_/default_runtime.py', # 运行设置
]
# 模型设置
model = dict(
backbone=dict(
frozen_stages=2,
init_cfg=dict(
type='Pretrained',
checkpoint='https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth',
prefix='backbone',
)),
head=dict(num_classes=10),
)
# 数据设置
data_root = 'data/custom_dataset'
train_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='',
data_prefix='train',
))
val_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='',
data_prefix='test',
))
test_dataloader = val_dataloader
# >>>>>>>>>>>>>>> 在这里重载训练策略设置 >>>>>>>>>>>>>>>>>>>
# 优化器超参数
optim_wrapper = dict(
optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))
# 学习率策略
param_scheduler = dict(
type='MultiStepLR', by_epoch=True, milestones=[15], gamma=0.1)
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
```
```{tip}
更多关于配置文件的信息,请参阅[学习配置文件](../user_guides/config.md)
```
## 开始训练
现在,我们完成了用于微调的配置文件,完整的文件如下:
```python
_base_ = [
'../_base_/models/resnet50.py', # 模型设置
'../_base_/datasets/imagenet_bs32.py', # 数据设置
'../_base_/schedules/imagenet_bs256.py', # 训练策略设置
'../_base_/default_runtime.py', # 运行设置
]
# 模型设置
model = dict(
backbone=dict(
frozen_stages=2,
init_cfg=dict(
type='Pretrained',
checkpoint='https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth',
prefix='backbone',
)),
head=dict(num_classes=10),
)
# 数据设置
data_root = 'data/custom_dataset'
train_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='',
data_prefix='train',
))
val_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='',
data_prefix='test',
))
test_dataloader = val_dataloader
# 训练策略设置
optim_wrapper = dict(
optimizer=dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001))
param_scheduler = dict(
type='MultiStepLR', by_epoch=True, milestones=[15], gamma=0.1)
```
接下来,我们使用一台 8 张 GPU 的电脑来训练我们的模型,指令如下:
```shell
bash tools/dist_train.sh configs/resnet/resnet50_8xb32-ft_custom.py 8
```
当然,我们也可以使用单张 GPU 来进行训练,使用如下命令:
```shell
python tools/train.py configs/resnet/resnet50_8xb32-ft_custom.py
```
但是如果我们使用单张 GPU 进行训练的话,需要在数据集设置部分作如下修改:
```python
data_root = 'data/custom_dataset'
train_dataloader = dict(
batch_size=256,
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='',
data_prefix='train',
))
val_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root=data_root,
ann_file='',
data_prefix='test',
))
test_dataloader = val_dataloader
```
这是因为我们的训练策略是针对批次大小(batch size)为 256 设置的。在父配置文件中,
设置了单张 `batch_size=32`,如果使用 8 张 GPU,总的批次大小就是 256。而如果使
用单张 GPU,就必须手动修改 `batch_size=256` 来匹配训练策略。
然而,更大的批次大小需要更大的 GPU 显存,这里有几个简单的技巧来节省显存:
1. 启用自动混合精度训练
```shell
python tools/train.py configs/resnet/resnet50_8xb32-ft_custom.py --amp
```
2. 使用较小的批次大小,例如仍然使用 `batch_size=32`,而不是 256,并启用学习率自动缩放
```shell
python tools/train.py configs/resnet/resnet50_8xb32-ft_custom.py --auto-scale-lr
```
学习率自动缩放功能会根据实际的 batch size 和配置文件中的 `auto_scale_lr.base_batch_size`
字段对学习率进行线性调整(你可以在基配置文件 `configs/_base_/schedules/imagenet_bs256.py`
中找到这一字段)
```{note}
以上技巧都有可能对训练效果造成轻微影响。
```
### 在命令行指定预训练模型
如果您不想修改配置文件,您可以使用 `--cfg-options` 将您的预训练模型文件添加到 `init_cfg`.
例如,以下命令也会加载预训练模型:
```shell
bash tools/dist_train.sh configs/tutorial/resnet50_finetune_cifar.py 8 \
--cfg-options model.backbone.init_cfg.type='Pretrained' \
model.backbone.init_cfg.checkpoint='https://download.openmmlab.com/mmselfsup/1.x/mocov3/mocov3_resnet50_8xb512-amp-coslr-100e_in1k/mocov3_resnet50_8xb512-amp-coslr-100e_in1k_20220927-f1144efa.pth' \
model.backbone.init_cfg.prefix='backbone' \
```
# 如何在自定义数据集上进行模型预训练
在本教程中,我们提供了一个实践示例和一些有关如何在您自己的数据集上进行训练的技巧。
在 MMPretrain 中,我们支持用户直接调用 MMPretrain 的 `CustomDataset` (类似于 `torchvision``ImageFolder`), 该数据集能自动的读取给的路径下的图片。你只需要准备你的数据集路径,并修改配置文件,即可轻松使用 MMPretrain 进行预训练。
## 第一步:准备你的数据集
按照 [准备数据集](../user_guides/dataset_prepare.md) 准备你的数据集。
假设我们的数据集根文件夹路径为 `data/custom_dataset/`
假设我们想使用 MAE 算法进行图像自监督训练,并使用子文件夹格式的 `CustomDataset` 来组织数据集:
```text
data/custom_dataset/
├── sample1.png
├── sample2.png
├── sample3.png
├── sample4.png
└── ...
```
## 第二步:选择一个配置文件作为模板
在本教程中,我们使用 `configs/mae/mae_vit-base-p16_8xb512-amp-coslr-300e_in1k.py` 作为一个示例进行介绍。
首先在同一文件夹下复制一份配置文件,并将其重命名为 `mae_vit-base-p16_8xb512-amp-coslr-300e_custom.py`
```{tip}
按照惯例,配置名称的最后一个字段是数据集,例如,`in1k` 表示 ImageNet-1k,`coco` 表示 coco 数据集
```
这个配置文件的内容如下:
```python
_base_ = [
'../_base_/models/mae_vit-base-p16.py',
'../_base_/datasets/imagenet_bs512_mae.py',
'../_base_/default_runtime.py',
]
# optimizer wrapper
optim_wrapper = dict(
type='AmpOptimWrapper',
loss_scale='dynamic',
optimizer=dict(
type='AdamW',
lr=1.5e-4 * 4096 / 256,
betas=(0.9, 0.95),
weight_decay=0.05),
paramwise_cfg=dict(
custom_keys={
'ln': dict(decay_mult=0.0),
'bias': dict(decay_mult=0.0),
'pos_embed': dict(decay_mult=0.),
'mask_token': dict(decay_mult=0.),
'cls_token': dict(decay_mult=0.)
}))
# learning rate scheduler
param_scheduler = [
dict(
type='LinearLR',
start_factor=0.0001,
by_epoch=True,
begin=0,
end=40,
convert_to_iter_based=True),
dict(
type='CosineAnnealingLR',
T_max=260,
by_epoch=True,
begin=40,
end=300,
convert_to_iter_based=True)
]
# runtime settings
train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=300)
default_hooks = dict(
# only keeps the latest 3 checkpoints
checkpoint=dict(type='CheckpointHook', interval=1, max_keep_ckpts=3))
randomness = dict(seed=0, diff_rank_seed=True)
# auto resume
resume = True
# NOTE: `auto_scale_lr` is for automatically scaling LR
# based on the actual training batch size.
auto_scale_lr = dict(base_batch_size=4096)
```
## 第三步:修改数据集设置
- 重载数据集设置中的 `type``'CustomDataset'`
- 重载数据集设置中的 `data_root``data/custom_dataset`
- 重载数据集设置中的 `ann_file` 为空字符串,这是因为我们使用子文件格式的 `CustomDataset`,需要将配置文件置空
- 重载数据集设置中的 `data_prefix` 为空字符串,这是因为我们希望使用数据集根目录下的所有数据进行训练,并不需要将其拆分为不同子集。
修改后的文件应如下:
```python
_base_ = [
'../_base_/models/mae_vit-base-p16.py',
'../_base_/datasets/imagenet_bs512_mae.py',
'../_base_/default_runtime.py',
]
# >>>>>>>>>>>>>>> 在此重载数据设置 >>>>>>>>>>>>>>>>>>>
train_dataloader = dict(
dataset=dict(
type='CustomDataset',
data_root='data/custom_dataset/',
ann_file='', # 我们假定使用子文件夹格式,因此需要将标注文件置空
data_prefix='', # 使用 `data_root` 路径下所有数据
with_label=False,
)
)
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# optimizer wrapper
optim_wrapper = dict(
type='AmpOptimWrapper',
loss_scale='dynamic',
optimizer=dict(
type='AdamW',
lr=1.5e-4 * 4096 / 256,
betas=(0.9, 0.95),
weight_decay=0.05),
paramwise_cfg=dict(
custom_keys={
'ln': dict(decay_mult=0.0),
'bias': dict(decay_mult=0.0),
'pos_embed': dict(decay_mult=0.),
'mask_token': dict(decay_mult=0.),
'cls_token': dict(decay_mult=0.)
}))
# learning rate scheduler
param_scheduler = [
dict(
type='LinearLR',
start_factor=0.0001,
by_epoch=True,
begin=0,
end=40,
convert_to_iter_based=True),
dict(
type='CosineAnnealingLR',
T_max=260,
by_epoch=True,
begin=40,
end=300,
convert_to_iter_based=True)
]
# runtime settings
train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=300)
default_hooks = dict(
# only keeps the latest 3 checkpoints
checkpoint=dict(type='CheckpointHook', interval=1, max_keep_ckpts=3))
randomness = dict(seed=0, diff_rank_seed=True)
# auto resume
resume = True
# NOTE: `auto_scale_lr` is for automatically scaling LR
# based on the actual training batch size.
auto_scale_lr = dict(base_batch_size=4096)
```
使用上述配置文件,你就能够轻松的在自定义数据集上使用 `MAE` 算法来进行预训练了。
## 另一个例子:在 COCO 数据集上训练 MAE
```{note}
你可能需要参考[文档](https://github.com/open-mmlab/mmdetection/blob/3.x/docs/en/get_started.md)安装 MMDetection 来使用 `mmdet.CocoDataset`。
```
与在自定义数据集上进行预训练类似,我们在本教程中也提供了一个使用 COCO 数据集进行预训练的示例。修改后的文件如下:
```python
# >>>>>>>>>>>>>>>>>>>>> Start of Changed >>>>>>>>>>>>>>>>>>>>>>>>>
_base_ = [
'../_base_/models/mae_vit-base-p16.py',
'../_base_/datasets/imagenet_mae.py',
'../_base_/default_runtime.py',
]
# >>>>>>>>>>>>>>> 在这里重载数据配置 >>>>>>>>>>>>>>>>>>>
train_dataloader = dict(
dataset=dict(
type='mmdet.CocoDataset',
data_root='data/coco/',
ann_file='annotations/instances_train2017.json', # 仅用于加载图片,不会使用标签
data_prefix=dict(img='train2017/'),
)
)
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# optimizer wrapper
optim_wrapper = dict(
type='AmpOptimWrapper',
loss_scale='dynamic',
optimizer=dict(
type='AdamW',
lr=1.5e-4 * 4096 / 256,
betas=(0.9, 0.95),
weight_decay=0.05),
paramwise_cfg=dict(
custom_keys={
'ln': dict(decay_mult=0.0),
'bias': dict(decay_mult=0.0),
'pos_embed': dict(decay_mult=0.),
'mask_token': dict(decay_mult=0.),
'cls_token': dict(decay_mult=0.)
}))
# learning rate scheduler
param_scheduler = [
dict(
type='LinearLR',
start_factor=0.0001,
by_epoch=True,
begin=0,
end=40,
convert_to_iter_based=True),
dict(
type='CosineAnnealingLR',
T_max=260,
by_epoch=True,
begin=40,
end=300,
convert_to_iter_based=True)
]
# runtime settings
train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=300)
default_hooks = dict(
# only keeps the latest 3 checkpoints
checkpoint=dict(type='CheckpointHook', interval=1, max_keep_ckpts=3))
randomness = dict(seed=0, diff_rank_seed=True)
# auto resume
resume = True
# NOTE: `auto_scale_lr` is for automatically scaling LR
# based on the actual training batch size.
auto_scale_lr = dict(base_batch_size=4096)
```
# 基于 MMPretrain 的项目列表(待更新)
#!/usr/bin/env python
import re
import warnings
from collections import defaultdict
from pathlib import Path
from modelindex.load_model_index import load
from modelindex.models.Result import Result
from tabulate import tabulate
MMPT_ROOT = Path(__file__).absolute().parents[2]
PAPERS_ROOT = Path('papers') # Path to save generated paper pages.
GITHUB_PREFIX = 'https://github.com/open-mmlab/mmpretrain/blob/main/'
MODELZOO_TEMPLATE = """\
# 模型库统计
在本页面中,我们列举了我们支持的[所有算法](#所有已支持的算法)。你可以点击链接跳转至对应的模型详情页面。
另外,我们还列出了我们提供的所有模型权重文件。你可以使用排序和搜索功能找到需要的模型权重,并使用链接跳转至模型详情页面。
## 所有已支持的算法
* 论文数量:{num_papers}
{type_msg}
* 模型权重文件数量:{num_ckpts}
{paper_msg}
""" # noqa: E501
METRIC_ALIAS = {
'Top 1 Accuracy': 'Top-1 (%)',
'Top 5 Accuracy': 'Top-5 (%)',
}
model_index = load(str(MMPT_ROOT / 'model-index.yml'))
def build_collections(model_index):
col_by_name = {}
for col in model_index.collections:
setattr(col, 'models', [])
col_by_name[col.name] = col
for model in model_index.models:
col = col_by_name[model.in_collection]
col.models.append(model)
setattr(model, 'collection', col)
if model.results is None:
setattr(model, 'tasks', [])
else:
setattr(model, 'tasks', [result.task for result in model.results])
build_collections(model_index)
def count_papers(collections):
total_num_ckpts = 0
type_count = defaultdict(int)
paper_msgs = []
for collection in collections:
with open(MMPT_ROOT / collection.readme) as f:
readme = f.read()
ckpts = set(x.lower().strip()
for x in re.findall(r'\[model\]\((https?.*)\)', readme))
total_num_ckpts += len(ckpts)
title = collection.paper['Title']
papertype = collection.data.get('type', 'Algorithm')
type_count[papertype] += 1
readme = PAPERS_ROOT / Path(
collection.filepath).parent.with_suffix('.md').name
paper_msgs.append(
f'\t- [{papertype}] [{title}]({readme}) ({len(ckpts)} ckpts)')
type_msg = '\n'.join(
[f'\t- {type_}: {count}' for type_, count in type_count.items()])
paper_msg = '\n'.join(paper_msgs)
modelzoo = MODELZOO_TEMPLATE.format(
num_papers=len(collections),
num_ckpts=total_num_ckpts,
type_msg=type_msg,
paper_msg=paper_msg,
)
with open('modelzoo_statistics.md', 'w') as f:
f.write(modelzoo)
count_papers(model_index.collections)
def generate_paper_page(collection):
PAPERS_ROOT.mkdir(exist_ok=True)
# Write a copy of README
with open(MMPT_ROOT / collection.readme) as f:
readme = f.read()
folder = Path(collection.filepath).parent
copy = PAPERS_ROOT / folder.with_suffix('.md').name
def replace_link(matchobj):
# Replace relative link to GitHub link.
name = matchobj.group(1)
link = matchobj.group(2)
if not link.startswith('http'):
assert (folder / link).exists(), \
f'Link not found:\n{collection.readme}: {link}'
rel_link = (folder / link).absolute().relative_to(MMPT_ROOT)
link = GITHUB_PREFIX + str(rel_link)
return f'[{name}]({link})'
content = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', replace_link, readme)
content = f'---\ngithub_page: /{collection.readme}\n---\n' + content
def make_tabs(matchobj):
"""modify the format from emphasis black symbol to tabs."""
content = matchobj.group()
content = content.replace('<!-- [TABS-BEGIN] -->', '')
content = content.replace('<!-- [TABS-END] -->', '')
# split the content by "**{Tab-Name}**""
splits = re.split(r'^\*\*(.*)\*\*$', content, flags=re.M)[1:]
tabs_list = []
for title, tab_content in zip(splits[::2], splits[1::2]):
title = ':::{tab} ' + title + '\n'
tab_content = tab_content.strip() + '\n:::\n'
tabs_list.append(title + tab_content)
return '::::{tabs}\n' + ''.join(tabs_list) + '::::'
if '<!-- [TABS-BEGIN] -->' in content and '<!-- [TABS-END] -->' in content:
# Make TABS block a selctive tabs
try:
pattern = r'<!-- \[TABS-BEGIN\] -->([\d\D]*?)<!-- \[TABS-END\] -->'
content = re.sub(pattern, make_tabs, content)
except Exception as e:
warnings.warn(f'Can not parse the TABS, get an error : {e}')
with open(copy, 'w') as copy_file:
copy_file.write(content)
for collection in model_index.collections:
generate_paper_page(collection)
def scatter_results(models):
model_result_pairs = []
for model in models:
if model.results is None:
result = Result(task=None, dataset=None, metrics={})
model_result_pairs.append((model, result))
else:
for result in model.results:
model_result_pairs.append((model, result))
return model_result_pairs
def generate_summary_table(task, model_result_pairs, title=None):
metrics = set()
for model, result in model_result_pairs:
if result.task == task:
metrics = metrics.union(result.metrics.keys())
metrics = sorted(list(metrics))
rows = []
for model, result in model_result_pairs:
if result.task != task:
continue
name = model.name
params = f'{model.metadata.parameters / 1e6:.2f}' # Params
if model.metadata.flops is not None:
flops = f'{model.metadata.flops / 1e9:.2f}' # Flops
else:
flops = None
readme = Path(model.collection.filepath).parent.with_suffix('.md').name
page = f'[链接]({PAPERS_ROOT / readme})'
model_metrics = []
for metric in metrics:
model_metrics.append(str(result.metrics.get(metric, '')))
rows.append([name, params, flops, *model_metrics, page])
with open('modelzoo_statistics.md', 'a') as f:
if title is not None:
f.write(f'\n{title}')
f.write("""\n```{table}\n:class: model-summary\n""")
header = [
'模型',
'参数量 (M)',
'Flops (G)',
*[METRIC_ALIAS.get(metric, metric) for metric in metrics],
'Readme',
]
table_cfg = dict(
tablefmt='pipe',
floatfmt='.2f',
numalign='right',
stralign='center')
f.write(tabulate(rows, header, **table_cfg))
f.write('\n```\n')
def generate_dataset_wise_table(task, model_result_pairs, title=None):
dataset_rows = defaultdict(list)
for model, result in model_result_pairs:
if result.task == task:
dataset_rows[result.dataset].append((model, result))
if title is not None:
with open('modelzoo_statistics.md', 'a') as f:
f.write(f'\n{title}')
for dataset, pairs in dataset_rows.items():
generate_summary_table(task, pairs, title=f'### {dataset}')
model_result_pairs = scatter_results(model_index.models)
# Generate Pretrain Summary
generate_summary_table(
task=None,
model_result_pairs=model_result_pairs,
title='## 预训练模型',
)
# Generate Image Classification Summary
generate_dataset_wise_table(
task='Image Classification',
model_result_pairs=model_result_pairs,
title='## 图像分类',
)
# Generate Multi-Label Classification Summary
generate_dataset_wise_table(
task='Multi-Label Classification',
model_result_pairs=model_result_pairs,
title='## 图像多标签分类',
)
# Generate Image Retrieval Summary
generate_dataset_wise_table(
task='Image Retrieval',
model_result_pairs=model_result_pairs,
title='## 图像检索',
)
# 类别激活图(CAM)可视化
## 类别激活图可视化工具介绍
MMPretrain 提供 `tools/visualization/vis_cam.py` 工具来可视化类别激活图。请使用 `pip install "grad-cam>=1.3.6"` 安装依赖的 [pytorch-grad-cam](https://github.com/jacobgil/pytorch-grad-cam)
目前支持的方法有:
| Method | What it does |
| :----------: | :-----------------------------------------------------------------------------------------------: |
| GradCAM | 使用平均梯度对 2D 激活进行加权 |
| GradCAM++ | 类似 GradCAM,但使用了二阶梯度 |
| XGradCAM | 类似 GradCAM,但通过归一化的激活对梯度进行了加权 |
| EigenCAM | 使用 2D 激活的第一主成分(无法区分类别,但效果似乎不错) |
| EigenGradCAM | 类似 EigenCAM,但支持类别区分,使用了激活 * 梯度的第一主成分,看起来和 GradCAM 差不多,但是更干净 |
| LayerCAM | 使用正梯度对激活进行空间加权,对于浅层有更好的效果 |
也可以使用新版本 `pytorch-grad-cam` 支持的更多 CAM 方法,但我们尚未验证可用性。
**命令行**
```bash
python tools/visualization/vis_cam.py \
${IMG} \
${CONFIG_FILE} \
${CHECKPOINT} \
[--target-layers ${TARGET-LAYERS}] \
[--preview-model] \
[--method ${METHOD}] \
[--target-category ${TARGET-CATEGORY}] \
[--save-path ${SAVE_PATH}] \
[--vit-like] \
[--num-extra-tokens ${NUM-EXTRA-TOKENS}]
[--aug_smooth] \
[--eigen_smooth] \
[--device ${DEVICE}] \
[--cfg-options ${CFG-OPTIONS}]
```
**所有参数的说明**
- `img`:目标图片路径。
- `config`:模型配置文件的路径。
- `checkpoint`:权重路径。
- `--target-layers`:所查看的网络层名称,可输入一个或者多个网络层,如果不设置,将使用最后一个`block`中的`norm`层。
- `--preview-model`:是否查看模型所有网络层。
- `--method`:类别激活图图可视化的方法,目前支持 `GradCAM`, `GradCAM++`, `XGradCAM`, `EigenCAM`, `EigenGradCAM`, `LayerCAM`,不区分大小写。如果不设置,默认为 `GradCAM`
- `--target-category`:查看的目标类别,如果不设置,使用模型检测出来的类别做为目标类别。
- `--save-path`:保存的可视化图片的路径,默认不保存。
- `--eigen-smooth`:是否使用主成分降低噪音,默认不开启。
- `--vit-like`: 是否为 `ViT` 类似的 Transformer-based 网络
- `--num-extra-tokens`: `ViT` 类网络的额外的 tokens 通道数,默认使用主干网络的 `num_extra_tokens`
- `--aug-smooth`:是否使用测试时增强
- `--device`:使用的计算设备,如果不设置,默认为'cpu'。
- `--cfg-options`:对配置文件的修改,参考[学习配置文件](../user_guides/config.md)
```{note}
在指定 `--target-layers` 时,如果不知道模型有哪些网络层,可使用命令行添加 `--preview-model` 查看所有网络层名称;
```
## 如何可视化 CNN 网络的类别激活图(如 ResNet-50)
`--target-layers``Resnet-50` 中的一些示例如下:
- `'backbone.layer4'`,表示第四个 `ResLayer` 层的输出。
- `'backbone.layer4.2'` 表示第四个 `ResLayer` 层中第三个 `BottleNeck` 块的输出。
- `'backbone.layer4.2.conv1'` 表示上述 `BottleNeck` 块中 `conv1` 层的输出。
1. 使用不同方法可视化 `ResNet50`,默认 `target-category` 为模型检测的结果,使用默认推导的 `target-layers`
```shell
python tools/visualization/vis_cam.py \
demo/bird.JPEG \
configs/resnet/resnet50_8xb32_in1k.py \
https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth \
--method GradCAM
# GradCAM++, XGradCAM, EigenCAM, EigenGradCAM, LayerCAM
```
| Image | GradCAM | GradCAM++ | EigenGradCAM | LayerCAM |
| ------------------------------------ | --------------------------------------- | ----------------------------------------- | -------------------------------------------- | ---------------------------------------- |
| <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429496-628d3fb3-1f6e-41ff-aa5c-1b08c60c32a9.JPEG' height="auto" width="160" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065002-f1c86516-38b2-47ba-90c1-e00b49556c70.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065119-82581fa1-3414-4d6c-a849-804e1503c74b.jpg' height="auto" width="150"></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065096-75a6a2c1-6c57-4789-ad64-ebe5e38765f4.jpg' height="auto" width="150"></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065129-814d20fb-98be-4106-8c5e-420adcc85295.jpg' height="auto" width="150"></div> |
2. 同一张图不同类别的激活图效果图,在 `ImageNet` 数据集中,类别 238 为 'Greater Swiss Mountain dog',类别 281 为 'tabby, tabby cat'。
```shell
python tools/visualization/vis_cam.py \
demo/cat-dog.png configs/resnet/resnet50_8xb32_in1k.py \
https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth \
--target-layers 'backbone.layer4.2' \
--method GradCAM \
--target-category 238
# --target-category 281
```
| Category | Image | GradCAM | XGradCAM | LayerCAM |
| -------- | ---------------------------------------------- | ------------------------------------------------ | ------------------------------------------------- | ------------------------------------------------- |
| Dog | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429526-f27f4cce-89b9-4117-bfe6-55c2ca7eaba6.png' height="auto" width="165" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144433562-968a57bc-17d9-413e-810e-f91e334d648a.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144433853-319f3a8f-95f2-446d-b84f-3028daca5378.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144433937-daef5a69-fd70-428f-98a3-5e7747f4bb88.jpg' height="auto" width="150" ></div> |
| Cat | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429526-f27f4cce-89b9-4117-bfe6-55c2ca7eaba6.png' height="auto" width="165" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144434518-867ae32a-1cb5-4dbd-b1b9-5e375e94ea48.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144434603-0a2fd9ec-c02e-4e6c-a17b-64c234808c56.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144434623-b4432cc2-c663-4b97-aed3-583d9d3743e6.jpg' height="auto" width="150" ></div> |
3. 使用 `--eigen-smooth` 以及 `--aug-smooth` 获取更好的可视化效果。
```shell
python tools/visualization/vis_cam.py \
demo/dog.jpg \
configs/mobilenet_v3/mobilenet-v3-large_8xb128_in1k.py \
https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth \
--target-layers 'backbone.layer16' \
--method LayerCAM \
--eigen-smooth --aug-smooth
```
| Image | LayerCAM | eigen-smooth | aug-smooth | eigen&aug |
| ------------------------------------ | --------------------------------------- | ------------------------------------------- | ----------------------------------------- | ----------------------------------------- |
| <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557492-98ac5ce0-61f9-4da9-8ea7-396d0b6a20fa.jpg' height="auto" width="160"></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557541-a4cf7d86-7267-46f9-937c-6f657ea661b4.jpg' height="auto" width="145" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557547-2731b53e-e997-4dd2-a092-64739cc91959.jpg' height="auto" width="145" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557545-8189524a-eb92-4cce-bf6a-760cab4a8065.jpg' height="auto" width="145" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557548-c1e3f3ec-3c96-43d4-874a-3b33cd3351c5.jpg' height="auto" width="145" ></div> |
## 如何可视化 Transformer 类型网络的类别激活图
`--target-layers` 在 Transformer-based 网络中的一些示例如下:
- Swin-Transformer 中:`'backbone.norm3'`
- ViT 中:`'backbone.layers.11.ln1'`
对于 Transformer-based 的网络,比如 ViT、T2T-ViT 和 Swin-Transformer,特征是被展平的。为了绘制 CAM 图,我们需要指定 `--vit-like` 选项,从而让被展平的特征恢复方形的特征图。
除了特征被展平之外,一些类 ViT 的网络还会添加额外的 tokens。比如 ViT 和 T2T-ViT 中添加了分类 token,DeiT 中还添加了蒸馏 token。在这些网络中,分类计算在最后一个注意力模块之后就已经完成了,分类得分也只和这些额外的 tokens 有关,与特征图无关,也就是说,分类得分对这些特征图的导数为 0。因此,我们不能使用最后一个注意力模块的输出作为 CAM 绘制的目标层。
另外,为了去除这些额外的 toekns 以获得特征图,我们需要知道这些额外 tokens 的数量。MMPretrain 中几乎所有 Transformer-based 的网络都拥有 `num_extra_tokens` 属性。而如果你希望将此工具应用于新的,或者第三方的网络,而且该网络没有指定 `num_extra_tokens` 属性,那么可以使用 `--num-extra-tokens` 参数手动指定其数量。
1.`Swin Transformer` 使用默认 `target-layers` 进行 CAM 可视化:
```shell
python tools/visualization/vis_cam.py \
demo/bird.JPEG \
configs/swin_transformer/swin-tiny_16xb64_in1k.py \
https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth \
--vit-like
```
2.`Vision Transformer(ViT)` 进行 CAM 可视化:
```shell
python tools/visualization/vis_cam.py \
demo/bird.JPEG \
configs/vision_transformer/vit-base-p16_64xb64_in1k-384px.py \
https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth \
--vit-like \
--target-layers 'backbone.layers.11.ln1'
```
3.`T2T-ViT` 进行 CAM 可视化:
```shell
python tools/visualization/vis_cam.py \
demo/bird.JPEG \
configs/t2t_vit/t2t-vit-t-14_8xb64_in1k.py \
https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth \
--vit-like \
--target-layers 'backbone.encoder.12.ln1'
```
| Image | ResNet50 | ViT | Swin | T2T-ViT |
| --------------------------------------- | ------------------------------------------ | -------------------------------------- | --------------------------------------- | ------------------------------------------ |
| <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429496-628d3fb3-1f6e-41ff-aa5c-1b08c60c32a9.JPEG' height="auto" width="165" ></div> | <div align=center><img src=https://user-images.githubusercontent.com/18586273/144431491-a2e19fe3-5c12-4404-b2af-a9552f5a95d9.jpg height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144436218-245a11de-6234-4852-9c08-ff5069f6a739.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144436168-01b0e565-442c-4e1e-910c-17c62cff7cd3.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144436198-51dbfbda-c48d-48cc-ae06-1a923d19b6f6.jpg' height="auto" width="150" ></div> |
# 模型复杂度分析
## 计算 FLOPs 和参数数量(实验性的)
我们根据 [MMEngine](https://github.com/open-mmlab/mmengine/blob/main/mmengine/analysis/complexity_analysis.py) 提供了一个脚本用于计算给定模型的 FLOPs 和参数量。
```shell
python tools/analysis_tools/get_flops.py ${CONFIG_FILE} [--shape ${INPUT_SHAPE}]
```
所有参数说明:
- `config` : 配置文件的路径。
- `--shape`: 输入尺寸,支持单值或者双值, 如: `--shape 256``--shape 224 256`。默认为`224 224`
示例:
```shell
python tools/analysis_tools/get_flops.py configs/resnet/resnet50_8xb32_in1k.py
```
你将获得如下结果:
```text
==============================
Input shape: (3, 224, 224)
Flops: 4.109G
Params: 25.557M
Activation: 11.114M
==============================
```
同时,你会得到每层的详细复杂度信息,如下所示:
```text
+--------------------------+----------------------+-----------+--------------+
| module | #parameters or shape | #flops | #activations |
+--------------------------+----------------------+-----------+--------------+
| model | 25.557M | 4.109G | 11.114M |
| backbone | 23.508M | 4.109G | 11.114M |
| backbone.conv1 | 9.408K | 0.118G | 0.803M |
| backbone.conv1.weight | (64, 3, 7, 7) | | |
| backbone.bn1 | 0.128K | 1.606M | 0 |
| backbone.bn1.weight | (64,) | | |
| backbone.bn1.bias | (64,) | | |
| backbone.layer1 | 0.216M | 0.677G | 4.415M |
| backbone.layer1.0 | 75.008K | 0.235G | 2.007M |
| backbone.layer1.1 | 70.4K | 0.221G | 1.204M |
| backbone.layer1.2 | 70.4K | 0.221G | 1.204M |
| backbone.layer2 | 1.22M | 1.034G | 3.111M |
| backbone.layer2.0 | 0.379M | 0.375G | 1.305M |
| backbone.layer2.1 | 0.28M | 0.22G | 0.602M |
| backbone.layer2.2 | 0.28M | 0.22G | 0.602M |
| backbone.layer2.3 | 0.28M | 0.22G | 0.602M |
| backbone.layer3 | 7.098M | 1.469G | 2.158M |
| backbone.layer3.0 | 1.512M | 0.374G | 0.652M |
| backbone.layer3.1 | 1.117M | 0.219G | 0.301M |
| backbone.layer3.2 | 1.117M | 0.219G | 0.301M |
| backbone.layer3.3 | 1.117M | 0.219G | 0.301M |
| backbone.layer3.4 | 1.117M | 0.219G | 0.301M |
| backbone.layer3.5 | 1.117M | 0.219G | 0.301M |
| backbone.layer4 | 14.965M | 0.81G | 0.627M |
| backbone.layer4.0 | 6.04M | 0.373G | 0.326M |
| backbone.layer4.1 | 4.463M | 0.219G | 0.151M |
| backbone.layer4.2 | 4.463M | 0.219G | 0.151M |
| head.fc | 2.049M | | |
| head.fc.weight | (1000, 2048) | | |
| head.fc.bias | (1000,) | | |
| neck.gap | | 0.1M | 0 |
+--------------------------+----------------------+-----------+--------------+
```
```{warning}
警告
此工具仍处于试验阶段,我们不保证该数字正确无误。您最好将结果用于简单比较,但在技术报告或论文中采用该结果之前,请仔细检查。
- FLOPs 与输入的尺寸有关,而参数量与输入尺寸无关。默认输入尺寸为 (1, 3, 224, 224)
- 一些运算不会被计入 FLOPs 的统计中,例如某些自定义运算。详细信息请参考 [`mmengine.analysis.complexity_analysis._DEFAULT_SUPPORTED_FLOP_OPS`](https://github.com/open-mmlab/mmengine/blob/main/mmengine/analysis/complexity_analysis.py)
```
# 混淆矩阵
MMPretrain 提供 `tools/analysis_tools/confusion_matrix.py` 工具来分析预测结果的混淆矩阵。关于混淆矩阵的介绍,可参考[链接](https://zh.wikipedia.org/zh-cn/%E6%B7%B7%E6%B7%86%E7%9F%A9%E9%98%B5)
## 命令行使用
**命令行**
```shell
python tools/analysis_tools/confusion_matrix.py \
${CONFIG_FILE} \
${CHECKPOINT} \
[--show] \
[--show-path] \
[--include-values] \
[--cmap ${CMAP}] \
[--cfg-options ${CFG-OPTIONS}]
```
**所有参数的说明**
- `config`:模型配置文件的路径。
- `checkpoint`:权重路径。
- `--show`:是否展示混淆矩阵的 matplotlib 可视化结果,默认不展示。
- `--show-path`:如果 `show` 为 True,可视化结果的保存路径。
- `--include-values`:是否在可视化结果上添加数值。
- `--cmap`:可视化结果使用的颜色映射图,即 `cmap`,默认为 `viridis`
- `--cfg-options`:对配置文件的修改,参考[学习配置文件](../user_guides/config.md)
**使用示例**
```shell
python tools/analysis_tools/confusion_matrix.py \
configs/resnet/resnet50_8xb16_cifar10.py \
https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_b16x8_cifar10_20210528-f54bfad9.pth \
--show
```
**输出图片**
<div align=center><img src="https://user-images.githubusercontent.com/26739999/210298124-49ae00f7-c8fd-488a-a4da-58c285e9c1f1.png" style=" width: auto; height: 40%; "></div>
## 基础用法
```python
>>> import torch
>>> from mmpretrain.evaluation import ConfusionMatrix
>>> y_pred = [0, 1, 1, 3]
>>> y_true = [0, 2, 1, 3]
>>> ConfusionMatrix.calculate(y_pred, y_true, num_classes=4)
tensor([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1]])
>>> # plot the confusion matrix
>>> import matplotlib.pyplot as plt
>>> y_score = torch.rand((1000, 10))
>>> y_true = torch.randint(10, (1000, ))
>>> matrix = ConfusionMatrix.calculate(y_score, y_true)
>>> ConfusionMatrix().plot(matrix)
>>> plt.show()
```
## 结合评估器使用
```python
>>> import torch
>>> from mmpretrain.evaluation import ConfusionMatrix
>>> from mmpretrain.structures import DataSample
>>> from mmengine.evaluator import Evaluator
>>> data_samples = [
... DataSample().set_gt_label(i%5).set_pred_score(torch.rand(5))
... for i in range(1000)
... ]
>>> evaluator = Evaluator(metrics=ConfusionMatrix())
>>> evaluator.process(data_samples)
>>> evaluator.evaluate(1000)
{'confusion_matrix/result': tensor([[37, 37, 48, 43, 35],
[35, 51, 32, 46, 36],
[45, 28, 39, 42, 46],
[42, 40, 40, 35, 43],
[40, 39, 41, 37, 43]])}
```
# 数据集可视化
## 数据集可视化工具简介
```bash
python tools/visualization/browse_dataset.py \
${CONFIG_FILE} \
[-o, --output-dir ${OUTPUT_DIR}] \
[-p, --phase ${DATASET_PHASE}] \
[-n, --show-number ${NUMBER_IMAGES_DISPLAY}] \
[-i, --show-interval ${SHOW_INTERRVAL}] \
[-m, --mode ${DISPLAY_MODE}] \
[-r, --rescale-factor ${RESCALE_FACTOR}] \
[-c, --channel-order ${CHANNEL_ORDER}] \
[--cfg-options ${CFG_OPTIONS}]
```
**所有参数的说明**
- `config` : 模型配置文件的路径。
- `-o, --output-dir`: 保存图片文件夹,如果没有指定,默认为 `''`,表示不保存。
- **`-p, --phase`**: 可视化数据集的阶段,只能为 `['train', 'val', 'test']` 之一,默认为 `'train'`
- **`-n, --show-number`**: 可视化样本数量。如果没有指定,默认展示数据集的所有图片。
- `-i, --show-interval`: 浏览时,每张图片的停留间隔,单位为秒。
- **`-m, --mode`**: 可视化的模式,只能为 `['original', 'transformed', 'concat', 'pipeline']` 之一。 默认为`'transformed'`.
- `-r, --rescale-factor`: 在 `mode='original'` 下,可视化图片的放缩倍数,在图片过大或过小时设置。
- `-c, --channel-order`: 图片的通道顺序,为 `['BGR', 'RGB']` 之一,默认为 `'BGR'`
- `--cfg-options` : 对配置文件的修改,参考[学习配置文件](../user_guides/config.md)
```{note}
1. `-m, --mode` 用于设置可视化的模式,默认设置为 'transformed'。
- 如果 `--mode` 设置为 'original',则获取原始图片;
- 如果 `--mode` 设置为 'transformed',则获取预处理后的图片;
- 如果 `--mode` 设置为 'concat',获取原始图片和预处理后图片拼接的图片;
- 如果 `--mode` 设置为 'pipeline',则获得数据流水线所有中间过程图片。
2. `-r, --rescale-factor` 在数据集中图片的分辨率过大或者过小时设置。比如在可视化 CIFAR 数据集时,由于图片的分辨率非常小,可将 `-r, --rescale-factor` 设置为 10。
```
## 如何可视化原始图像
使用 **'original'** 模式 :
```shell
python ./tools/visualization/browse_dataset.py ./configs/resnet/resnet101_8xb16_cifar10.py --phase val --output-dir tmp --mode original --show-number 100 --rescale-factor 10 --channel-order RGB
```
- `--phase val`: 可视化验证集,可简化为 `-p val`;
- `--output-dir tmp`: 可视化结果保存在 "tmp" 文件夹,可简化为 `-o tmp`;
- `--mode original`: 可视化原图,可简化为 `-m original`;
- `--show-number 100`: 可视化 100 张图,可简化为 `-n 100`;
- `--rescale-factor`: 图像放大 10 倍,可简化为 `-r 10`;
- `--channel-order RGB`: 可视化图像的通道顺序为 "RGB", 可简化为 `-c RGB`
<div align=center><img src="https://user-images.githubusercontent.com/18586273/190993839-216a7a1e-590e-47b9-92ae-08f87a7d58df.jpg" style=" width: auto; height: 40%; "></div>
## 如何可视化处理后图像
使用 **'transformed'** 模式:
```shell
python ./tools/visualization/browse_dataset.py ./configs/resnet/resnet50_8xb32_in1k.py -n 100
```
<div align=center><img src="https://user-images.githubusercontent.com/18586273/190994696-737b09d9-d0fb-4593-94a2-4487121e0286.JPEG" style=" width: auto; height: 40%; "></div>
## 如何同时可视化原始图像与处理后图像
使用 **'concat'** 模式:
```shell
python ./tools/visualization/browse_dataset.py configs/swin_transformer/swin-small_16xb64_in1k.py -n 10 -m concat
```
<div align=center><img src="https://user-images.githubusercontent.com/18586273/190995078-3872feb2-d4e2-4727-a21b-7062d52f7d3e.JPEG" style=" width: auto; height: 40%; "></div>
使用 **'pipeline'** 模式:
```shell
python ./tools/visualization/browse_dataset.py configs/swin_transformer/swin-small_16xb64_in1k.py -m pipeline
```
<div align=center><img src="https://user-images.githubusercontent.com/18586273/190995525-fac0220f-6630-4013-b94a-bc6de4fdff7a.JPEG" style=" width: auto; height: 40%; "></div>
```shell
python ./tools/visualization/browse_dataset.py configs/beit/beit_beit-base-p16_8xb256-amp-coslr-300e_in1k.py -m pipeline
```
<div align=center><img src="https://user-images.githubusercontent.com/26739999/226542300-74216187-e3d0-4a6e-8731-342abe719721.png" style=" width: auto; height: 40%; "></div>
# 日志分析工具
## 日志分析
### 日志分析工具介绍
`tools/analysis_tools/analyze_logs.py` 脚本绘制指定键值的变化曲线。
<div align=center><img src="../_static/image/tools/analysis/analyze_log.jpg" style=" width: 75%; height: 30%; "></div>
```shell
python tools/analysis_tools/analyze_logs.py plot_curve \
${JSON_LOGS} \
[--keys ${KEYS}] \
[--title ${TITLE}] \
[--legend ${LEGEND}] \
[--backend ${BACKEND}] \
[--style ${STYLE}] \
[--out ${OUT_FILE}] \
[--window-size ${WINDOW_SIZE}]
```
**所有参数的说明:**
- `json_logs` : 模型配置文件的路径(可同时传入多个,使用空格分开)。
- `--keys` : 分析日志的关键字段,数量为 `len(${JSON_LOGS}) * len(${KEYS})` 默认为 ‘loss’。
- `--title` : 分析日志的图片名称,默认使用配置文件名, 默认为空。
- `--legend` : 图例的名称,其数目必须与相等`len(${JSON_LOGS}) * len(${KEYS})`。 默认使用 `"${JSON_LOG}-${KEYS}"`.
- `--backend` : matplotlib 的绘图后端,默认由 matplotlib 自动选择。
- `--style` : 绘图配色风格,默认为 `whitegrid`
- `--out` : 保存分析图片的路径,如不指定则不保存。
- `--window-size`: 可视化窗口大小,如果没有指定,默认为 `'12*7'`。如果需要指定,需按照格式 `'W*H'`
```{note}
The `--style` option depends on `seaborn` package, please install it before setting it.
```
### 如何或绘制损失/精度曲线
我们将给出一些示例,来展示如何使用 `tools/analysis_tools/analyze_logs.py`脚本绘制精度曲线的损失曲线
#### 绘制某日志文件对应的损失曲线图
```shell
python tools/analysis_tools/analyze_logs.py plot_curve your_log_json --keys loss --legend loss
```
#### 绘制某日志文件对应的 top-1 和 top-5 准确率曲线图,并将曲线图导出为 results.jpg 文件。
```shell
python tools/analysis_tools/analyze_logs.py plot_curve your_log_json --keys accuracy/top1 accuracy/top5 --legend top1 top5 --out results.jpg
```
#### 在同一图像内绘制两份日志文件对应的 top-1 准确率曲线图。
```shell
python tools/analysis_tools/analyze_logs.py plot_curve log1.json log2.json --keys accuracy/top1 --legend exp1 exp2
```
### 如何统计训练时间
`tools/analysis_tools/analyze_logs.py` 也可以根据日志文件统计训练耗时。
```shell
python tools/analysis_tools/analyze_logs.py cal_train_time \
${JSON_LOGS}
[--include-outliers]
```
**所有参数的说明:**
- `json_logs`:模型配置文件的路径(可同时传入多个,使用空格分开)。
- `--include-outliers`:如果指定,将不会排除每个轮次中第一个时间记录(有时第一轮迭代会耗时较长)。
**示例:**
```shell
python tools/analysis_tools/analyze_logs.py cal_train_time work_dirs/your_exp/20230206_181002/vis_data/scalars.json
```
预计输出结果如下所示:
```text
-----Analyze train time of work_dirs/your_exp/20230206_181002/vis_data/scalars.json-----
slowest epoch 68, average time is 0.3818
fastest epoch 1, average time is 0.3694
time std over epochs is 0.0020
average iter time: 0.3777 s/iter
```
## 结果分析
利用 `tools/test.py``--out`,我们可以将所有的样本的推理结果保存到输出文件中。利用这一文件,我们可以进行进一步的分析。
### 如何进行离线度量评估
我们提供了 `tools/analysis_tools/eval_metric.py` 脚本,使用户能够根据预测文件评估模型。
```shell
python tools/analysis_tools/eval_metric.py \
${RESULT} \
[--metric ${METRIC_OPTIONS} ...]
```
**所有参数说明**
- `result``tools/test.py` 输出的结果文件。
- `--metric`:用于评估结果的指标,请至少指定一个指标,并且你可以通过指定多个 `--metric` 来同时计算多个指标。
请参考[评估文档](mmpretrain.evaluation)选择可用的评估指标和对应的选项。
```{note}
在 `tools/test.py` 中,我们支持使用 `--out-item` 选项来选择保存何种结果至输出文件。
请确保没有额外指定 `--out-item`,或指定了 `--out-item=pred`。
```
**示例**:
```shell
# 获取结果文件
python tools/test.py configs/resnet/resnet18_8xb16_cifar10.py \
https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_b16x8_cifar10_20210528-bd6371c8.pth \
--out results.pkl
# 计算 top-1 和 top-5 准确率
python tools/analysis_tools/eval_metric.py results.pkl --metric type=Accuracy topk=1,5
# 计算总体准确率,各个类别上的精确度、召回率、F1-score
python tools/analysis_tools/eval_metric.py results.pkl --metric type=Accuracy \
--metric type=SingleLabelMetric items=precision,recall,f1-score average=None
```
### 如何绘制测试结果的混淆矩阵
我们提供 `tools/analysis_tools/confusion_matrix.py`,帮助用户能够从测试输出文件中绘制混淆矩阵。
```shell
python tools/analysis_tools/confusion_matrix.py \
${CONFIG} \
${RESULT} \
[--out ${OUT}] \
[--show] \
[--show-path ${SHOW_PATH}] \
[--include-values] \
[--cmap] \
[--cfg-options ${CFG_OPTIONS} ...] \
```
**所有参数说明**
- `config`:配置文件的路径。
- `result``tools/test.py`的输出结果文件,或是模型权重文件。
- `--out`:将混淆矩阵保存到指定路径下的 pickle 文件中。
- `--show`:是否可视化混淆矩阵图。
- `--show-path`:将可视化混淆矩阵图保存到指定路径下。
- `--include-values`:是否在可视化混淆矩阵图中显示具体值。
- `--cmap`:用以可视化混淆矩阵的颜色配置。
- `--cfg-options`:额外的配置选项,会被合入配置文件,参考[学习配置文件](../user_guides/config.md)
```{note}
在 `tools/test.py` 中,我们支持使用 `--out-item` 选项来选择保存何种结果至输出文件。
请确保没有额外指定 `--out-item`,或指定了 `--out-item=pred`。
```
**Examples**:
```shell
# 获取结果文件
python tools/test.py configs/resnet/resnet18_8xb16_cifar10.py \
https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_b16x8_cifar10_20210528-bd6371c8.pth \
--out results.pkl
# 将混淆矩阵计算结果保存至 cm.pkl 中
python tools/analysis_tools/confusion_matrix.py configs/resnet/resnet18_8xb16_cifar10.py results.pkl --out cm.pkl
# 可视化混淆矩阵图,并在图形窗口显示
python tools/analysis_tools/confusion_matrix.py configs/resnet/resnet18_8xb16_cifar10.py results.pkl --show
```
### 如何将预测结果可视化
我们可以使用脚本 `tools/analysis_tools/analyze_results.py` 来保存预测成功或失败时得分最高的图像。
```shell
python tools/analysis_tools/analyze_results.py \
${CONFIG} \
${RESULT} \
[--out-dir ${OUT_DIR}] \
[--topk ${TOPK}] \
[--rescale-factor ${RESCALE_FACTOR}] \
[--cfg-options ${CFG_OPTIONS}]
```
**所有参数说明:**:
- `config`:配置文件的路径。
- `result``tools/test.py`的输出结果文件。
- `--out_dir`:保存结果分析的文件夹路径。
- `--topk`:分别保存多少张预测成功/失败的图像。如果不指定,默认为 `20`
- `--rescale-factor`:图像的缩放系数,如果样本图像过大或过小时可以使用(过小的图像可能导致结果标签非常模糊)。
- `--cfg-options`:额外的配置选项,会被合入配置文件,参考[学习配置文件](../user_guides/config.md)
```{note}
在 `tools/test.py` 中,我们支持使用 `--out-item` 选项来选择保存何种结果至输出文件。
请确保没有额外指定 `--out-item`,或指定了 `--out-item=pred`。
```
**示例**:
```shell
# 获取预测结果文件
python tools/test.py configs/resnet/resnet18_8xb16_cifar10.py \
https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_b16x8_cifar10_20210528-bd6371c8.pth \
--out results.pkl
# 保存预测成功/失败的图像中,得分最高的前 10 张,并在可视化时将输出图像放大 10 倍。
python tools/analysis_tools/analyze_results.py \
configs/resnet/resnet18_8xb16_cifar10.py \
results.pkl \
--out-dir output \
--topk 10 \
--rescale-factor 10
```
# TorchServe 部署
为了使用 [`TorchServe`](https://pytorch.org/serve/) 部署一个 `MMPretrain` 模型,需要进行以下几步:
## 1. 转换 MMPretrain 模型至 TorchServe
```shell
python tools/torchserve/mmpretrain2torchserve.py ${CONFIG_FILE} ${CHECKPOINT_FILE} \
--output-folder ${MODEL_STORE} \
--model-name ${MODEL_NAME}
```
```{note}
${MODEL_STORE} 需要是一个文件夹的绝对路径。
```
示例:
```shell
python tools/torchserve/mmpretrain2torchserve.py \
configs/resnet/resnet18_8xb32_in1k.py \
checkpoints/resnet18_8xb32_in1k_20210831-fbbb1da6.pth \
--output-folder ./checkpoints \
--model-name resnet18_in1k
```
## 2. 构建 `mmpretrain-serve` docker 镜像
```shell
docker build -t mmpretrain-serve:latest docker/serve/
```
## 3. 运行 `mmpretrain-serve` 镜像
请参考官方文档 [基于 docker 运行 TorchServe](https://github.com/pytorch/serve/blob/master/docker/README.md#running-torchserve-in-a-production-docker-environment).
为了使镜像能够使用 GPU 资源,需要安装 [nvidia-docker](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html)。之后可以传递 `--gpus` 参数以在 GPU 上运。
示例:
```shell
docker run --rm \
--name mar \
--cpus 8 \
--gpus device=0 \
-p8080:8080 -p8081:8081 -p8082:8082 \
--mount type=bind,source=`realpath ./checkpoints`,target=/home/model-server/model-store \
mmpretrain-serve:latest
```
```{note}
`realpath ./checkpoints` 是 "./checkpoints" 的绝对路径,你可以将其替换为你保存 TorchServe 模型的目录的绝对路径。
```
参考 [该文档](https://github.com/pytorch/serve/blob/master/docs/rest_api.md) 了解关于推理 (8080),管理 (8081) 和指标 (8082) 等 API 的信息。
## 4. 测试部署
```shell
curl http://127.0.0.1:8080/predictions/${MODEL_NAME} -T demo/demo.JPEG
```
您应该获得类似于以下内容的响应:
```json
{
"pred_label": 58,
"pred_score": 0.38102269172668457,
"pred_class": "water snake"
}
```
另外,你也可以使用 `test_torchserver.py` 来比较 TorchServe 和 PyTorch 的结果,并进行可视化。
```shell
python tools/torchserve/test_torchserver.py ${IMAGE_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} ${MODEL_NAME}
[--inference-addr ${INFERENCE_ADDR}] [--device ${DEVICE}]
```
示例:
```shell
python tools/torchserve/test_torchserver.py \
demo/demo.JPEG \
configs/resnet/resnet18_8xb32_in1k.py \
checkpoints/resnet18_8xb32_in1k_20210831-fbbb1da6.pth \
resnet18_in1k
```
# 打印完整配置文件
`print_config.py`脚本脚本会解析所有输入变量,并打印完整配置信息。
## 说明:
`tools/misc/print_config.py` 脚本会逐字打印整个配置文件,并展示所有导入的文件。
```shell
python tools/misc/print_config.py ${CONFIG} [--cfg-options ${CFG_OPTIONS}]
```
所有参数的说明:
- `config`:模型配置文件的路径。
- `--cfg-options`:额外的配置选项,会被合入配置文件,参考[学习配置文件](../user_guides/config.md)
## 示例:
打印`configs/t2t_vit/t2t-vit-t-14_8xb64_in1k.py`文件的完整配置
```shell
# 打印完成的配置文件
python tools/misc/print_config.py configs/t2t_vit/t2t-vit-t-14_8xb64_in1k.py
# 将完整的配置文件保存为一个独立的配置文件
python tools/misc/print_config.py configs/t2t_vit/t2t-vit-t-14_8xb64_in1k.py > final_config.py
```
# 优化器参数策略可视化
该工具旨在帮助用户检查优化器的超参数调度器(无需训练),支持学习率(learning rate)和动量(momentum)。
## 工具简介
```bash
python tools/visualization/vis_scheduler.py \
${CONFIG_FILE} \
[-p, --parameter ${PARAMETER_NAME}] \
[-d, --dataset-size ${DATASET_SIZE}] \
[-n, --ngpus ${NUM_GPUs}] \
[-s, --save-path ${SAVE_PATH}] \
[--title ${TITLE}] \
[--style ${STYLE}] \
[--window-size ${WINDOW_SIZE}] \
[--cfg-options]
```
**所有参数的说明**
- `config` : 模型配置文件的路径。
- **`-p, parameter`**: 可视化参数名,只能为 `["lr", "momentum"]` 之一, 默认为 `"lr"`.
- **`-d, --dataset-size`**: 数据集的大小。如果指定,`build_dataset` 将被跳过并使用这个大小作为数据集大小,默认使用 `build_dataset` 所得数据集的大小。
- **`-n, --ngpus`**: 使用 GPU 的数量,默认为 1。
- **`-s, --save-path`**: 保存的可视化图片的路径,默认不保存。
- `--title`: 可视化图片的标题,默认为配置文件名。
- `--style`: 可视化图片的风格,默认为 `whitegrid`
- `--window-size`: 可视化窗口大小,如果没有指定,默认为 `12*7`。如果需要指定,按照格式 `'W*H'`
- `--cfg-options`: 对配置文件的修改,参考[学习配置文件](../user_guides/config.md)
```{note}
部分数据集在解析标注阶段比较耗时,可直接将 `-d, dataset-size` 指定数据集的大小,以节约时间。
```
## 如何在开始训练前可视化学习率曲线
你可以使用如下命令来绘制配置文件 `configs/swin_transformer/swin-base_16xb64_in1k.py` 将会使用的变化率曲线:
```bash
python tools/visualization/vis_scheduler.py configs/swin_transformer/swin-base_16xb64_in1k.py --dataset-size 1281167 --ngpus 16
```
<div align=center><img src="https://user-images.githubusercontent.com/26739999/226544329-cf3a3d45-6ab3-48aa-8972-2c2a58c35e62.png" style=" width: auto; height: 40%; "></div>
## 形状偏差(Shape Bias)工具用法
形状偏差(shape bias)衡量模型与纹理相比,如何依赖形状来感知图像中的语义。关于更多细节,我们向感兴趣的读者推荐这篇[论文](https://arxiv.org/abs/2106.07411) 。MMPretrain提供现成的工具箱来获得分类模型的形状偏差。您可以按照以下步骤操作:
### 准备数据集
首先你应该下载[cue-conflict](https://github.com/bethgelab/model-vs-human/releases/download/v0.1/cue-conflict.tar.gz)`data`文件夹,然后解压缩这个数据集。之后,你的`data`文件夹应具有一下结构:
```text
data
├──cue-conflict
| |──airplane
| |──bear
| ...
| |── truck
```
### 修改分类配置
我们在使用MAE预训练的ViT-base模型上运行形状偏移工具。它的配置文件为`configs/mae/benchmarks/vit-base-p16_8xb128-coslr-100e_in1k.py`,它的检查点可从[此链接](https://download.openmmlab.com/mmselfsup/1.x/mae/mae_vit-base-p16_8xb512-fp16-coslr-1600e_in1k/vit-base-p16_ft-8xb128-coslr-100e_in1k/vit-base-p16_ft-8xb128-coslr-100e_in1k_20220825-cf70aa21.pth) 下载。将原始配置中的test_pipeline, test_dataloader和test_evaluation替换为以下配置:
```python
test_pipeline = [
dict(type='LoadImageFromFile'),
dict(
type='ResizeEdge',
scale=256,
edge='short',
backend='pillow'),
dict(type='CenterCrop', crop_size=224),
dict(type='PackInputs')
]
test_dataloader = dict(
pin_memory=True,
collate_fn=dict(type='default_collate'),
batch_size=32,
num_workers=4,
dataset=dict(
type='CustomDataset',
data_root='data/cue-conflict',
pipeline=test_pipeline,
_delete_=True),
sampler=dict(type='DefaultSampler', shuffle=False),
persistent_workers=True,
drop_last=False)
test_evaluator = dict(
type='mmpretrain.ShapeBiasMetric',
_delete_=True,
csv_dir='work_dirs/shape_bias',
model_name='mae')
```
请注意,你应该对上面的`csv_dir``model_name`进行自定义修改。我把修改后的示例配置文件重命名为`configs/mae/benchmarks/`文件夹中的`vit-base-p16_8xb128-coslr-100e_in1k_shape-bias.py`文件。
### 用上面修改后的配置文件在你的模型上做推断
然后,你应该使用修改后的配置文件在`cue-conflict`数据集上推断你的模型。
```shell
# For PyTorch
bash tools/dist_test.sh $CONFIG $CHECKPOINT
```
**所有参数的说明**
- `$CONFIG`: 修改后的配置文件的路径。
- `$CHECKPOINT`: 检查点文件的路径或链接。
```shell
# Example
bash tools/dist_test.sh configs/mae/benchmarks/vit-base-p16_8xb128-coslr-100e_in1k_shape-bias.py https://download.openmmlab.com/mmselfsup/1.x/mae/mae_vit-base-p16_8xb512-fp16-coslr-1600e_in1k/vit-base-p16_ft-8xb128-coslr-100e_in1k/vit-base-p16_ft-8xb128-coslr-100e_in1k_20220825-cf70aa21.pth 1
```
之后,你应该在`csv_dir`文件夹中获得一个名为`cue-conflict_model-name_session-1.csv`的csv文件。除了这个文件以外,你还应该下载这些[csv文件](https://github.com/bethgelab/model-vs-human/tree/master/raw-data/cue-conflict)`csv_dir`
### 绘制形状偏差图
然后我们可以开始绘制形状偏差图:
```shell
python tools/analysis_tools/shape_bias.py --csv-dir $CSV_DIR --result-dir $RESULT_DIR --colors $RGB --markers o --plotting-names $YOUR_MODEL_NAME --model-names $YOUR_MODEL_NAME
```
**所有参数的说明**:
- `--csv-dir $CSV_DIR`, 与保存这些csv文件的目录相同。
- `--result-dir $RESULT_DIR`, 输出名为`cue-conflict_shape-bias_matrixplot.pdf`的结果的目录。
- `--colors $RGB`, 应该是RGB值,格式为R G B,例如100 100 100,如果你想绘制几个模型的形状偏差,可以是多个RGB值。
- `--plotting-names $YOUR_MODEL_NAME`, 形状偏移图中图例的名称,您可以将其设置为模型名称。如果要绘制多个模型,plotting_names可以是多个值。
- `model-names $YOUR_MODEL_NAME`, 应与配置中指定的名称相同,如果要绘制多个模型的形状偏差,则可以是多个名称。
请注意,`--colors`的每三个值对应于`--model-names`的一个值。完成以上所有步骤后,你将获得下图。
<div align="center">
<img src="https://github.com/open-mmlab/mmpretrain/assets/42371271/dc608d06-43eb-4860-bb70-486ed2a3f927" width="500" />
</div>
# t-分布随机邻域嵌入(t-SNE)可视化
## t-分布随机邻域嵌入可视化工具介绍
MMPretrain 提供 `tools/visualization/vis_tsne.py` 工具来用t-SNE可视化图像的特征嵌入。请使用 `pip install scikit-learn` 安装 `sklearn` 来计算t-SNE。
**命令**
```bash
python tools/visualization/vis_tsne.py \
CONFIG \
[--checkpoint CHECKPOINT] \
[--work-dir WORK_DIR] \
[--test-cfg TEST_CFG] \
[--vis-stage {backbone,neck,pre_logits}]
[--class-idx ${CLASS_IDX} [CLASS_IDX ...]]
[--max-num-class MAX_NUM_CLASS]
[--max-num-samples MAX_NUM_SAMPLES]
[--cfg-options CFG_OPTIONS [CFG_OPTIONS ...]]
[--device DEVICE]
[--legend]
[--show]
[--n-components N_COMPONENTS]
[--perplexity PERPLEXITY]
[--early-exaggeration EARLY_EXAGGERATION]
[--learning-rate LEARNING_RATE]
[--n-iter N_ITER]
[--n-iter-without-progress N_ITER_WITHOUT_PROGRESS]
[--init INIT]
```
**所有参数的说明**
- `CONFIG`: t-SNE 配置文件的路径。
- `--checkpoint CHECKPOINT`: 模型权重文件的路径。
- `--work-dir WORK_DIR`: 保存日志和可视化图像的目录。
- `--test-cfg TEST_CFG`: 用来加载 test_dataloader 配置的 t-SNE 配置文件的路径。
- `--vis-stage {backbone,neck,pre_logits}`: 模型可视化的阶段。
- `--class-idx CLASS_IDX [CLASS_IDX ...]`: 用来计算 t-SNE 的类别。
- `--max-num-class MAX_NUM_CLASS`: 前 N 个被应用 t-SNE 算法的类别,默认为20。
- `--max-num-samples MAX_NUM_SAMPLES`: 每个类别中最大的样本数,值越高需要的计算时间越长,默认为100。
- `--cfg-options CFG_OPTIONS [CFG_OPTIONS ...]`: 覆盖被使用的配置中的一些设定,形如 xxx=yyy 格式的关键字-值对会被合并到配置文件中。如果被覆盖的值是一个列表,它应该形如 key="[a,b]" 或者 key=a,b 。它还允许嵌套的列表/元组值,例如 key="[(a,b),(c,d)]" 。注意引号是必需的,而且不允许有空格。
- `--device DEVICE`: 用于推理的设备。
- `--legend`: 显示所有类别的图例。
- `--show`: 在图形窗口中显示结果。
- `--n-components N_COMPONENTS`: 结果的维数。
- `--perplexity PERPLEXITY`: 复杂度与其他流形学习算法中使用的最近邻的数量有关。
- `--early-exaggeration EARLY_EXAGGERATION`: 控制原空间中的自然聚类在嵌入空间中的紧密程度以及它们之间的空间大小。
- `--learning-rate LEARNING_RATE`: t-SNE 的学习率通常在[10.0, 1000.0]的范围内。如果学习率太高,数据可能看起来像一个球,其中任何一点与它最近的邻居近似等距。如果学习率太低,大多数点可能看起来被压缩在一个几乎没有异常值的密集点云中。
- `--n-iter N_ITER`: 优化的最大迭代次数。应该至少为250。
- `--n-iter-without-progress N_ITER_WITHOUT_PROGRESS`: 在我们中止优化之前,最大的没有进展的迭代次数。
- `--init INIT`: 初始化方法。
## 如何可视化分类模型的t-SNE(如 ResNet)
以下是在CIFAR-10数据集上训练的 ResNet-18 和 ResNet-50 模型上运行 t-SNE 可视化的两个样例:
```shell
python tools/visualization/vis_tsne.py \
configs/resnet/resnet18_8xb16_cifar10.py \
--checkpoint https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_b16x8_cifar10_20210528-bd6371c8.pth
python tools/visualization/vis_tsne.py \
configs/resnet/resnet50_8xb16_cifar10.py \
--checkpoint https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_b16x8_cifar10_20210528-f54bfad9.pth
```
| ResNet-18 | ResNet-50 |
| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| <div align=center><img src='https://user-images.githubusercontent.com/42371271/236410521-c4d087da-d16f-48ad-b951-c74d10c68f33.png' height="auto" width="auto" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/42371271/236411844-c97dc514-dad0-401e-ba8f-307d0a385b4e.png' height="auto" width="auto" ></div> |
## 如何可视化自监督模型的t-SNE(如 MAE)
以下是在ImageNet数据集上训练的 MAE-ViT-base 模型上运行 t-SNE 可视化的一个样例。输入数据来自 ImageNet 验证集。MAE和一些自监督预训练算法配置中没有 test_dataloader 信息。在分析这些自监督算法时,你需要在配置中添加 test_dataloader 信息,或者使用 `--test-cfg` 字段来指定一个配置文件。
```shell
python tools/visualization/vis_tsne.py \
configs/mae/mae_vit-base-p16_8xb512-amp-coslr-800e_in1k.py \
--checkpoint https://download.openmmlab.com/mmselfsup/1.x/mae/mae_vit-base-p16_8xb512-fp16-coslr-800e_in1k/mae_vit-base-p16_8xb512-coslr-800e-fp16_in1k_20220825-5d81fbc4.pth \
--test-cfg configs/_base_/datasets/imagenet_bs32.py
```
| MAE-ViT-base |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <div align=center><img src='https://github.com/open-mmlab/mmpretrain/assets/42371271/ee576c0c-abef-43d1-8866-24a5f5fd0cf6' height="auto" width="auto" ></div> |
# 数据集验证
在 MMPretrain 中,`tools/misc/verify_dataset.py` 脚本会检查数据集的所有图片,查看是否有**已经损坏**的图片。
## 工具介绍
```shell
python tools/print_config.py \
${CONFIG} \
[--out-path ${OUT-PATH}] \
[--phase ${PHASE}] \
[--num-process ${NUM-PROCESS}]
[--cfg-options ${CFG_OPTIONS}]
```
**所有参数说明**:
- `config` : 配置文件的路径。
- `--out-path` : 输出结果路径,默认为 ‘brokenfiles.log’。
- `--phase` : 检查哪个阶段的数据集,可用值为 “train” 、”test” 或者 “val”, 默认为 “train”。
- `--num-process` : 指定的进程数,默认为 1。
- `--cfg-options`: 额外的配置选项,会被合入配置文件,参考[教程 1:如何编写配置文件](https://mmpretrain.readthedocs.io/zh_CN/latest/tutorials/config.html)
## 示例:
```shell
python tools/misc/verify_dataset.py configs/t2t_vit/t2t-vit-t-14_8xb64_in1k.py --out-path broken_imgs.log --phase val --num-process 8
```
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment