{"notes":[{"id":"classic-cnns/deformable-convnets","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"Deformable ConvNets","subtitle":"凌波微步","author":"Z. Yuan","paperDate":"2017-03-17","publishedOn":"2024-05-01","world":"external/deformable-convnets","paperTitle":"Deformable Convolutional Networks","paperUrl":"https://arxiv.org/abs/1703.06211","bibliography":{"title":"Deformable Convolutional Networks","authors":["Jifeng Dai","Haozhi Qi","Yuwen Xiong","Yi Li","Guodong Zhang","Han Hu","Yichen Wei"],"url":"https://arxiv.org/abs/1703.06211","pdfUrl":"https://arxiv.org/pdf/1703.06211v3","codeUrl":null,"preprint":{"id":"1703.06211","submittedOn":"2017-03-17","url":"https://arxiv.org/abs/1703.06211"},"publication":{"venue":"ICCV","year":2017,"kind":"conference","url":"https://openaccess.thecvf.com/content_iccv_2017/html/Dai_Deformable_Convolutional_Networks_ICCV_2017_paper.html"},"citation":"@misc{classiccnnsdeformableconvnets2017,\n  title = {{Deformable Convolutional Networks}},\n  author = {Jifeng Dai and Haozhi Qi and Yuwen Xiong and Yi Li and Guodong Zhang and Han Hu and Yichen Wei},\n  year = {2017},\n  eprint = {1703.06211},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1703.06211}\n}\n","citationPath":"/content/papers/citations/classic-cnns/deformable-convnets.f724f3ff1784.bib"},"thumbnail":{"src":"/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png","width":987,"height":633},"locale":"zh-hans","path":"/content/papers/zh-hans/classic-cnns/deformable-convnets.b75c34ef240f.md","headings":[{"id":"定义问题","text":"定义问题","depth":2},{"id":"固定的网格","text":"固定的网格","depth":3},{"id":"已经有人让网络学变形","text":"已经有人让网络学变形","depth":3},{"id":"解决问题","text":"解决问题","depth":2},{"id":"卷积的两个步骤","text":"卷积的两个步骤","depth":3},{"id":"每个点加一个偏移量","text":"每个点加一个偏移量","depth":3},{"id":"小数位置怎么取值","text":"小数位置怎么取值","depth":3},{"id":"偏移量从哪里来","text":"偏移量从哪里来","depth":3},{"id":"两个特例","text":"两个特例","depth":3},{"id":"可变形-roi-pooling","text":"可变形 RoI pooling","depth":3},{"id":"放进网络","text":"放进网络","depth":3},{"id":"讨论","text":"讨论","depth":2},{"id":"感受野真的会跟着物体变","text":"感受野真的会跟着物体变","depth":3},{"id":"要换几层","text":"要换几层","depth":3},{"id":"和空洞卷积比","text":"和空洞卷积比","depth":3},{"id":"代价","text":"代价","depth":3},{"id":"coco","text":"COCO","depth":3},{"id":"结论","text":"结论","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/stn","relation":"referenced","topic":""},{"id":"object-detection/deformable-detr","relation":"referenced","topic":""}]},{"id":"classic-cnns/stn","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"STN","subtitle":"乾坤大挪移","author":"Z. Yuan","paperDate":"2015-06-05","publishedOn":"2023-10-25","world":"external/stn","paperTitle":"Spatial Transformer Networks","paperUrl":"https://arxiv.org/abs/1506.02025","bibliography":{"title":"Spatial Transformer Networks","authors":["Max Jaderberg","Karen Simonyan","Andrew Zisserman","Koray Kavukcuoglu"],"url":"https://arxiv.org/abs/1506.02025","pdfUrl":"https://arxiv.org/pdf/1506.02025v3","codeUrl":null,"preprint":{"id":"1506.02025","submittedOn":"2015-06-05","url":"https://arxiv.org/abs/1506.02025"},"publication":{"venue":"NeurIPS","year":2015,"kind":"conference","url":"https://papers.nips.cc/paper_files/paper/2015/hash/33ceb07bf4eeb3da587e268d663aba1a-Abstract.html"},"citation":"@misc{classiccnnsstn2015,\n  title = {{Spatial Transformer Networks}},\n  author = {Max Jaderberg and Karen Simonyan and Andrew Zisserman and Koray Kavukcuoglu},\n  year = {2015},\n  eprint = {1506.02025},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1506.02025}\n}\n","citationPath":"/content/papers/citations/classic-cnns/stn.e823b15c17eb.bib"},"thumbnail":{"src":"/content/papers/assets/7b29191fd34e282fbd2d0fa901014f5770192cf1641d814b39b43a903817fb7f.png","width":480,"height":364},"locale":"zh-hans","path":"/content/papers/zh-hans/classic-cnns/stn.980bd4bd8a58.md","headings":[{"id":"定义问题","text":"定义问题","depth":2},{"id":"解决问题","text":"解决问题","depth":2},{"id":"三个零件","text":"三个零件","depth":3},{"id":"网格生成器从输出往回找","text":"网格生成器：从输出往回找","depth":3},{"id":"采样器在格点之间取值","text":"采样器：在格点之间取值","depth":3},{"id":"放进网络里","text":"放进网络里","depth":3},{"id":"讨论","text":"讨论","depth":2},{"id":"变形的-mnist","text":"变形的 MNIST","depth":3},{"id":"街景门牌号码","text":"街景门牌号码","depth":3},{"id":"鸟类细粒度分类","text":"鸟类细粒度分类","depth":3},{"id":"结论","text":"结论","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"text-recognition/rare","relation":"referenced","topic":""}]},{"id":"object-detection/deformable-detr","category":"object-detection","legacyPath":"object-detection/2010-deformable-detr","legacySlug":"2010-deformable-detr","legacyPaths":[],"title":"Deformable DETR","subtitle":"可变形的注意力机制","author":"Z. Yuan","paperDate":"2020-10-08","publishedOn":"2024-09-20","paperTitle":"Deformable DETR: Deformable Transformers for End-to-End Object Detection","paperUrl":"https://arxiv.org/abs/2010.04159","bibliography":{"title":"Deformable DETR: Deformable Transformers for End-to-End Object Detection","authors":["Xizhou Zhu","Weijie Su","Lewei Lu","Bin Li","Xiaogang Wang","Jifeng Dai"],"url":"https://arxiv.org/abs/2010.04159","pdfUrl":"https://arxiv.org/pdf/2010.04159","codeUrl":null,"preprint":{"id":"2010.04159","submittedOn":"2020-10-08","url":"https://arxiv.org/abs/2010.04159"},"publication":{"venue":"ICLR","year":2021,"kind":"conference","url":"https://arxiv.org/abs/2010.04159"},"citation":"@misc{objectdetectiondeformabledetr2020,\n  title = {{Deformable DETR: Deformable Transformers for End-to-End Object Detection}},\n  author = {Xizhou Zhu and Weijie Su and Lewei Lu and Bin Li and Xiaogang Wang and Jifeng Dai},\n  year = {2020},\n  eprint = {2010.04159},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/2010.04159}\n}\n","citationPath":"/content/papers/citations/object-detection/deformable-detr.df721a30472e.bib"},"thumbnail":{"src":"/content/papers/assets/36edfc5595a85c73fcdc9a4bbb9e1ca512e1f100676eb7c37203d71c84905c05.jpg","width":1224,"height":752},"locale":"zh-hans","path":"/content/papers/zh-hans/object-detection/deformable-detr.d2987152d10d.md","headings":[{"id":"定义问题","text":"定义问题","depth":2},{"id":"解决问题","text":"解决问题","depth":2},{"id":"可变形注意力","text":"可变形注意力","depth":3},{"id":"多尺度计算","text":"多尺度计算","depth":3},{"id":"模型架构","text":"模型架构","depth":3},{"id":"程式码拆解","text":"程式码拆解","depth":3},{"id":"训练策略","text":"训练策略","depth":3},{"id":"讨论","text":"讨论","depth":2},{"id":"与-detr-的比较","text":"与 DETR 的比较","depth":3},{"id":"消融实验","text":"消融实验","depth":3},{"id":"sota-比较","text":"SOTA 比较","depth":3},{"id":"结论","text":"结论","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"object-detection/detr","relation":"background","topic":"集合预测与注意力检测器"},{"id":"object-detection/h-detr","relation":"next","topic":"集合预测与注意力检测器"},{"id":"object-detection/smca-detr","relation":"comparison","topic":"让 Transformer 检测器更快收敛"}]}],"text":"同一张照片里，近处的狗很大，远处的狗很小；同一只猫，可以坐着、躺着，也可以伸懒腰。识别模型要应付的，除了类别，还有这些几何上的变化。\n\n卷积网络处理这件事的方式，主要是靠数据。训练时把图像翻转、缩放、裁剪，让网络看过足够多种样子，再用足够大的模型把它们记下来。网络本身的结构却是固定的，$3\\times3$ 卷积永远在一个方方正正的网格上采样，池化永远按固定比例缩小，RoI pooling 永远把候选框切成固定的格子。\n\n2017 年 3 月，微软亚洲研究院的 Jifeng Dai 等人发布了这篇论文。他们想让卷积的采样位置自己动起来。\n\n## 定义问题\n\n### 固定的网格\n\n作者指出，传统上处理几何变化有两种做法。一种是数据增强，让训练数据涵盖足够多的变化；另一种是设计对变化不敏感的特征和算法，例如 SIFT，或是用滑动窗口做检测。\n\n两种做法有同一个前提，就是事先知道会有哪些变化。数据增强要先决定怎么转、怎么缩；手工设计的不变特征，遇到太复杂的变化也做不出来。新的任务只要出现没想到的变化，这些做法就没辙了。\n\n卷积网络也有同样的问题，原因出在固定的结构上。作者举了两个例子：\n\n1. **同一层的感受野都一样大**。高层的特征带有语义，不同位置可能对应到大小、形状都不一样的物体，感受野却一模一样。像语义分割这种需要精细定位的任务，会希望感受野能跟着物体调整。\n2. **检测仍然靠方框取特征**。当时的目标检测方法，最后都是在矩形的候选框里取特征。对人、动物这种形状不固定的物体，方框里有一大半是背景。\n\n### 已经有人让网络学变形\n\n让网络自己学几何变换，之前已经有 STN。它预测一组全局的变换参数，例如仿射矩阵，再把整张特征图扭过去。\n\n\n> [!TIP]\n> \n> STN 可以看我们的笔记：\n> \n> - **[[15.06] STN：乾坤大挪移](/blog/papers/classic-cnns/stn)**\n\n\nSTN 对整张特征图套用同一个变换，计算量大，变换参数也不好学，当时的成功案例主要是小规模的图像分类。检测和分割的画面里有很多物体，每个物体的大小、姿态都不一样，一个全局的变换顾不过来。\n\n能不能让卷积的每一个采样点，在每一个位置上，都按照眼前的内容各自移动？\n\n## 解决问题\n\n### 卷积的两个步骤\n\n先把普通的卷积写清楚。作者把 2D 卷积拆成两步，先在输入特征图上按照一个规则的网格 $\\mathcal{R}$ 采样，再把采到的值乘上权重求和。\n\n$3\\times3$ 卷积的网格是\n\n$$\n\\mathcal{R}=\\{(-1,-1),(-1,0),\\ldots,(0,1),(1,1)\\},\n$$\n\n共 9 个点。输出特征图上的每个位置 $\\mathbf{p}_0$，\n\n$$\n\\mathbf{y}(\\mathbf{p}_0)=\\sum_{\\mathbf{p}_n\\in\\mathcal{R}}\\mathbf{w}(\\mathbf{p}_n)\\cdot\\mathbf{x}(\\mathbf{p}_0+\\mathbf{p}_n).\n$$\n\n$\\mathbf{p}_n$ 依次走过网格上的 9 个点。这条式子在每个通道上都一样，所以后面都只看 2D 的空间位置。\n\n### 每个点加一个偏移量\n\n可变形卷积只改一个地方，为网格上的每个点加一个偏移量 $\\Delta\\mathbf{p}_n$：\n\n$$\n\\mathbf{y}(\\mathbf{p}_0)=\\sum_{\\mathbf{p}_n\\in\\mathcal{R}}\\mathbf{w}(\\mathbf{p}_n)\\cdot\\mathbf{x}(\\mathbf{p}_0+\\mathbf{p}_n+\\Delta\\mathbf{p}_n).\n$$\n\n权重 $\\mathbf{w}$ 不变，变的是去哪里取值。\n\n[![四张 3x3 卷积采样位置的示意图，背景是虚线网格。(a) 绿色的 9 个点排成规则的 3x3 方格；(b) 浅绿色的原始位置各自被浅蓝色箭头推到深蓝色的新位置，方向和距离都不一样；(c) 9 个点从中心等比例往外推，形成一个放大的方格；(d) 9 个点整体转了一个角度](/content/papers/assets/45479d40b2beb8096d042f56871e08317e36ffc2be1857a325732d436b36da0f.png)](/content/papers/assets/45479d40b2beb8096d042f56871e08317e36ffc2be1857a325732d436b36da0f.png)\n\n*原论文 Figure 1，取自 [**v3 PDF 第 2 页**](https://arxiv.org/pdf/1703.06211v3#page=2)。(a) 普通卷积的规则采样网格；(b) 可变形卷积，每个点加上偏移量；(c)(d) 是 (b) 的特例，分别对应缩放与旋转。*\n\n(a) 是普通的卷积。(b) 的 9 个点各自被推到新的位置，方向、距离都不一样。(c) 和 (d) 是 (b) 的特例。(c) 的所有点从中心往外推，相当于把卷积核放大；(d) 的所有点一起绕中心转，相当于旋转。也就是说，缩放、长宽比、旋转这些变换，都包含在「每个点各自偏移」里面。\n\n### 小数位置怎么取值\n\n偏移量是学出来的，通常不会刚好是整数，采样点会落在格点之间。作者用双线性插值取值：\n\n$$\n\\mathbf{x}(\\mathbf{p})=\\sum_{\\mathbf{q}}G(\\mathbf{q},\\mathbf{p})\\cdot\\mathbf{x}(\\mathbf{q}),\\qquad\nG(\\mathbf{q},\\mathbf{p})=g(q_x,p_x)\\cdot g(q_y,p_y),\\qquad g(a,b)=\\max(0,1-|a-b|).\n$$\n\n$\\mathbf{q}$ 走过特征图上所有的整数位置，但 $g$ 只在距离小于 1 时不为 0，所以实际上只有最近的四个点有权重。我们拿一张 $5\\times5$ 的特征图算一次，中心位置 $\\mathbf{p}_0=(2,2)$，网格上右上角那个点 $\\mathbf{p}_n=(-1,1)$，假设学到的偏移量是 $(0.25,0.5)$：\n\n```python\nimport torch\n\nx = torch.arange(25.).reshape(5, 5)                # 一张 5×5 的特征图，数值就是 0 到 24\nprint(x)\n\ndef g(a, b):\n    return max(0.0, 1 - abs(a - b))\n\ndef sample(x, py, px):\n    \"\"\"式 (3)、(4)：对所有整数位置 q 加权，只有最近的四个点权重不为 0\"\"\"\n    total = 0.0\n    for qy in range(x.shape[0]):\n        for qx in range(x.shape[1]):\n            w = g(qy, py) * g(qx, px)\n            if w > 0:\n                print(f\"  q=({qy},{qx}) 值 {x[qy, qx].item():4.1f} 权重 {w:.3f}\")\n                total += w * x[qy, qx].item()\n    return total\n\np0, pn, dp = (2, 2), (-1, 1), (0.25, 0.5)           # 中心位置、网格上的右上角、学到的偏移量\npy, px = p0[0] + pn[0] + dp[0], p0[1] + pn[1] + dp[1]\nprint(f\"采样位置 p0 + pn + Δpn = ({py}, {px})\")\nprint(\"插值结果：\", sample(x, py, px))\n```\n\n```text\ntensor([[ 0.,  1.,  2.,  3.,  4.],\n        [ 5.,  6.,  7.,  8.,  9.],\n        [10., 11., 12., 13., 14.],\n        [15., 16., 17., 18., 19.],\n        [20., 21., 22., 23., 24.]])\n采样位置 p0 + pn + Δpn = (1.25, 3.5)\n  q=(1,3) 值  8.0 权重 0.375\n  q=(1,4) 值  9.0 权重 0.375\n  q=(2,3) 值 13.0 权重 0.125\n  q=(2,4) 值 14.0 权重 0.125\n插值结果： 9.75\n```\n\n原本右上角的点在 $(1,3)$，值是 8。偏移后落在 $(1.25,3.5)$，四个邻居按距离分配权重，四个权重加起来是 1。这张特征图的值刚好是 $5\\times\\text{行}+\\text{列}$，代进去 $5\\times1.25+3.5=9.75$，和插值的结果一样。\n\n插值是连续的，输出对 $\\Delta\\mathbf{p}_n$ 可微，梯度就能一路传回偏移量。\n\n### 偏移量从哪里来\n\n[![可变形卷积的示意图。左边是 input feature map，上面标着一组 3x3 采样框。一条绿色箭头把输入送进 conv，产生通道数为 2N 的 offset field，从中取出这个位置的 offsets，画成 3x3 个方向不同的小箭头。offsets 往下送到 deformable convolution，输入特征图上变形后的采样位置以蓝色虚线连到右边 output feature map 上的一个点](/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png)](/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png)\n\n*原论文 Figure 2，取自 [**v3 PDF 第 2 页**](https://arxiv.org/pdf/1703.06211v3#page=2)。$3\\times3$ 可变形卷积。*\n\n偏移量由另一个卷积层从**同一张输入特征图**算出来，就是图上方那条绿色的路径。这个卷积的大小和 dilation 都和主卷积一样，输出的 offset field 空间大小和输入相同，通道数是 $2N$。$3\\times3$ 卷积有 $N=9$ 个采样点，每个点需要 $(\\Delta y,\\Delta x)$ 两个数，所以是 18 个通道。\n\n换句话说，特征图上每个位置都有自己的一组 9 个偏移量，偏移量又是看着该位置附近的内容算出来的。这就是作者说的 local、dense、adaptive。\n\n我们用 torchvision 的 `deform_conv2d` 搭一个完整的层：\n\n```python\nimport torch\nimport torch.nn as nn\nfrom torchvision.ops import deform_conv2d\n\nclass DeformableConv(nn.Module):\n    def __init__(self, cin, cout, k=3):\n        super().__init__()\n        self.weight = nn.Parameter(torch.randn(cout, cin, k, k) * 0.01)\n        # 预测偏移量的卷积：和主卷积同样是 3×3，输出 2N = 18 个通道，初始化为 0\n        self.offset = nn.Conv2d(cin, 2 * k * k, k, padding=k // 2)\n        nn.init.zeros_(self.offset.weight)\n        nn.init.zeros_(self.offset.bias)\n\n    def forward(self, x):\n        offset = self.offset(x)                        # [B, 18, H, W]：每个输出位置各有 9 组 (Δy, Δx)\n        print(\"偏移量的形状：\", tuple(offset.shape))\n        return deform_conv2d(x, offset, self.weight, padding=1)\n\nm = DeformableConv(256, 256)\ny = m(torch.randn(2, 256, 38, 38))\nprint(\"输出的形状：\", tuple(y.shape))\nprint(f\"主卷积参数 {m.weight.numel():,}，偏移卷积参数 {sum(p.numel() for p in m.offset.parameters()):,}\")\n```\n\n```text\n偏移量的形状： (2, 18, 38, 38)\n输出的形状： (2, 256, 38, 38)\n主卷积参数 589,824，偏移卷积参数 41,490\n```\n\n偏移卷积只多了主卷积 7% 左右的参数。它的权重初始化为 0，所以刚开始训练时，偏移量全是 0，整层就是普通的卷积；偏移卷积的学习率是其他层的 $\\beta$ 倍，默认 $\\beta=1$。主卷积的权重和偏移量在训练时一起学，不需要任何额外的标注。\n\n### 两个特例\n\nFigure 1(c) 那种等比例放大，其实就是空洞卷积。我们直接验证这两个特例：\n\n```python\nimport torch\nimport torch.nn.functional as F\nfrom torchvision.ops import deform_conv2d\n\ntorch.manual_seed(0)\nx = torch.randn(1, 8, 7, 7)\nw = torch.randn(16, 8, 3, 3)\n\n# 1. 偏移量全为 0：和普通的 3×3 卷积一样\noffset = torch.zeros(1, 2 * 9, 7, 7)                # 9 个采样点，每点 (Δy, Δx)\ndiff = (deform_conv2d(x, offset, w, padding=1) - F.conv2d(x, w, padding=1)).abs().max()\nprint(\"偏移量为 0，和普通卷积的差异：\", diff.item())\n\n# 2. 每个采样点往外推一格：等于 dilation=2 的空洞卷积\ngrid = [(i, j) for i in (-1, 0, 1) for j in (-1, 0, 1)]\noffset = torch.tensor([v for i, j in grid for v in (i, j)], dtype=torch.float32)\noffset = offset.view(1, 18, 1, 1).expand(1, 18, 7, 7)\ndiff = (deform_conv2d(x, offset, w, padding=1) - F.conv2d(x, w, padding=2, dilation=2)).abs().max()\nprint(\"偏移量等于网格坐标，和 dilation=2 的差异：\", diff.item())\n```\n\n```text\n偏移量为 0，和普通卷积的差异： 0.0\n偏移量等于网格坐标，和 dilation=2 的差异： 0.0\n```\n\n第二个例子里，网格上的点 $(i,j)$ 加上偏移量 $(i,j)$，就跑到 $(2i,2j)$，正好是 dilation 为 2 的空洞卷积。空洞卷积的放大倍率要事先写死，可变形卷积则让网络在每个位置自己决定。\n\n### 可变形 RoI pooling\n\n同样的想法也用在检测的 RoI pooling 上。RoI pooling 把一个候选框切成 $k\\times k$ 个格子，每格取平均，输出固定大小的特征。可变形 RoI pooling 为每一格加一个偏移量，让格子可以往物体身上移动。\n\n偏移量的算法有点不同。作者先做一次普通的 RoI pooling，接一层全连接层，输出「归一化」的偏移量 $\\Delta\\hat{\\mathbf{p}}_{ij}$，再乘上候选框的宽高，换成实际的偏移量：\n\n$$\n\\Delta\\mathbf{p}_{ij}=\\gamma\\cdot\\Delta\\hat{\\mathbf{p}}_{ij}\\circ(w,h),\\qquad \\gamma=0.1.\n$$\n\n乘上宽高，是为了让偏移量的学习和候选框的大小无关。R-FCN 用的 position-sensitive RoI pooling 也有对应的可变形版本，偏移量改由一个卷积层对整张图算出来。\n\n### 放进网络\n\n作者把可变形卷积放进两个在 ImageNet 上预训练好的骨干，分别是 ResNet-101，以及修正了特征对齐问题的 Aligned-Inception-ResNet。和当时的常见做法一样，最后一个阶段（conv5）的步幅从 2 改成 1，卷积改成 dilation 2，让输出特征图的分辨率从 1/32 提高到 1/16。\n\n可变形卷积只替换 conv5 最后几层 $3\\times3$ 卷积，默认是最后 3 层。骨干后面接四种任务网络，分别是语义分割的 DeepLab、只有一个阶段的 class-aware RPN，以及两阶段检测的 Faster R-CNN 和 R-FCN。后两者还可以再把 RoI pooling 换成可变形的版本。\n\n## 讨论\n\n语义分割用 PASCAL VOC 和 Cityscapes，指标是 mIoU；检测用 PASCAL VOC 和 COCO，指标是 mAP。\n\n### 感受野真的会跟着物体变\n\n[![两层卷积感受野的示意图，背景是一张有小羊和大羊的灰度照片，分成三层叠起来。(a) standard convolution：上层小羊和大羊身上各有一个点，往下两层对应到的采样点都是固定大小的方格。(b) deformable convolution：同样的两个点，小羊那边的采样点聚在小羊身上，大羊那边的采样点散得很开，覆盖整只大羊](/content/papers/assets/e05ab32de27fdd8551794ded9ab71fafae0ea423979ef7250f9e7d31cedf1534.png)](/content/papers/assets/e05ab32de27fdd8551794ded9ab71fafae0ea423979ef7250f9e7d31cedf1534.png)\n\n*原论文 Figure 5，取自 [**v3 PDF 第 4 页**](https://arxiv.org/pdf/1703.06211v3#page=4)。两层普通卷积 (a) 与两层可变形卷积 (b) 的感受野。*\n\n从最上层的一个点往下追，经过两层 $3\\times3$ 卷积，会对应到底层的一群采样点。(a) 是普通卷积，不管点落在小羊还是大羊身上，采样范围都是一样大的方格。(b) 换成可变形卷积之后，小羊身上的点只看小羊，大羊身上的点则散开到整只羊。\n\n这是示意图，作者另外做了量化。他们定义了一个指标叫 effective dilation，是一个卷积核里相邻采样点之间的平均距离，可以粗略看成感受野的大小。接着在 VOC 2007 测试集上跑有 3 层可变形卷积的 R-FCN，按卷积核中心落在哪种物体上分类统计：\n\n| 层 | 小物体 | 中物体 | 大物体 | 背景 |\n| --- | ---: | ---: | ---: | ---: |\n| res5c | 5.3 ± 3.3 | 5.8 ± 3.5 | 8.4 ± 4.5 | 6.2 ± 3.0 |\n| res5b | 2.5 ± 1.3 | 3.1 ± 1.5 | 5.1 ± 2.5 | 3.2 ± 1.2 |\n| res5a | 2.2 ± 1.2 | 2.9 ± 1.3 | 4.2 ± 1.6 | 3.1 ± 1.1 |\n\n*表：摘自原论文 Table 2，effective dilation 的平均值与标准差。物体按框的面积分成小、中、大三类，界线是 $96^2$ 和 $224^2$ 像素。*\n\n三层都看得到同样的趋势，物体越大，采样点散得越开。背景的数值介于中物体和大物体之间，作者认为辨认背景也需要比较大的感受野。\n\n### 要换几层\n\n| 可变形卷积的层数 | DeepLab（Cityscapes） | class-aware RPN | Faster R-CNN | R-FCN |\n| --- | ---: | ---: | ---: | ---: |\n| 0（基准） | 70.4 | 44.9 | 62.1 | 61.8 |\n| 1 | 73.5 | 54.4 | 63.8 | 63.0 |\n| 2 | 74.4 | 56.3 | 63.3 | 63.8 |\n| 3（默认） | **75.2** | 57.2 | 63.3 | 64.7 |\n| 6 | 75.1 | **57.7** | **64.0** | **65.4** |\n\n*表：摘自原论文 Table 1，ResNet-101。DeepLab 为 Cityscapes 的 mIoU，三种检测器为 VOC 2007 测试集的 mAP@0.7。*\n\n只换最后一层，DeepLab 就进步了 3 个百分点，class-aware RPN 进步将近 10 个百分点。这两个方法没有 RoI pooling，结果直接从卷积特征算出来，这可能是它们受益最多的原因。DeepLab 换到 3 层就饱和了，检测器则到 6 层还在进步，作者取 3 层作为各任务的折中。\n\n### 和空洞卷积比\n\n既然空洞卷积是特例，把 dilation 直接调大，是不是也有一样的效果？\n\n| 设置 | DeepLab（VOC / Cityscapes） | class-aware RPN | Faster R-CNN | R-FCN |\n| --- | ---: | ---: | ---: | ---: |\n| 空洞卷积 (2,2,2)，默认 | 69.7 / 70.4 | 68.0 / 44.9 | 78.1 / 62.1 | 80.0 / 61.8 |\n| 空洞卷积 (4,4,4) | 73.1 / 71.9 | 72.8 / 53.1 | 78.6 / 63.1 | 80.5 / 63.0 |\n| 空洞卷积 (6,6,6) | 73.6 / 72.7 | 73.6 / 55.2 | 78.5 / 62.3 | 80.2 / 63.5 |\n| 空洞卷积 (8,8,8) | 73.2 / 72.4 | 73.2 / 55.1 | 77.8 / 61.8 | 80.3 / 63.2 |\n| 可变形卷积 | **75.3 / 75.2** | **74.5 / 57.2** | 78.6 / 63.3 | 81.4 / 64.7 |\n| 可变形 RoI pooling | – | – | 78.3 / 66.6 | 81.2 / 65.0 |\n| 两者都用 | – | – | **79.3 / 66.9** | **82.6 / 68.5** |\n\n*表：摘自原论文 Table 3，ResNet-101。DeepLab 为 mIoU，检测器为 VOC 2007 测试集的 mAP@0.5 / mAP@0.7。括号内是最后三层 $3\\times3$ 卷积的 dilation。*\n\ndilation 调大，所有任务都变好，说明默认的感受野本来就太小。可是最好的 dilation 因任务而异，DeepLab 是 6，Faster R-CNN 是 4，没有一个值适合全部任务。可变形卷积在每个任务上都不输最好的那个 dilation，而且不需要逐个任务去调。\n\n可变形 RoI pooling 单独用，就让两个检测器的 mAP@0.7 进步不少，这个比较严格的指标要求框得更准。两个模块一起用，R-FCN 从 80.0 / 61.8 进步到 82.6 / 68.5。\n\n### 代价\n\n| 方法 | 参数 | 网络前向（秒） | 整体运行（秒） |\n| --- | ---: | ---: | ---: |\n| DeepLab（Cityscapes） | 46.0M | 0.610 | 0.650 |\n| 加可变形模块 | 46.1M | 0.656 | 0.696 |\n| Faster R-CNN | 58.3M | 0.147 | 0.190 |\n| 加可变形模块 | 59.9M | 0.192 | 0.234 |\n| R-FCN | 47.1M | 0.143 | 0.170 |\n| 加可变形模块 | 49.5M | 0.169 | 0.193 |\n\n*表：摘自原论文 Table 4 的三组，ResNet-101，Intel E5-2650 v2 CPU 与 NVIDIA K40 GPU。整体运行时间包含图像缩放与 NMS 等后处理。*\n\n参数最多只多了 2.4M，作者据此认为，进步来自建模几何变换的能力，而不是模型变大。时间上，DeepLab 只多了 7% 左右，Faster R-CNN 的前向时间则从 0.147 秒变成 0.192 秒，多了三成。\n\n另一个代价在部署。可变形卷积不是标准卷积，早期把 torchvision 的 `deform_conv2d` 导出成 ONNX，还得靠[第三方库](https://pypi.org/project/deform-conv2d-onnx-exporter/)把它拆成普通运算，速度也会受影响。\n\n### COCO\n\n最后在 COCO test-dev 上做完整比较，指标是 mAP@[0.5:0.95]：\n\n| 方法 | 骨干 | 普通 | 可变形 |\n| --- | --- | ---: | ---: |\n| class-aware RPN | ResNet-101 | 23.2 | 25.8 |\n| Faster R-CNN | ResNet-101 | 29.4 | 33.1 |\n| R-FCN | ResNet-101 | 30.8 | 34.5 |\n| Faster R-CNN | Aligned-Inception-ResNet | 30.8 | 34.1 |\n| R-FCN | Aligned-Inception-ResNet | 32.9 | 36.1 |\n| R-FCN，多尺度测试加框平均 | Aligned-Inception-ResNet | 35.5 | **37.5** |\n\n*表：摘自原论文 Table 5。*\n\n在 ResNet-101 上，三个方法分别相对进步了 11%、13% 和 12%。换成更强的骨干，或加上多尺度测试这些技巧，进步依然存在。按物体大小拆开，以绝对数字看，进步最多的是中、大物体，例如 Faster R-CNN 的大物体 mAP 从 47.1 到 51.2，小物体则从 9.0 到 11.6。\n\n## 结论\n\n可变形卷积为卷积的每个采样点加上一个学出来的偏移量。改动很小，参数只多几个百分点，感受野却能跟着物体的大小和形状变化，在分割和检测上都有稳定的进步，尤其是要求框得更准的 mAP@0.7。\n\n想在自己的网络里试，torchvision 的 `deform_conv2d` 加一个初始化为 0 的偏移卷积就行。部署就没这么轻松了，它不是标准卷积，推理框架不一定支持。采样点学会了凌波微步，负责部署的工程师可能也得跟着练。\n\n这个想法后来走了很远。v2 为采样点加上调制权重，InternImage 用 v3 做成大模型的骨干，[Deformable DETR](/blog/papers/object-detection/deformable-detr) 则把它搬到了注意力上。后面几篇我们还会再遇到它。\n","imageSizes":{"/content/papers/assets/45479d40b2beb8096d042f56871e08317e36ffc2be1857a325732d436b36da0f.png":{"width":988,"height":316},"/content/papers/assets/942dcd98b1119ce95e8a9a24b84ed42b780d062caafa004c66f75640c4e2584a.png":{"width":987,"height":633},"/content/papers/assets/e05ab32de27fdd8551794ded9ab71fafae0ea423979ef7250f9e7d31cedf1534.png":{"width":1000,"height":571}}}
