{"notes":[{"id":"classic-cnns/cam","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"CAM","subtitle":"模型在看哪里？","author":"Z. Yuan","paperDate":"2015-12-14","publishedOn":"2026-01-05","world":"external/cam","paperTitle":"Learning Deep Features for Discriminative Localization","paperUrl":"https://arxiv.org/abs/1512.04150","bibliography":{"title":"Learning Deep Features for Discriminative Localization","authors":["Bolei Zhou","Aditya Khosla","Agata Lapedriza","Aude Oliva","Antonio Torralba"],"url":"https://arxiv.org/abs/1512.04150","pdfUrl":"https://arxiv.org/pdf/1512.04150v1","codeUrl":null,"preprint":{"id":"1512.04150","submittedOn":"2015-12-14","url":"https://arxiv.org/abs/1512.04150"},"publication":null,"citation":"@misc{classiccnnscam2015,\n  title = {{Learning Deep Features for Discriminative Localization}},\n  author = {Bolei Zhou and Aditya Khosla and Agata Lapedriza and Aude Oliva and Antonio Torralba},\n  year = {2015},\n  eprint = {1512.04150},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1512.04150}\n}\n","citationPath":"/content/papers/citations/classic-cnns/cam.2ce837236f9d.bib"},"thumbnail":{"src":"/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png","width":1372,"height":615},"locale":"zh-hans","path":"/content/papers/zh-hans/classic-cnns/cam.15140ed7296e.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":"去掉全连接层分类变差多少","text":"去掉全连接层，分类变差多少？","depth":3},{"id":"弱监督定位","text":"弱监督定位","depth":3},{"id":"换到其他任务","text":"换到其他任务","depth":3},{"id":"结论","text":"结论","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/nin","relation":"referenced","topic":""},{"id":"classic-cnns/zfnet","relation":"referenced","topic":""},{"id":"classic-cnns/resnet","relation":"referenced","topic":""}]},{"id":"classic-cnns/nin","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"NiN","subtitle":"一乘一的伏笔","author":"Z. Yuan","paperDate":"2013-12-16","publishedOn":"2025-09-24","world":"external/network-in-network","paperTitle":"Network In Network","paperUrl":"https://arxiv.org/abs/1312.4400","bibliography":{"title":"Network In Network","authors":["Min Lin","Qiang Chen","Shuicheng Yan"],"url":"https://arxiv.org/abs/1312.4400","pdfUrl":"https://arxiv.org/pdf/1312.4400v3","codeUrl":null,"preprint":{"id":"1312.4400","submittedOn":"2013-12-16","url":"https://arxiv.org/abs/1312.4400"},"publication":null,"citation":"@misc{classiccnnsnin2013,\n  title = {{Network In Network}},\n  author = {Min Lin and Qiang Chen and Shuicheng Yan},\n  year = {2013},\n  eprint = {1312.4400},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1312.4400}\n}\n","citationPath":"/content/papers/citations/classic-cnns/nin.db58639ac47c.bib"},"thumbnail":{"src":"/content/papers/assets/8024fc245eb852a2c0cd34b59c18c680b92766c33d621b934dbf102c1cacda13.png","width":896,"height":330},"locale":"zh-hans","path":"/content/papers/zh-hans/classic-cnns/nin.317b38a6f564.md","headings":[{"id":"定义问题","text":"定义问题","depth":2},{"id":"解决问题","text":"解决问题","depth":2},{"id":"在每个区域上跑一个小-mlp","text":"在每个区域上跑一个小 MLP","depth":3},{"id":"把全连接层换成平均","text":"把全连接层换成平均","depth":3},{"id":"整个网络","text":"整个网络","depth":3},{"id":"讨论","text":"讨论","depth":2},{"id":"cifar-10-与-cifar-100","text":"CIFAR-10 与 CIFAR-100","depth":3},{"id":"svhn-与-mnist","text":"SVHN 与 MNIST","depth":3},{"id":"全局平均池化真的有正则化效果吗","text":"全局平均池化真的有正则化效果吗？","depth":3},{"id":"最后一层真的变成置信度图了吗","text":"最后一层真的变成置信度图了吗？","depth":3},{"id":"结论","text":"结论","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/inception-v1","relation":"referenced","topic":""},{"id":"classic-cnns/resnet","relation":"referenced","topic":""},{"id":"classic-cnns/cam","relation":"referenced","topic":""}]},{"id":"classic-cnns/zfnet","category":"classic-cnns","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"ZFNet","subtitle":"黑盒里的风景","author":"Z. Yuan","paperDate":"2013-11-12","publishedOn":"2025-09-06","world":"external/zfnet","paperTitle":"Visualizing and Understanding Convolutional Networks","paperUrl":"https://arxiv.org/abs/1311.2901","bibliography":{"title":"Visualizing and Understanding Convolutional Networks","authors":["Matthew D Zeiler","Rob Fergus"],"url":"https://arxiv.org/abs/1311.2901","pdfUrl":"https://arxiv.org/pdf/1311.2901v3","codeUrl":null,"preprint":{"id":"1311.2901","submittedOn":"2013-11-12","url":"https://arxiv.org/abs/1311.2901"},"publication":null,"citation":"@misc{classiccnnszfnet2013,\n  title = {{Visualizing and Understanding Convolutional Networks}},\n  author = {Matthew D Zeiler and Rob Fergus},\n  year = {2013},\n  eprint = {1311.2901},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1311.2901}\n}\n","citationPath":"/content/papers/citations/classic-cnns/zfnet.6d4dd35e9f91.bib"},"thumbnail":{"src":"/content/papers/assets/6c45d745e8a2f4b60cc45bea1029353e303d5e3bbee1e62f438c71874fddf883.png","width":1200,"height":1329},"locale":"zh-hans","path":"/content/papers/zh-hans/classic-cnns/zfnet.d3507051f65f.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":3},{"id":"不同图像之间的对应","text":"不同图像之间的对应","depth":3},{"id":"讨论","text":"讨论","depth":2},{"id":"imagenet-2012","text":"ImageNet 2012","depth":3},{"id":"拆掉哪几层影响最大","text":"拆掉哪几层影响最大？","depth":3},{"id":"换一个数据集","text":"换一个数据集","depth":3},{"id":"越高层越好用吗","text":"越高层越好用吗？","depth":3},{"id":"结论","text":"结论","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/alexnet","relation":"referenced","topic":""},{"id":"classic-cnns/vgg","relation":"referenced","topic":""},{"id":"classic-cnns/resnet","relation":"referenced","topic":""}]},{"id":"classic-cnns/resnet","category":"classic-cnns","legacyPath":"classic-cnns/1512-resnet","legacySlug":"1512-resnet","legacyPaths":[],"title":"ResNet","subtitle":"卷积网络 100 层","author":"Z. Yuan","paperDate":"2015-12-10","publishedOn":"2024-08-08","paperTitle":"Deep Residual Learning for Image Recognition","paperUrl":"https://arxiv.org/abs/1512.03385","bibliography":{"title":"Deep Residual Learning for Image Recognition","authors":["Kaiming He","Xiangyu Zhang","Shaoqing Ren","Jian Sun"],"url":"https://arxiv.org/abs/1512.03385","pdfUrl":"https://arxiv.org/pdf/1512.03385","codeUrl":null,"preprint":{"id":"1512.03385","submittedOn":"2015-12-10","url":"https://arxiv.org/abs/1512.03385"},"publication":{"venue":"CVPR","year":2016,"kind":"conference","url":"https://doi.org/10.1109/cvpr.2016.90"},"citation":"@misc{classiccnnsresnet2015,\n  title = {{Deep Residual Learning for Image Recognition}},\n  author = {Kaiming He and Xiangyu Zhang and Shaoqing Ren and Jian Sun},\n  year = {2015},\n  eprint = {1512.03385},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1512.03385}\n}\n","citationPath":"/content/papers/citations/classic-cnns/resnet.cf4014e8baae.bib"},"thumbnail":{"src":"/content/papers/assets/b8fe87fe757249ea6d7bb667fef3196754b9f80e28bf842a69b540ca94585d08.jpg","width":1224,"height":408},"locale":"zh-hans","path":"/content/papers/zh-hans/classic-cnns/resnet.98d0474f71e6.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":"resnet-18-vs-resnet-34","text":"ResNet-18 vs ResNet-34","depth":3},{"id":"更深的瓶颈架构","text":"更深的瓶颈架构","depth":3},{"id":"在-imagenet-上的表现","text":"在 ImageNet 上的表现","depth":3},{"id":"结论","text":"结论","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"classic-cnns/vgg","relation":"background","topic":"如何训练更深的卷积网络"},{"id":"classic-cnns/densenet","relation":"next","topic":"如何训练更深的卷积网络"}]}],"text":"一个只见过类别标签的分类网络，知道物体在哪里吗？\n\nMIT 的 Bolei Zhou 等人在前一年的研究中发现，答案多半是肯定的：用场景分类训练出来的卷积网络，各层卷积单元的行为很像目标检测器，尽管训练时没有人告诉它物体在哪里。可是这种能力到了全连接层就消失了。卷积层的输出还保留着空间位置，展平送进全连接层之后，「在哪里」和「是什么」就混在一起，分不开了。\n\n2015 年 12 月，同一组人（Bolei Zhou、Aditya Khosla、Agata Lapedriza、Aude Oliva、Antonio Torralba）发布了这篇论文，后来发表在 CVPR 2016。他们把目光投向一个已经存在的零件：NiN 提出、GoogLeNet 也用过的全局平均池化。\n\n\n> [!TIP]\n> \n> 全局平均池化的来历，以及 NiN 最后一层特征图的样子，可以先看我们的笔记：\n> \n> - **[[13.12] NiN：一乘一的伏笔](/blog/papers/classic-cnns/nin)**\n\n\n## 定义问题\n\n这篇论文处理的是**弱监督目标定位**：训练时只有图像级的类别标签，测试时却要框出物体的位置。\n\n当时已经有几种做法。有的把图像一块一块地遮住，看遮掉哪里时响应下降得最多；有的把多示例学习和卷积特征结合起来；有的在大量重叠的小区域上各跑一次网络。这些方法都不是端到端训练的，而且要跑很多次前向传播，很难用在大规模数据上。\n\n最接近的是 Oquab 等人的方法：用全局**最大**池化来定位物体。它只能找到物体边界上的一个点，说不出物体的范围。\n\n另一条相关的研究线是可视化卷积网络。[ZFNet](/blog/papers/classic-cnns/zfnet) 用反卷积看每个单元在找什么样的模式，Zhou 等人自己的前作则发现场景网络里存在目标检测器；这些研究都只分析卷积层，跳过了全连接层。\n\n作者想做的是：网络只要跑一次前向传播，就能说出它是根据图像的哪些区域判断出这个类别的。\n\n## 解决问题\n\n### 把分数拆回空间\n\n作者用的网络结构和 NiN、GoogLeNet 类似：前面大多是卷积层，最后一层卷积之后做全局平均池化，接一层全连接，再接 softmax。\n\n设 $f_k(x, y)$ 是最后一层卷积第 $k$ 个单元在位置 $(x, y)$ 的激活。全局平均池化把每张特征图压成一个数：\n\n$$\nF_k = \\sum_{x,y} f_k(x, y)\n$$\n\n论文这里直接写成求和，省略了除以位置数；差一个常数，不影响后面的推导。\n\n类别 $c$ 的分数，是这些数的加权和：\n\n$$\nS_c = \\sum_k w_k^c F_k\n$$\n\n$w_k^c$ 是全连接层的权重，代表第 $k$ 张特征图对类别 $c$ 有多重要。作者把 softmax 的偏置设为 0，说它对分类几乎没有影响。\n\n接下来是整篇论文的关键。把 $F_k$ 代进去，交换两个求和的顺序：\n\n$$\nS_c = \\sum_k w_k^c \\sum_{x,y} f_k(x, y) = \\sum_{x,y} \\sum_k w_k^c f_k(x, y)\n$$\n\n内层的 $\\sum_k w_k^c f_k(x, y)$ 还带着位置 $(x, y)$。作者把它定义为类别 $c$ 的**类别激活图**（class activation map，CAM）：\n\n$$\nM_c(x, y) = \\sum_k w_k^c f_k(x, y)\n$$\n\n于是 $S_c = \\sum_{x,y} M_c(x, y)$。类别分数就是 CAM 上所有位置的总和，$M_c(x, y)$ 直接说明了位置 $(x, y)$ 对这个分数贡献了多少。\n\n换句话说，分类时是先平均、再加权；CAM 把顺序倒过来，先加权、保留空间不做平均。我们用一个小例子验证：\n\n```python\nimport torch\n\ntorch.manual_seed(0)\nK, C = 4, 3                                   # 4 张特征图、3 个类别\nf = torch.relu(torch.randn(K, 7, 7))          # 最后一层卷积的输出：4 × 7 × 7\nw = torch.randn(C, K)                         # GAP 后面的全连接层：每个类别一组 4 个权重\n\n# 分类：先平均，再加权\nscores = w @ f.mean(dim=(1, 2))               # [3]\n\n# CAM：先加权，保留空间\ncams = torch.einsum('ck,khw->chw', w, f)      # [3, 7, 7]\n\nprint(\"分类分数：      \", scores)\nprint(\"每张 CAM 的平均：\", cams.mean(dim=(1, 2)))\nprint(cams.shape)\n```\n\n```text\n分类分数：       tensor([-1.0399, -0.8521, -0.5210])\n每张 CAM 的平均： tensor([-1.0399, -0.8521, -0.5210])\ntorch.Size([3, 7, 7])\n```\n\n两组数字完全一样。CAM 没有用到任何新的参数，也不需要再跑一次网络，只是把已经学好的全连接权重，「投影」回最后一层卷积的特征图上。\n\n[![上半部分是网络：一张男孩抱着狗的照片，经过一连串卷积层，最后一层卷积的多张特征图经过 GAP，以权重 w1、w2 到 wn 连接到 Australian terrier 这个类别。下半部分标题是 Class Activation Mapping：w1、w2、wn 分别乘上对应的特征图再相加，得到一张叠在原图上的热力图，最亮的位置在狗身上](/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png)](/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png)\n\n*原论文 Figure 2，取自 [**v1 PDF 第 3 页**](https://arxiv.org/pdf/1512.04150v1#page=3)。预测的类别分数被映射回前一层卷积，生成类别激活图。*\n\n图的下半部分就是 $M_c$ 的计算。每张特征图各自检测某种视觉模式：有的亮在狗的头部，有的亮在下方的身体；乘上「Australian terrier」这个类别的权重再加起来，就是这个类别的热力图。最后一层卷积的分辨率很低（论文里是 $13\\times13$ 或 $14\\times14$），把 CAM 放大到输入图像的大小，就能看出图像上哪些区域和这个类别最相关。\n\n同一张图像，换一个类别，就换一组权重，CAM 也跟着不同：\n\n[![左上角是一张夜间圆顶建筑的照片，标题为 dome。其余五格是预测前五名类别的 CAM 与分数：palace 0.459 亮在下方横向的宫殿主体；dome 0.195 亮在上方的圆顶；church 0.146、altar 0.091、monastery 0.051 的亮点集中在建筑的左下角与尖塔一带](/content/papers/assets/5feae5d21639fe423e9457b6eee11d7c4357e0fab4014dac2dd2482a810f0c45.png)](/content/papers/assets/5feae5d21639fe423e9457b6eee11d7c4357e0fab4014dac2dd2482a810f0c45.png)\n\n*原论文 Figure 4，取自 [**v1 PDF 第 4 页**](https://arxiv.org/pdf/1512.04150v1#page=4)。真实类别是 dome（圆顶），其余五格是预测前五名的 CAM，每格上方是类别与分数。*\n\npalace（宫殿）亮在下方平整的建筑主体，dome 亮在上方的圆顶。网络判断这两个类别时，看的是同一张图的不同地方。\n\n### 平均和最大，差在哪里\n\n既然 Oquab 等人用的是全局最大池化，作者特别解释了为什么要用平均。\n\n他们的直觉是：取平均时，要让分数变高，最好的办法是找出物体**所有**具有辨识度的部分，因为任何一处低激活都会拉低平均值；取最大值时，只有最强的那个位置算数，其他位置的高低都没有影响。\n\n这个区别在反向传播时看得最清楚：\n\n```python\nimport torch\n\ntorch.manual_seed(0)\nx = torch.rand(1, 1, 4, 4, requires_grad=True)\n\nx.mean().backward()                           # 全局平均池化\nprint(\"GAP 的梯度：\\n\", x.grad[0, 0])\n\nx.grad = None\nx.amax().backward()                           # 全局最大池化\nprint(\"GMP 的梯度：\\n\", x.grad[0, 0])\n```\n\n```text\nGAP 的梯度：\n tensor([[0.0625, 0.0625, 0.0625, 0.0625],\n        [0.0625, 0.0625, 0.0625, 0.0625],\n        [0.0625, 0.0625, 0.0625, 0.0625],\n        [0.0625, 0.0625, 0.0625, 0.0625]])\nGMP 的梯度：\n tensor([[0., 0., 0., 0.],\n        [0., 0., 0., 1.],\n        [0., 0., 0., 0.],\n        [0., 0., 0., 0.]])\n```\n\n平均池化把梯度平均分给 16 个位置，每个位置都会被训练；最大池化只把梯度传给最大的那一格，其他 15 格在这一步什么都学不到。后面的实验会检验这个直觉在定位上是否成立。\n\n### 让最后一层的分辨率高一点\n\n作者拿 AlexNet、VGGnet、GoogLeNet 三个网络来改：去掉最后的全连接层，换成 GAP，再接一层全连接的 softmax。\n\n他们发现，GAP 前最后一层卷积的空间分辨率越高，定位能力越好。作者把这个分辨率称为 mapping resolution。为了提高它，他们还多删了几层：\n\n- **AlexNet**：删掉 conv5 之后的所有层，得到 $13\\times13$。\n- **VGGnet**：删掉 conv5-3 之后的所有层，得到 $14\\times14$。\n- **GoogLeNet**：删掉 inception4e 之后的所有层，得到 $14\\times14$。\n\n每个网络再加一层 $3\\times3$、步幅 1、补零 1、1024 个单元的卷积，接 GAP 与 softmax，然后在 ILSVRC 的 130 万张训练图像上微调 1000 类分类。得到的网络分别叫 AlexNet-GAP、VGGnet-GAP、GoogLeNet-GAP。作者在脚注中提到，从头训练的结果也差不多。\n\n### 从热力图到框\n\nILSVRC 的定位评估要的是一个框。作者用的方法很简单：\n\n1. 把 CAM 放大到图像大小。\n2. 留下数值超过最大值 20% 的区域。\n3. 取最大的那块连通区域，画出它的外接框。\n\n我们拿一张假的 $14\\times14$ CAM 走一遍。左边有一块大的响应，右上角有一块小的：\n\n```python\nimport torch\nimport torch.nn.functional as F\nfrom scipy import ndimage\n\n# 一张 14×14 的 CAM：左边一块大的响应，右上角一块小的\nyy, xx = torch.meshgrid(torch.arange(14.), torch.arange(14.), indexing='ij')\ncam = 1.0 * torch.exp(-((yy - 8) ** 2 / 6 + (xx - 4) ** 2 / 4)) \\\n    + 0.6 * torch.exp(-((yy - 2) ** 2 + (xx - 11) ** 2) / 1.5)\n\n# 1. 放大回输入图像的大小\ncam = F.interpolate(cam[None, None], size=(224, 224), mode='bilinear', align_corners=False)[0, 0]\n\n# 2. 留下超过最大值 20% 的区域\nmask = (cam > 0.2 * cam.max()).numpy()\n\n# 3. 找最大的连通区域，取外接框\nlabels, n = ndimage.label(mask)\nsizes = ndimage.sum(mask, labels, range(1, n + 1))\nlargest = labels == (sizes.argmax() + 1)\nrows, cols = largest.any(axis=1).nonzero()[0], largest.any(axis=0).nonzero()[0]\n\nprint(\"连通区域数：\", n, \"，各自的像素数：\", sizes.astype(int).tolist())\nprint(\"框（x0, y0, x1, y1）：\", cols[0], rows[0], cols[-1], rows[-1])\n```\n\n```text\n连通区域数： 2 ，各自的像素数： [1344, 6552]\n框（x0, y0, x1, y1）： 30 85 113 186\n```\n\n右上角那块也超过了阈值，但面积小，最后的框只围住左边的大区域。这也看得出这个方法的局限：同一张图里有两个物体时，只会框出其中一个。top-5 的定位评估，则是对预测前五名的类别各做一次。\n\n## 讨论\n\n### 去掉全连接层，分类变差多少？\n\n| 网络 | Top-1 错误率 | Top-5 错误率 |\n| --- | ---: | ---: |\n| AlexNet | 42.6 | 19.5 |\n| AlexNet-GAP | 51.1 | 26.3 |\n| AlexNet\\*-GAP | 44.9 | 20.9 |\n| VGGnet | 31.2 | 11.4 |\n| VGGnet-GAP | 33.4 | 12.2 |\n| GoogLeNet | 31.9 | 11.3 |\n| GoogLeNet-GAP | 35.0 | 13.2 |\n| GoogLeNet-GMP | 35.6 | 13.9 |\n| NIN | 41.9 | 19.6 |\n\n*表：摘自原论文 Table 1，ILSVRC 验证集，单位为百分比；行的顺序重新排过，方便和原本的网络对照。*\n\nVGGnet 和 GoogLeNet 删掉几层之后，top-1 错误率分别多了 2.2 和 3.1 个百分点，top-5 都多了不到 2 个百分点。AlexNet 受影响最大，top-1 从 42.6% 掉到 51.1%；作者在 GAP 前多加两层卷积，得到的 AlexNet\\*-GAP 就和原本的 AlexNet 差不多了。GAP 和 GMP 在分类上的表现也相近，这和预期一致。\n\n分类不能差太多，因为定位的评估要求类别和框都对。\n\n### 弱监督定位\n\n| 方法 | Top-1 错误率 | Top-5 错误率 |\n| --- | ---: | ---: |\n| **GoogLeNet-GAP** | **56.40** | **43.00** |\n| VGGnet-GAP | 57.20 | 45.14 |\n| GoogLeNet-GMP | 57.78 | 45.26 |\n| GoogLeNet | 60.09 | 49.34 |\n| AlexNet\\*-GAP | 63.75 | 49.53 |\n| Backprop on GoogLeNet | 61.31 | 50.55 |\n| Backprop on VGGnet | 61.12 | 51.46 |\n| AlexNet-GAP | 67.19 | 52.16 |\n| Backprop on AlexNet | 65.17 | 52.64 |\n| NIN | 65.47 | 54.19 |\n\n*表：摘自原论文 Table 2，ILSVRC 验证集的定位错误率，按 top-5 排序。Backprop 指用 Simonyan 等人的反向传播显著图取代 CAM。*\n\n几组比较：\n\n- **CAM 对比反向传播**：在 GoogLeNet 和 VGGnet 上，CAM 的 top-5 错误率比反向传播低了 6 个百分点以上；在 AlexNet 上两者接近（52.16 对 52.64），top-1 反而是反向传播好一点。\n- **GoogLeNet-GAP 对比 GoogLeNet**：原本的 GoogLeNet 分类更好，定位却输给了删过层的 GoogLeNet-GAP。作者认为原因是原本的 GoogLeNet 最后一层只有 $7\\times7$，分辨率太低。\n- **GAP 对比 GMP**：分类差不多，定位上 GAP 的 top-5 错误率低了 2.26 个百分点，支持了前面关于平均池化的直觉。\n\n[![左右两组。a) 四个例子，各有一张原图与一张 CAM：美洲短吻鳄、迷宫、喜乐蒂牧羊犬、蚱蜢；原图上绿色是标注的框，红色是从 CAM 推出的框，两者大致重叠。b) 上排两个例子是 GoogLeNet-GAP 对雪地摩托车与围裙的定位，热力图集中在物体上；下排两个例子是 AlexNet 反向传播的结果，热力图是零散的点，推出的框偏大](/content/papers/assets/dce42d560109e416d7229359699d2bca63f7ec9a74374b2d62fce387c9bbed22.png)](/content/papers/assets/dce42d560109e416d7229359699d2bca63f7ec9a74374b2d62fce387c9bbed22.png)\n\n*原论文 Figure 6，取自 [**v1 PDF 第 6 页**](https://arxiv.org/pdf/1512.04150v1#page=6)。a) GoogLeNet-GAP 的定位示例；b) 上排两例是 GoogLeNet-GAP，下排两例是 AlexNet 的反向传播。绿框是标注，红框是从热力图推出的框。*\n\n右边的对照很直观：反向传播的显著图是一片分散的亮点，用阈值切下去之后框得太大；CAM 的热区集中，框也更贴近物体。\n\n在测试集上，作者换了一种挑框的策略：前两名的类别各取一个紧的框和一个松的框，第三名取一个松的框；这个启发式做法是在验证集上调出来的。\n\n| 方法 | 监督方式 | Top-5 测试错误率 |\n| --- | --- | ---: |\n| GoogLeNet-GAP（启发式挑框） | 弱监督 | **37.1** |\n| GoogLeNet-GAP | 弱监督 | 42.9 |\n| Backprop | 弱监督 | 46.4 |\n| GoogLeNet | 全监督 | 26.7 |\n| OverFeat | 全监督 | 29.9 |\n| AlexNet | 全监督 | 34.2 |\n\n*表：摘自原论文 Table 3，ILSVRC 测试集。*\n\n37.1% 已经接近全监督的 AlexNet（34.2%），而 GoogLeNet-GAP 训练时没有见过任何一个框。作者也承认差距还很大：同样的 GoogLeNet 架构，全监督可以做到 26.7%。\n\n### 换到其他任务\n\n作者还想知道，这种能定位的特征能不能直接用在别的数据集上。做法是固定 GoogLeNet-GAP，在 GAP 的输出上训练一个线性 SVM，再用 SVM 的权重取代原本的 softmax 权重来计算 CAM。\n\n作为一般的图像特征，GoogLeNet-GAP 在 SUN397、MIT Indoor67、Caltech256 等八个数据集上，和原本 GoogLeNet 平均池化层的特征表现相近，大多明显优于 AlexNet 的 fc7。\n\n细粒度分类的 CUB-200-2011 鸟类数据集更有意思。它有 200 种鸟、5,994 张训练图像、5,794 张测试图像，还附带了鸟的框。\n\n| 方法 | 训练／测试用到的标注 | 准确率 |\n| --- | --- | ---: |\n| GoogLeNet-GAP，整张图像 | 无 | 63.0% |\n| GoogLeNet-GAP，CAM 推出的框 | 无 | 67.8% |\n| GoogLeNet-GAP，标注的框 | 框 | 70.5% |\n| PANDA R-CNN | 框＋部位 | 76.4% |\n\n*表：摘自原论文 Table 4 的部分行。*\n\n先用 CAM 把鸟框出来，再从框里重新提取特征，准确率从 63.0% 升到 67.8%，没有用到任何标注。CAM 推出的框，在 IoU 0.5 的标准下有 41.0% 是对的；随机猜的概率是 5.5%。细粒度分类的差别常常只在某些细节上，先把注意力集中到鸟身上，帮助很明显。\n\n最后一组实验是「找模式」：给一组有共同概念的图像，看 CAM 会不会亮在那个概念上。其中一个例子和文字检测有关：\n\n[![上排六张街景店面照片，下排是对应的 CAM。热区都落在店铺招牌的文字上，例如遮阳棚上的店名、门口上方的招牌](/content/papers/assets/89c6a4410389b611bdcc7e40a414223d52106f89494e17ede86188b4552d73a0.png)](/content/papers/assets/89c6a4410389b611bdcc7e40a414223d52106f89494e17ede86188b4552d73a0.png)\n\n*原论文 Figure 11，取自 [**v1 PDF 第 8 页**](https://arxiv.org/pdf/1512.04150v1#page=8)。弱监督的文字检测器：网络没有用文字或任何框训练，CAM 仍然亮在文字上。*\n\n正样本是 SVT 数据集中 350 张有文字的 Google 街景图像，负样本是从 SUN 数据集里随机抽取的户外场景。只靠「这张图有没有字」的二元标签，CAM 就落在了招牌的文字上。论文另外也试了场景中关键物体的定位、短语概念的定位，以及解释视觉问答模型的答案。\n\n作者还做了一件事：用 GAP 后面的权重，把最后一层卷积的单元按照对某个类别的重要性排序，直接看出哪些单元在判断这个类别时最重要。例如对 lakeland terrier 这种梗犬重要的单元，检测的是狗脸和身上的毛；对客厅重要的，是沙发、桌子和壁炉。\n\n## 结论\n\nCAM 本身只有一行公式：把分类层的权重乘回最后一层特征图。它之所以成立，是因为 GAP 之后只接一层线性层，类别分数可以拆回到每个位置。这也是它的前提：网络得是「卷积、GAP、一层全连接」的结构，其他网络要先改架构、重新微调，分类准确率可能因此下降。\n\n实验上，CAM 在 ILSVRC 的弱监督定位上得到 37.1% 的 top-5 错误率，和全监督的 AlexNet 相近，但离同架构的全监督 GoogLeNet 还有一段距离。一张图只取最大连通区域的框，也让它更适合一张图里只有一个主要物体的情况。\n\nCAM 对架构的限制，很快就有人处理了：2016 年 10 月的 [Grad-CAM](https://arxiv.org/abs/1610.02391) 用梯度来决定每张特征图的权重，不必改成 GAP 架构，也不用重新训练。\n\nCAM 的门槛很低：只要网络末端是 GAP 接一层全连接，几行代码就能算出来。现在常见的 [ResNet](/blog/papers/classic-cnns/resnet) 恰好就是这种结构，想知道模型在看哪里，随手就能画一张。\n\n不过别把热力图看得太认真。它标出的是最有辨识度的部位，比如动物的头，常常只覆盖物体的一部分。\n","imageSizes":{"/content/papers/assets/c3c6af7f1943634784331ca03a55efc3e68a7714d936d953a14f11b0828d9cf8.png":{"width":1372,"height":615},"/content/papers/assets/5feae5d21639fe423e9457b6eee11d7c4357e0fab4014dac2dd2482a810f0c45.png":{"width":672,"height":462},"/content/papers/assets/dce42d560109e416d7229359699d2bca63f7ec9a74374b2d62fce387c9bbed22.png":{"width":1381,"height":304},"/content/papers/assets/89c6a4410389b611bdcc7e40a414223d52106f89494e17ede86188b4552d73a0.png":{"width":669,"height":235}}}
