{"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-hant","path":"/content/papers/zh-hant/classic-cnns/deformable-convnets.c8fe7262b5e2.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-hant","path":"/content/papers/zh-hant/classic-cnns/stn.a90226bdb034.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-hant","path":"/content/papers/zh-hant/object-detection/deformable-detr.4eb78eef1d58.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\nResNet-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}}}
