Commit fdeee889 authored by limm's avatar limm
Browse files

release v1.6.1 of mmcv

parent df465820
...@@ -259,7 +259,7 @@ conv = ConvModule( ...@@ -259,7 +259,7 @@ conv = ConvModule(
self.cls = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) self.cls = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2))
# 如果我们想将模型的权重初始化为 1,将偏差初始化为 2 # 如果我们想将模型的权重初始化为 1,将偏差初始化为 2
# 但希望 `cls` 中的权重为 3,偏差为 4,则我们可以使用关键字override # 但希望 `reg` 中的权重为 3,偏差为 4,则我们可以使用关键字override
model = FooNet() model = FooNet()
init_cfg = dict(type='Constant', layer=['Conv1d','Conv2d'], val=1, bias=2, init_cfg = dict(type='Constant', layer=['Conv1d','Conv2d'], val=1, bias=2,
...@@ -355,7 +355,7 @@ conv = ConvModule( ...@@ -355,7 +355,7 @@ conv = ConvModule(
initialize(model, init_cfg) initialize(model, init_cfg)
``` ```
4. 初始化继承自BaseModule、Sequential、ModuleList的模型 4. 初始化继承自BaseModule、Sequential、ModuleList、ModuleDict的模型
`BaseModule` 继承自 `torch.nn.Module`, 它们之间唯一的不同是 `BaseModule` 实现了 `init_weight` `BaseModule` 继承自 `torch.nn.Module`, 它们之间唯一的不同是 `BaseModule` 实现了 `init_weight`
...@@ -363,9 +363,11 @@ conv = ConvModule( ...@@ -363,9 +363,11 @@ conv = ConvModule(
`ModuleList` 继承自 `BaseModule``torch.nn.ModuleList` `ModuleList` 继承自 `BaseModule``torch.nn.ModuleList`
`````python `ModuleDict` 继承自 `BaseModule``torch.nn.ModuleDict`
```python
import torch.nn as nn import torch.nn as nn
from mmcv.runner import BaseModule, Sequential, ModuleList from mmcv.runner import BaseModule, Sequential, ModuleList, ModuleDict
class FooConv1d(BaseModule): class FooConv1d(BaseModule):
...@@ -483,7 +485,50 @@ conv = ConvModule( ...@@ -483,7 +485,50 @@ conv = ConvModule(
# [[2., 2., 2.], # [[2., 2., 2.],
# [2., 2., 2.], # [2., 2., 2.],
# [2., 2., 2.]]]], requires_grad=True) # [2., 2., 2.]]]], requires_grad=True)
`````
# ModuleDict
model1 = FooConv1d(init_cfg1)
model2 = FooConv2d(init_cfg2)
modeldict = ModuleDict(dict(model1=model1, model2=model2))
modeldict.init_weights()
# modeldict['model1'].conv1d.weight
# Parameter containing:
# tensor([[[0., 0., 0., 0.],
# [0., 0., 0., 0.],
# [0., 0., 0., 0.],
# [0., 0., 0., 0.]]], requires_grad=True)
# modeldict['model2'].conv2d.weight
# Parameter containing:
# tensor([[[[2., 2., 2.],
# [2., 2., 2.],
# [2., 2., 2.]],
# ...,
# [[2., 2., 2.],
# [2., 2., 2.],
# [2., 2., 2.]]]], requires_grad=True)
# inner init_cfg has higher priority
model1 = FooConv1d(init_cfg1)
model2 = FooConv2d(init_cfg2)
init_cfg = dict(type='Constant', layer=['Conv1d', 'Conv2d'], val=4., bias=5.)
modeldict = ModuleDict(dict(model1=model1, model2=model2), init_cfg=init_cfg)
modeldict.init_weights()
# modeldict['model1'].conv1d.weight
# Parameter containing:
# tensor([[[0., 0., 0., 0.],
# [0., 0., 0., 0.],
# [0., 0., 0., 0.],
# [0., 0., 0., 0.]]], requires_grad=True)
# modeldict['model2'].conv2d.weight
# Parameter containing:
# tensor([[[[2., 2., 2.],
# [2., 2., 2.],
# [2., 2., 2.]],
# ...,
# [[2., 2., 2.],
# [2., 2., 2.],
# [2., 2., 2.]]]], requires_grad=True)
```
### Model Zoo ### Model Zoo
......
...@@ -40,6 +40,7 @@ d = 'string' ...@@ -40,6 +40,7 @@ d = 'string'
这里是一个带有预定义变量的配置文件的例子。 这里是一个带有预定义变量的配置文件的例子。
`config_a.py` `config_a.py`
```python ```python
a = 1 a = 1
b = './work_dir/{{ fileBasenameNoExtension }}' b = './work_dir/{{ fileBasenameNoExtension }}'
...@@ -65,6 +66,7 @@ c = '{{ fileExtname }}' ...@@ -65,6 +66,7 @@ c = '{{ fileExtname }}'
a = 1 a = 1
b = dict(b1=[0, 1, 2], b2=None) b = dict(b1=[0, 1, 2], b2=None)
``` ```
### 不含重复键值对从基类配置文件继承 ### 不含重复键值对从基类配置文件继承
`config_b.py` `config_b.py`
...@@ -83,6 +85,7 @@ d = 'string' ...@@ -83,6 +85,7 @@ d = 'string'
... c=(1, 2), ... c=(1, 2),
... d='string') ... d='string')
``` ```
`config_b.py`里的新字段与在`config_a.py`里的旧字段拼接 `config_b.py`里的新字段与在`config_a.py`里的旧字段拼接
### 含重复键值对从基类配置文件继承 ### 含重复键值对从基类配置文件继承
......
...@@ -252,9 +252,9 @@ flow = mmcv.flowread('compressed.jpg', quantize=True, concat_axis=1) ...@@ -252,9 +252,9 @@ flow = mmcv.flowread('compressed.jpg', quantize=True, concat_axis=1)
mmcv.flowshow(flow) mmcv.flowshow(flow)
``` ```
![progress](../../docs/_static/flow_visualization.png) ![progress](../../en/_static/flow_visualization.png)
3. 流变换 1. 流变换
```python ```python
img1 = mmcv.imread('img1.jpg') img1 = mmcv.imread('img1.jpg')
...@@ -264,12 +264,12 @@ warpped_img2 = mmcv.flow_warp(img1, flow) ...@@ -264,12 +264,12 @@ warpped_img2 = mmcv.flow_warp(img1, flow)
img1 (左) and img2 (右) img1 (左) and img2 (右)
![raw images](../../docs/_static/flow_raw_images.png) ![raw images](../../en/_static/flow_raw_images.png)
光流 (img2 -> img1) 光流 (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 ...@@ -107,6 +107,7 @@ c
d d
e e
``` ```
#### 从硬盘读取 #### 从硬盘读取
使用 `list_from_file` 读取 `a.txt` 使用 `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) 来管理具有相似功能的不同模块, 例如, 检测器中的主干网络、头部、和模型颈部。 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) 等。 在 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 中的大部分开源项目的配置系统,这些系统通过配置文件来使用注册器创建钩子、执行器、模型和数据集。 一个典型的案例是,OpenMMLab 中的大部分开源项目的配置系统,这些系统通过配置文件来使用注册器创建钩子、执行器、模型和数据集。
可以在[这里](https://mmcv.readthedocs.io/en/latest/api.html?highlight=registry#mmcv.utils.Registry)找到注册器接口使用文档。 可以在[这里](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 ...@@ -15,7 +21,7 @@ MMCV 使用 [注册器](https://github.com/open-mmlab/mmcv/blob/master/mmcv/util
2. 创建注册器 2. 创建注册器
3. 使用此注册器来管理模块 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 ...@@ -29,9 +35,10 @@ from mmcv.utils import Registry
CONVERTERS = Registry('converter') CONVERTERS = Registry('converter')
``` ```
然后我们在包中可以实现不同的转换器(converter)。例如,在 `converters/converter1.py` 中实现 `Converter1` 然后我们在包中可以实现不同的转换器(converter),其可以为类或函数。例如,在 `converters/converter1.py` 中实现 `Converter1`,在 `converters/converter2.py` 中实现 `converter2`
```python ```python
# converter1.py
from .builder import CONVERTERS from .builder import CONVERTERS
# 使用注册器管理模块 # 使用注册器管理模块
...@@ -41,19 +48,39 @@ class Converter1(object): ...@@ -41,19 +48,39 @@ class Converter1(object):
self.a = a self.a = a
self.b = b 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 ```python
'Converter1' -> <class 'Converter1'> 'Converter1' -> <class 'Converter1'>
'converter2' -> <function 'converter2'>
```
```{note}
只有模块所在的文件被导入时,注册机制才会被触发,所以您需要在某处导入该文件。更多详情请查看 https://github.com/open-mmlab/mmdetection/issues/5974。
``` ```
如果模块被成功注册了,你可以通过配置文件使用这个转换器(converter),如下所示: 如果模块被成功注册了,你可以通过配置文件使用这个转换器(converter),如下所示:
```python ```python
converter_cfg = dict(type='Converter1', a=a_value, b=b_value) converter1_cfg = dict(type='Converter1', a=a_value, b=b_value)
converter = CONVERTERS.build(converter_cfg) 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) ...@@ -84,7 +111,7 @@ CONVERTERS = Registry('converter', build_func=build_converter)
该功能类似于默认的`build_from_cfg`。在大多数情况下,默认就足够了。 该功能类似于默认的`build_from_cfg`。在大多数情况下,默认就足够了。
``` ```
`build_model_from_cfg`也实现了在`nn.Sequentail`中构建PyTorch模块,你可以直接使用它们。 `build_model_from_cfg`也实现了在`nn.Sequential`中构建PyTorch模块,你可以直接使用它们。
### 注册器层结构 ### 注册器层结构
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
### EpochBasedRunner ### 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: ...@@ -25,6 +25,7 @@ while curr_epoch < max_epochs:
for _ in range(epochs): for _ in range(epochs):
epoch_runner(data_loaders[i], **kwargs) epoch_runner(data_loaders[i], **kwargs)
``` ```
目前支持训练和验证两个工作流,以训练函数为例,其抽象逻辑是: 目前支持训练和验证两个工作流,以训练函数为例,其抽象逻辑是:
```python ```python
...@@ -40,7 +41,8 @@ def train(self, data_loader, **kwargs): ...@@ -40,7 +41,8 @@ def train(self, data_loader, **kwargs):
``` ```
### IterBasedRunner ### 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: ...@@ -59,6 +61,7 @@ while curr_iter < max_iters:
for _ in range(iters): for _ in range(iters):
iter_runner(iter_loaders[i], **kwargs) iter_runner(iter_loaders[i], **kwargs)
``` ```
目前支持训练和验证两个工作流,以验证函数为例,其抽象逻辑是: 目前支持训练和验证两个工作流,以验证函数为例,其抽象逻辑是:
```python ```python
...@@ -75,6 +78,7 @@ def val(self, data_loader, **kwargs): ...@@ -75,6 +78,7 @@ def val(self, data_loader, **kwargs):
除了上述基础功能外,`EpochBasedRunner``IterBasedRunner` 还提供了 resume 、 save_checkpoint 和注册 hook 功能。 除了上述基础功能外,`EpochBasedRunner``IterBasedRunner` 还提供了 resume 、 save_checkpoint 和注册 hook 功能。
### 一个简单例子 ### 一个简单例子
以最常用的分类任务为例详细说明 `runner` 的使用方法。 开启任何一个训练任务,都需要包括如下步骤: 以最常用的分类任务为例详细说明 `runner` 的使用方法。 开启任何一个训练任务,都需要包括如下步骤:
**(1) dataloader、model 和优化器等类初始化** **(1) dataloader、model 和优化器等类初始化**
...@@ -148,8 +152,8 @@ runner.run(data_loaders, cfg.workflow) ...@@ -148,8 +152,8 @@ runner.run(data_loaders, cfg.workflow)
关于 workflow 设置,以 `EpochBasedRunner` 为例,详情如下: 关于 workflow 设置,以 `EpochBasedRunner` 为例,详情如下:
- 假设只想运行训练工作流,则可以设置 workflow = [('train', 1)],表示只进行迭代训练 - 假设只想运行训练工作流,则可以设置 workflow = \[('train', 1)\],表示只进行迭代训练
- 假设想运行训练和验证工作流,则可以设置 workflow = [('train', 3), ('val', 1)],表示先训练 3 个 epoch ,然后切换到 val 工作流,运行 1 个 epoch,然后循环,直到训练 epoch 次数达到指定值 - 假设想运行训练和验证工作流,则可以设置 workflow = \[('train', 3), ('val', 1)\],表示先训练 3 个 epoch ,然后切换到 val 工作流,运行 1 个 epoch,然后循环,直到训练 epoch 次数达到指定值
- 工作流设置还自由定制,例如你可以先验证再训练 workflow = [('val', 1), ('train', 1)] - 工作流设置还自由定制,例如你可以先验证再训练 workflow = \[('val', 1), ('train', 1)\]
上述代码都已经封装到了各个代码库的 train.py 中,用户只需要设置相应的配置即可,上述流程会自动运行。 上述代码都已经封装到了各个代码库的 train.py 中,用户只需要设置相应的配置即可,上述流程会自动运行。
...@@ -17,7 +17,7 @@ mmcv.track_progress(func, tasks) ...@@ -17,7 +17,7 @@ mmcv.track_progress(func, tasks)
``` ```
效果如下 效果如下
![progress](../../docs/_static/progress.*) ![progress](../../en/_static/progress.*)
如果你想可视化多进程任务的进度,你可以使用 `track_parallel_progress` 如果你想可视化多进程任务的进度,你可以使用 `track_parallel_progress`
...@@ -25,7 +25,7 @@ mmcv.track_progress(func, tasks) ...@@ -25,7 +25,7 @@ mmcv.track_progress(func, tasks)
mmcv.track_parallel_progress(func, tasks, 8) # 8 workers mmcv.track_parallel_progress(func, tasks, 8) # 8 workers
``` ```
![progress](../../docs/_static/parallel_progress.*) ![progress](../../_static/parallel_progress.*)
如果你想要迭代或枚举数据列表并可视化进度,你可以使用 `track_iter_progress` 如果你想要迭代或枚举数据列表并可视化进度,你可以使用 `track_iter_progress`
...@@ -58,7 +58,6 @@ with mmcv.Timer(): ...@@ -58,7 +58,6 @@ with mmcv.Timer():
你也可以使用 `since_start()``since_last_check()` 。前者返回计时器启动后的运行时长,后者返回最近一次查看计时器后的运行时长。 你也可以使用 `since_start()``since_last_check()` 。前者返回计时器启动后的运行时长,后者返回最近一次查看计时器后的运行时长。
```python ```python
timer = mmcv.Timer() timer = mmcv.Timer()
# code block 1 here # 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 ...@@ -14,7 +14,7 @@ from mmcv.utils import get_logger
class Model(nn.Module): class Model(nn.Module):
def __init__(self): def __init__(self):
super(Model, self).__init__() super().__init__()
self.conv1 = nn.Conv2d(3, 6, 5) self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2) self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5) self.conv2 = nn.Conv2d(6, 16, 5)
......
...@@ -13,3 +13,4 @@ from .visualization import * ...@@ -13,3 +13,4 @@ from .visualization import *
# - runner # - runner
# - parallel # - parallel
# - op # - op
# - device
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
from typing import Union
import numpy as np 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]. """Quantize an array of (-inf, inf) to [0, levels-1].
Args: Args:
arr (ndarray): Input array. arr (ndarray): Input array.
min_val (scalar): Minimum value to be clipped. min_val (int or float): Minimum value to be clipped.
max_val (scalar): Maximum value to be clipped. max_val (int or float): Maximum value to be clipped.
levels (int): Quantization levels. levels (int): Quantization levels.
dtype (np.type): The type of the quantized array. dtype (np.type): The type of the quantized array.
...@@ -29,13 +35,17 @@ def quantize(arr, min_val, max_val, levels, dtype=np.int64): ...@@ -29,13 +35,17 @@ def quantize(arr, min_val, max_val, levels, dtype=np.int64):
return quantized_arr 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. """Dequantize an array.
Args: Args:
arr (ndarray): Input array. arr (ndarray): Input array.
min_val (scalar): Minimum value to be clipped. min_val (int or float): Minimum value to be clipped.
max_val (scalar): Maximum value to be clipped. max_val (int or float): Maximum value to be clipped.
levels (int): Quantization levels. levels (int): Quantization levels.
dtype (np.type): The type of the dequantized array. dtype (np.type): The type of the dequantized array.
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import logging import logging
from typing import Optional
import torch
import torch.nn as nn import torch.nn as nn
...@@ -11,8 +13,8 @@ class AlexNet(nn.Module): ...@@ -11,8 +13,8 @@ class AlexNet(nn.Module):
num_classes (int): number of classes for classification. num_classes (int): number of classes for classification.
""" """
def __init__(self, num_classes=-1): def __init__(self, num_classes: int = -1):
super(AlexNet, self).__init__() super().__init__()
self.num_classes = num_classes self.num_classes = num_classes
self.features = nn.Sequential( self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
...@@ -40,7 +42,7 @@ class AlexNet(nn.Module): ...@@ -40,7 +42,7 @@ class AlexNet(nn.Module):
nn.Linear(4096, num_classes), nn.Linear(4096, num_classes),
) )
def init_weights(self, pretrained=None): def init_weights(self, pretrained: Optional[str] = None) -> None:
if isinstance(pretrained, str): if isinstance(pretrained, str):
logger = logging.getLogger() logger = logging.getLogger()
from ..runner import load_checkpoint from ..runner import load_checkpoint
...@@ -51,7 +53,7 @@ class AlexNet(nn.Module): ...@@ -51,7 +53,7 @@ class AlexNet(nn.Module):
else: else:
raise TypeError('pretrained must be a str or None') 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) x = self.features(x)
if self.num_classes > 0: if self.num_classes > 0:
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
from typing import Dict
import torch import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
...@@ -28,12 +30,12 @@ class Clamp(nn.Module): ...@@ -28,12 +30,12 @@ class Clamp(nn.Module):
Default to 1. Default to 1.
""" """
def __init__(self, min=-1., max=1.): def __init__(self, min: float = -1., max: float = 1.):
super(Clamp, self).__init__() super().__init__()
self.min = min self.min = min
self.max = max self.max = max
def forward(self, x): def forward(self, x) -> torch.Tensor:
"""Forward function. """Forward function.
Args: Args:
...@@ -67,7 +69,7 @@ class GELU(nn.Module): ...@@ -67,7 +69,7 @@ class GELU(nn.Module):
>>> output = m(input) >>> output = m(input)
""" """
def forward(self, input): def forward(self, input: torch.Tensor) -> torch.Tensor:
return F.gelu(input) return F.gelu(input)
...@@ -78,11 +80,12 @@ else: ...@@ -78,11 +80,12 @@ else:
ACTIVATION_LAYERS.register_module(module=nn.GELU) ACTIVATION_LAYERS.register_module(module=nn.GELU)
def build_activation_layer(cfg): def build_activation_layer(cfg: Dict) -> nn.Module:
"""Build activation layer. """Build activation layer.
Args: Args:
cfg (dict): The activation layer config, which should contain: cfg (dict): The activation layer config, which should contain:
- type (str): Layer type. - type (str): Layer type.
- layer args: Args needed to instantiate an activation layer. - layer args: Args needed to instantiate an activation layer.
......
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