Commit fdeee889 authored by limm's avatar limm
Browse files

release v1.6.1 of mmcv

parent df465820
......@@ -40,6 +40,7 @@ d = 'string'
这里是一个带有预定义变量的配置文件的例子。
`config_a.py`
```python
a = 1
b = './work_dir/{{ fileBasenameNoExtension }}'
......@@ -65,6 +66,7 @@ c = '{{ fileExtname }}'
a = 1
b = dict(b1=[0, 1, 2], b2=None)
```
### 不含重复键值对从基类配置文件继承
`config_b.py`
......@@ -83,6 +85,7 @@ d = 'string'
... c=(1, 2),
... d='string')
```
`config_b.py`里的新字段与在`config_a.py`里的旧字段拼接
### 含重复键值对从基类配置文件继承
......
......@@ -252,9 +252,9 @@ flow = mmcv.flowread('compressed.jpg', quantize=True, concat_axis=1)
mmcv.flowshow(flow)
```
![progress](../../docs/_static/flow_visualization.png)
![progress](../../en/_static/flow_visualization.png)
3. 流变换
1. 流变换
```python
img1 = mmcv.imread('img1.jpg')
......@@ -264,12 +264,12 @@ warpped_img2 = mmcv.flow_warp(img1, flow)
img1 (左) and img2 (右)
![raw images](../../docs/_static/flow_raw_images.png)
![raw images](../../en/_static/flow_raw_images.png)
光流 (img2 -> img1)
![optical flow](../../docs/_static/flow_img2toimg1.png)
![optical flow](../../en/_static/flow_img2toimg1.png)
变换后的图像和真实图像的差异
![warpped image](../../docs/_static/flow_warp_diff.png)
![warpped image](../../en/_static/flow_warp_diff.png)
......@@ -107,6 +107,7 @@ c
d
e
```
#### 从硬盘读取
使用 `list_from_file` 读取 `a.txt`
......
## 算子
MMCV 提供了检测、分割等任务中常用的算子
| Device | CPU | CUDA | MLU | MPS |
| ---------------------------- | --- | ---- | --- | --- |
| ActiveRotatedFilter | √ | √ | | |
| AssignScoreWithK | | √ | | |
| BallQuery | | √ | | |
| BBoxOverlaps | | √ | √ | √ |
| BorderAlign | | √ | | |
| BoxIouRotated | √ | √ | | |
| CARAFE | | √ | | |
| ChamferDistance | | √ | | |
| CrissCrossAttention | | √ | | |
| ContourExpand | √ | | | |
| ConvexIoU | | √ | | |
| CornerPool | | √ | | |
| Correlation | | √ | | |
| Deformable Convolution v1/v2 | √ | √ | | |
| Deformable RoIPool | | √ | | |
| DiffIoURotated | | √ | | |
| DynamicScatter | | √ | | |
| FurthestPointSample | | √ | | |
| FurthestPointSampleWithDist | | √ | | |
| FusedBiasLeakyrelu | | √ | | |
| GatherPoints | | √ | | |
| GroupPoints | | √ | | |
| Iou3d | | √ | | |
| KNN | | √ | | |
| MaskedConv | | √ | | |
| MergeCells | | √ | | |
| MinAreaPolygon | | √ | | |
| ModulatedDeformConv2d | √ | √ | | |
| MultiScaleDeformableAttn | | √ | | |
| NMS | √ | √ | √ | |
| NMSRotated | √ | √ | | |
| PixelGroup | √ | | | |
| PointsInBoxes | √ | √ | | |
| PointsInPolygons | | √ | | |
| PSAMask | √ | √ | √ | |
| RotatedFeatureAlign | √ | √ | | |
| RoIPointPool3d | | √ | | |
| RoIPool | | √ | √ | |
| RoIAlignRotated | √ | √ | √ | |
| RiRoIAlignRotated | | √ | | |
| RoIAlign | √ | √ | √ | |
| RoIAwarePool3d | | √ | | |
| SAConv2d | | √ | | |
| SigmoidFocalLoss | | √ | √ | |
| SoftmaxFocalLoss | | √ | | |
| SoftNMS | | √ | | |
| Sparse Convolution | | √ | | |
| Synchronized BatchNorm | | √ | | |
| ThreeInterpolate | | √ | | |
| ThreeNN | | √ | | |
| TINShift | | √ | √ | |
| UpFirDn2d | | √ | | |
| Voxelization | √ | √ | | |
| PrRoIPool | | √ | | |
## 注册器
MMCV 使用 [注册器](https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/registry.py) 来管理具有相似功能的不同模块, 例如, 检测器中的主干网络、头部、和模型颈部。
在 OpenMMLab 家族中的绝大部分开源项目使用注册器去管理数据集和模型的模块,例如 [MMDetection](https://github.com/open-mmlab/mmdetection), [MMDetection3D](https://github.com/open-mmlab/mmdetection3d), [MMClassification](https://github.com/open-mmlab/mmclassification), [MMEditing](https://github.com/open-mmlab/mmediting) 等。
```{note}
在 v1.5.1 版本开始支持注册函数的功能。
```
### 什么是注册器
在MMCV中,注册器可以看作类到字符串的映射。
一个注册器中的类通常有相似的接口,但是可以实现不同的算法或支持不同的数据集。
借助注册器,用户可以通过使用相应的字符串查找并实例化该类,并根据他们的需要实例化对应模块。
在MMCV中,注册器可以看作类或函数到字符串的映射。
一个注册器中的类或函数通常有相似的接口,但是可以实现不同的算法或支持不同的数据集。
借助注册器,用户可以通过使用相应的字符串查找类或函数,并根据他们的需要实例化对应模块或调用函数获取结果。
一个典型的案例是,OpenMMLab 中的大部分开源项目的配置系统,这些系统通过配置文件来使用注册器创建钩子、执行器、模型和数据集。
可以在[这里](https://mmcv.readthedocs.io/en/latest/api.html?highlight=registry#mmcv.utils.Registry)找到注册器接口使用文档。
......@@ -15,7 +21,7 @@ MMCV 使用 [注册器](https://github.com/open-mmlab/mmcv/blob/master/mmcv/util
2. 创建注册器
3. 使用此注册器来管理模块
`Registry`(注册器)的参数 `build_func`(构建函数) 用来自定如何实例化类的实例,默认使用 [这里](https://mmcv.readthedocs.io/en/latest/api.html?highlight=registry#mmcv.utils.build_from_cfg)实现的`build_from_cfg`
`Registry`(注册器)的参数 `build_func`(构建函数) 用来自定如何实例化类的实例或如何调用函数获取结果,默认使用 [这里](https://mmcv.readthedocs.io/en/latest/api.html?highlight=registry#mmcv.utils.build_from_cfg) 实现的`build_from_cfg`
### 一个简单的例子
......@@ -29,9 +35,10 @@ from mmcv.utils import Registry
CONVERTERS = Registry('converter')
```
然后我们在包中可以实现不同的转换器(converter)。例如,在 `converters/converter1.py` 中实现 `Converter1`
然后我们在包中可以实现不同的转换器(converter),其可以为类或函数。例如,在 `converters/converter1.py` 中实现 `Converter1`,在 `converters/converter2.py` 中实现 `converter2`
```python
# converter1.py
from .builder import CONVERTERS
# 使用注册器管理模块
......@@ -41,19 +48,39 @@ class Converter1(object):
self.a = a
self.b = b
```
使用注册器管理模块的关键步骤是,将实现的模块注册到注册表 `CONVERTERS` 中。通过 `@CONVERTERS.register_module()` 装饰所实现的模块,字符串和类之间的映射就可以由 `CONVERTERS` 构建和维护,如下所示:
通过这种方式,就可以通过 `CONVERTERS` 建立字符串与类之间的映射,如下所示:
```python
# converter2.py
from .builder import CONVERTERS
from .converter1 import Converter1
# 使用注册器管理模块
@CONVERTERS.register_module()
def converter2(a, b)
return Converter1(a, b)
```
使用注册器管理模块的关键步骤是,将实现的模块注册到注册表 `CONVERTERS` 中。通过 `@CONVERTERS.register_module()` 装饰所实现的模块,字符串到类或函数之间的映射就可以由 `CONVERTERS` 构建和维护,如下所示:
通过这种方式,就可以通过 `CONVERTERS` 建立字符串与类或函数之间的映射,如下所示:
```python
'Converter1' -> <class 'Converter1'>
'converter2' -> <function 'converter2'>
```
```{note}
只有模块所在的文件被导入时,注册机制才会被触发,所以您需要在某处导入该文件。更多详情请查看 https://github.com/open-mmlab/mmdetection/issues/5974。
```
如果模块被成功注册了,你可以通过配置文件使用这个转换器(converter),如下所示:
```python
converter_cfg = dict(type='Converter1', a=a_value, b=b_value)
converter = CONVERTERS.build(converter_cfg)
converter1_cfg = dict(type='Converter1', a=a_value, b=b_value)
converter2_cfg = dict(type='converter2', a=a_value, b=b_value)
converter1 = CONVERTERS.build(converter1_cfg)
# returns the calling result
result = CONVERTERS.build(converter2_cfg)
```
### 自定义构建函数
......@@ -84,7 +111,7 @@ CONVERTERS = Registry('converter', build_func=build_converter)
该功能类似于默认的`build_from_cfg`。在大多数情况下,默认就足够了。
```
`build_model_from_cfg`也实现了在`nn.Sequentail`中构建PyTorch模块,你可以直接使用它们。
`build_model_from_cfg`也实现了在`nn.Sequential`中构建PyTorch模块,你可以直接使用它们。
### 注册器层结构
......
......@@ -8,7 +8,7 @@
### EpochBasedRunner
顾名思义,`EpochBasedRunner` 是指以 epoch 为周期的工作流,例如设置 workflow = [('train', 2), ('val', 1)] 表示循环迭代地训练 2 个 epoch,然后验证 1 个 epoch。MMDetection 目标检测框架默认采用的是 `EpochBasedRunner`
顾名思义,`EpochBasedRunner` 是指以 epoch 为周期的工作流,例如设置 workflow = \[('train', 2), ('val', 1)\] 表示循环迭代地训练 2 个 epoch,然后验证 1 个 epoch。MMDetection 目标检测框架默认采用的是 `EpochBasedRunner`
其抽象逻辑如下所示:
......@@ -25,6 +25,7 @@ while curr_epoch < max_epochs:
for _ in range(epochs):
epoch_runner(data_loaders[i], **kwargs)
```
目前支持训练和验证两个工作流,以训练函数为例,其抽象逻辑是:
```python
......@@ -40,7 +41,8 @@ def train(self, data_loader, **kwargs):
```
### IterBasedRunner
不同于 `EpochBasedRunner``IterBasedRunner` 是指以 iter 为周期的工作流,例如设置 workflow = [('train', 2), ('val', 1)] 表示循环迭代的训练 2 个 iter,然后验证 1 个 iter,MMSegmentation 语义分割框架默认采用的是 `EpochBasedRunner`
不同于 `EpochBasedRunner``IterBasedRunner` 是指以 iter 为周期的工作流,例如设置 workflow = \[('train', 2), ('val', 1)\] 表示循环迭代的训练 2 个 iter,然后验证 1 个 iter,MMSegmentation 语义分割框架默认采用的是 `IterBasedRunner`
其抽象逻辑如下所示:
......@@ -59,6 +61,7 @@ while curr_iter < max_iters:
for _ in range(iters):
iter_runner(iter_loaders[i], **kwargs)
```
目前支持训练和验证两个工作流,以验证函数为例,其抽象逻辑是:
```python
......@@ -75,6 +78,7 @@ def val(self, data_loader, **kwargs):
除了上述基础功能外,`EpochBasedRunner``IterBasedRunner` 还提供了 resume 、 save_checkpoint 和注册 hook 功能。
### 一个简单例子
以最常用的分类任务为例详细说明 `runner` 的使用方法。 开启任何一个训练任务,都需要包括如下步骤:
**(1) dataloader、model 和优化器等类初始化**
......@@ -148,8 +152,8 @@ runner.run(data_loaders, cfg.workflow)
关于 workflow 设置,以 `EpochBasedRunner` 为例,详情如下:
- 假设只想运行训练工作流,则可以设置 workflow = [('train', 1)],表示只进行迭代训练
- 假设想运行训练和验证工作流,则可以设置 workflow = [('train', 3), ('val', 1)],表示先训练 3 个 epoch ,然后切换到 val 工作流,运行 1 个 epoch,然后循环,直到训练 epoch 次数达到指定值
- 工作流设置还自由定制,例如你可以先验证再训练 workflow = [('val', 1), ('train', 1)]
- 假设只想运行训练工作流,则可以设置 workflow = \[('train', 1)\],表示只进行迭代训练
- 假设想运行训练和验证工作流,则可以设置 workflow = \[('train', 3), ('val', 1)\],表示先训练 3 个 epoch ,然后切换到 val 工作流,运行 1 个 epoch,然后循环,直到训练 epoch 次数达到指定值
- 工作流设置还自由定制,例如你可以先验证再训练 workflow = \[('val', 1), ('train', 1)\]
上述代码都已经封装到了各个代码库的 train.py 中,用户只需要设置相应的配置即可,上述流程会自动运行。
......@@ -17,7 +17,7 @@ mmcv.track_progress(func, tasks)
```
效果如下
![progress](../../docs/_static/progress.*)
![progress](../../en/_static/progress.*)
如果你想可视化多进程任务的进度,你可以使用 `track_parallel_progress`
......@@ -25,7 +25,7 @@ mmcv.track_progress(func, tasks)
mmcv.track_parallel_progress(func, tasks, 8) # 8 workers
```
![progress](../../docs/_static/parallel_progress.*)
![progress](../../_static/parallel_progress.*)
如果你想要迭代或枚举数据列表并可视化进度,你可以使用 `track_iter_progress`
......@@ -58,7 +58,6 @@ with mmcv.Timer():
你也可以使用 `since_start()``since_last_check()` 。前者返回计时器启动后的运行时长,后者返回最近一次查看计时器后的运行时长。
```python
timer = mmcv.Timer()
# code block 1 here
......
## 拉取请求
### 什么是拉取请求?
`拉取请求` (Pull Request), [GitHub 官方文档](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests)定义如下。
>拉取请求是一种通知机制。你修改了他人的代码,将你的修改通知原来作者,希望他合并你的修改。
### 基本的工作流:
1. 获取最新的代码库
2. 从主分支创建最新的分支进行开发
3. 提交修改
4. 推送你的修改并创建一个`拉取请求`
5. 讨论、审核代码
6. 将开发分支合并到主分支
### 具体步骤
1. 获取最新的代码库
+ 当你第一次提 PR 时
- 复刻 OpenMMLab 原代码库,点击 GitHub 页面右上角的 **Fork** 按钮即可
![avatar](../../docs/_static/community/1.png)
- 克隆复刻的代码库到本地
```bash
git clone git@github.com:XXX/mmcv.git
```
- 添加原代码库为上游代码库
```bash
git remote add upstream git@github.com:open-mmlab/mmcv
```
+ 从第二个 PR 起
- 检出本地代码库的主分支,然后从最新的原代码库的主分支拉取更新
```bash
git checkout master
git pull upstream master
```
2. 从主分支创建一个新的开发分支
```bash
git checkout -b branchname
```
注意:为了保证提交历史清晰可读,我们强烈推荐您先检出主分支 (master),再创建新的分支。
3. 提交你的修改
```bash
# coding
git add [files]
git commit -m 'messages'
```
4. 推送你的修改到复刻的代码库,并创建一个`拉取请求`
+ 推送当前分支到远端复刻的代码库
```bash
git push origin branchname
```
+ 创建一个`拉取请求`
![avatar](../../docs/_static/community/2.png)
+ 修改`拉取请求`信息模板,描述修改原因和修改内容。还可以在 PR 描述中,手动关联到相关的`议题` (issue),(更多细节,请参考[官方文档](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue))。
5. 讨论并评审你的代码
+ 创建`拉取请求`时,可以关联给相关人员进行评审
![avatar](../../docs/_static/community/3.png)
+ 根据评审人员的意见修改代码,并推送修改
6. `拉取请求`合并之后删除该分支
```bash
git branch -d branchname # delete local branch
git push origin --delete branchname # delete remote branch
```
### PR 规范
1. 使用 [pre-commit hook](https://pre-commit.com),尽量减少代码风格相关问题
2. 一个PR对应一个短期分支
3. 粒度要细,一个PR只做一件事情,避免超大的PR
>- Bad:实现Faster R-CNN
>- Acceptable:给 Faster R-CNN 添加一个 box head
>- Good:给 box head 增加一个参数来支持自定义的 conv 层数
4. 每次 Commit 时需要提供清晰且有意义 commit 信息
5. 提供清晰且有意义的`拉取请求`描述
>- 标题写明白任务名称,一般格式:[Prefix] Short description of the pull request (Suffix)
>- prefix: 新增功能 [Feature], 修 bug [Fix], 文档相关 [Docs], 开发中 [WIP] (暂时不会被review)
>- 描述里介绍`拉取请求`的主要修改内容,结果,以及对其他部分的影响, 参考`拉取请求`模板
>- 关联相关的`议题` (issue) 和其他`拉取请求`
## 常见问题
在这里我们列出了用户经常遇到的问题以及对应的解决方法。如果您遇到了其他常见的问题,并且知道可以帮到大家的解决办法,
欢迎随时丰富这个列表。
- MMCV 和 MMDetection 的兼容性问题;"ConvWS is already registered in conv layer"
请按照上述说明为您的 MMDetection 版本安装正确版本的 MMCV。
- "No module named 'mmcv.ops'"; "No module named 'mmcv._ext'"
1. 使用 `pip uninstall mmcv` 卸载您环境中的 mmcv
2. 按照上述说明安装 mmcv-full
- "invalid device function" 或者 "no kernel image is available for execution"
1. 检查 GPU 的 CUDA 计算能力
2. 运行 `python mmdet/utils/collect_env.py` 来检查 PyTorch、torchvision 和 MMCV 是否是针对正确的 GPU 架构构建的
您可能需要去设置 `TORCH_CUDA_ARCH_LIST` 来重新安装 MMCV
兼容性问题的可能会出现在使用旧版的 GPUs,如:colab 上的 Tesla K80 (3.7)
3. 检查运行环境是否和 mmcv/mmdet 编译时的环境相同。例如,您可能使用 CUDA 10.0 编译 mmcv,但在 CUDA 9.0 的环境中运行它
- "undefined symbol" 或者 "cannot open xxx.so"。
1. 如果符号和 CUDA/C++ 相关(例如:libcudart.so 或者 GLIBCXX),请检查 CUDA/GCC 运行时的版本是否和编译 mmcv 的一致
2. 如果符号和 PyTorch 相关(例如:符号包含 caffe、aten 和 TH),请检查 PyTorch 运行时的版本是否和编译 mmcv 的一致
3. 运行 `python mmdet/utils/collect_env.py` 以检查 PyTorch、torchvision 和 MMCV 构建和运行的环境是否相同
- "RuntimeError: CUDA error: invalid configuration argument"。
这个错误可能是由于您的 GPU 性能不佳造成的。尝试降低[THREADS_PER_BLOCK](https://github.com/open-mmlab/mmcv/blob/cac22f8cf5a904477e3b5461b1cc36856c2793da/mmcv/ops/csrc/common_cuda_helper.hpp#L10)
的值并重新编译 mmcv。
- "RuntimeError: nms is not compiled with GPU support"。
这个错误是由于您的 CUDA 环境没有正确安装。
您可以尝试重新安装您的 CUDA 环境,然后删除 mmcv/build 文件夹并重新编译 mmcv。
## CUDA 算子
MMCV 提供了检测、分割等任务中常用的 CUDA 算子
- AssignScoreWithK
- BallQuery
- BBoxOverlaps
- CARAFE
- CrissCrossAttention
- ContextBlock
- CornerPool
- Deformable Convolution v1/v2
- Deformable RoIPool
- DynamicScatter
- GatherPoints
- FurthestPointSample
- FurthestPointSampleWithDist
- GeneralizedAttention
- KNN
- MaskedConv
- NMS
- PSAMask
- RoIPointPool3d
- RoIPool
- RoIAlign
- RoIAwarePool3d
- SimpleRoIAlign
- SigmoidFocalLoss
- SoftmaxFocalLoss
- SoftNMS
- Synchronized BatchNorm
- Voxelization
- ThreeInterpolate
- ThreeNN
- Weight standardization
- Correlation
......@@ -14,7 +14,7 @@ from mmcv.utils import get_logger
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
super().__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
......
......@@ -13,3 +13,4 @@ from .visualization import *
# - runner
# - parallel
# - op
# - device
# Copyright (c) OpenMMLab. All rights reserved.
from typing import Union
import numpy as np
def quantize(arr, min_val, max_val, levels, dtype=np.int64):
def quantize(arr: np.ndarray,
min_val: Union[int, float],
max_val: Union[int, float],
levels: int,
dtype=np.int64) -> tuple:
"""Quantize an array of (-inf, inf) to [0, levels-1].
Args:
arr (ndarray): Input array.
min_val (scalar): Minimum value to be clipped.
max_val (scalar): Maximum value to be clipped.
min_val (int or float): Minimum value to be clipped.
max_val (int or float): Maximum value to be clipped.
levels (int): Quantization levels.
dtype (np.type): The type of the quantized array.
......@@ -29,13 +35,17 @@ def quantize(arr, min_val, max_val, levels, dtype=np.int64):
return quantized_arr
def dequantize(arr, min_val, max_val, levels, dtype=np.float64):
def dequantize(arr: np.ndarray,
min_val: Union[int, float],
max_val: Union[int, float],
levels: int,
dtype=np.float64) -> tuple:
"""Dequantize an array.
Args:
arr (ndarray): Input array.
min_val (scalar): Minimum value to be clipped.
max_val (scalar): Maximum value to be clipped.
min_val (int or float): Minimum value to be clipped.
max_val (int or float): Maximum value to be clipped.
levels (int): Quantization levels.
dtype (np.type): The type of the dequantized array.
......
# Copyright (c) OpenMMLab. All rights reserved.
import logging
from typing import Optional
import torch
import torch.nn as nn
......@@ -11,8 +13,8 @@ class AlexNet(nn.Module):
num_classes (int): number of classes for classification.
"""
def __init__(self, num_classes=-1):
super(AlexNet, self).__init__()
def __init__(self, num_classes: int = -1):
super().__init__()
self.num_classes = num_classes
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
......@@ -40,7 +42,7 @@ class AlexNet(nn.Module):
nn.Linear(4096, num_classes),
)
def init_weights(self, pretrained=None):
def init_weights(self, pretrained: Optional[str] = None) -> None:
if isinstance(pretrained, str):
logger = logging.getLogger()
from ..runner import load_checkpoint
......@@ -51,7 +53,7 @@ class AlexNet(nn.Module):
else:
raise TypeError('pretrained must be a str or None')
def forward(self, x):
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
if self.num_classes > 0:
......
This diff is collapsed.
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