今年 9 月,reddit 网友 Michael Klachko 对 CVPR 2018 的一篇接收论文《Perturbative Neural Networks》提出质疑,他在对论文的复现过程中发现了一些问题:「《Perturbative Neural Networks》一文提出用 1x1 卷积代替 3x3 卷积,输入中应用了一些噪声。作者称这种做法表现良好,但我的复现结果并不理想,因此我决定对其进行测试。作者提供了他们用的代码,但是经过仔细检查,我发现他们的测试准确率计算有误,导致得出的所有结果无效。」
在收到反馈后,论文作者做出了积极回应,称他们「计划透彻分析下这篇文章,在十分确定之后给出进一步的回应。可视化器中光滑函数的默认配置是疏忽点,我们进行了修改。」
今天,《Perturbative Neural Networks》论文作者给出了修改后的结果,他调查了 Michael Klachko 的实现过程,并在 GitHub 上给出了分析结果:(1)Michael 声称的性能下降(约 5%)主要原因在于其在 PNN 实现过程与论文作者给出的代码存在多处不一致,以及选择的超参数并非最优。(2)PNN 方法在实践中是有效的。
- 原论文链接:https://arxiv.org/abs/1806.01817
- Michael Klachko 的测试代码:https://github.com/michaelklachko/pnn.pytorch
- 原始 PNN GitHub repo:https://github.com/juefeix/pnn.pytorch
- 更新版 PNN GitHub repo:https://github.com/juefeix/pnn.pytorch.update
以下是论文作者在 GitHub 中给出的具体更新内容:
该 repo 包含新的 PNN 代码,以及对最近 reddit 讨论中所提出问题的回应。这份代码基于Michael Klachko 的测试代码,对 model.py 和 main.py 做了轻微修改。所有修改都做了标注。
针对此次 Reddit 事件的回应
第一部分:Michael Klachko 实现中的不一致
根据我们的分析,Michael 帖子中提到的性能下降(约 5%)主要是由于 Michael Klachko 的 PNN 实现的多处不一致以及超参数的次优选择。
由于 PNN 实现以及超参数的选择都不是最优的,MK 的实现在 CIFAR-10 上得到的最佳性能约为 85-86%,比论文中的结果下降了 5%。
Michael Klachko 测试中的截图。
将 MK 的实现与我们的实现进行比较之后,我们发现二者存在以下不同之处:
- 优化方法不同:MK 用的是 SGD,我们的实现用的是 Adam。
- 加性噪声(additive noise)的级别不同:MK 用的是 0.5,我们用的是 0.1。
- 学习率不同:MK 用的是 1e-3,我们用的是 1e-4。
- 学习率调度不同。
- Conv-BN-ReLU 模块顺序不同。
- 使用的 dropout 不同,MK 用的是 0.5,我们没有使用。
上述不一致之处见下图。左边为 MK 的实现,右边为我们的实现。
基于有限次数的尝试,我们发现前两个不一致(优化方法和噪声级别)对 PNN 性能的负面影响最大。优化方法的选择确实非常重要,在小规模实验中,每种优化方法(SGD、Adam、RMSProp 等)会导致差异巨大的优化路径。加性噪声级别的选择也非常重要,我们将在第三部分进行讨论。
让我们来看一下设置了正确的超参数之后 PNN 表现如何。保持噪声掩码不变(--nfilters 128),我们可以得到 90.35% 的准确率,而不是 MK 得到的~85-86% 的准确率。
e 1e-4 --first_filter_size 3 --level 0.1 --optim-method Adam --nepochs 450
第二部分:关于 CVPR 论文的结果
截至目前,对这篇 CPVR 论文《Perturbative Neural Networks》实验的重新评估大多已完成。有少数实验受到光滑函数中错误默认配置的影响。这些受影响的实验性能都略有下降,不过可以通过增加网络参数(如噪声掩码的数量)来弥补。我们将更新 arXiv 版本中的结果。
原始的 CPVR 论文不包括以下内容。
第三部分:在所有层中应用均匀加性噪声
接下来要讨论的内容没有涵盖在原始 CVPR 论文中,将在 PNN 后续研究中进一步探讨。其中一个主题是在所有层应用扰动噪声,包括第一层。
在 CVPR 版的 PNN 论文中,第一层使用了 3x3 或 7x7 的空间卷积作为特征提取,所有后续层使用扰动噪声模块,这一点也可以从原始 PNN GitHub repo 中看到。由于 MK 尝试并实现的是在 PNN 的所有层应用扰动噪声,因此提供我们的见解可能会有帮助。
根据 MK 的测试(截图如下所示),在所有层(包括第一层)应用均匀噪声的 PNN 在 CIFAR-10 上达到了 72.6% 的准确率。在此我们提供了一个简单的解决方案(没有对 MK 的实现做很大的改动),使准确率达到约 85-86%。我们想说明的一点是,这仍然是关于 PNN 的很多正在进行的研究主题之一,我们将在后续工作中报告研究结果。
我们从 class PerturbLayer(nn.Module) 创建了一个名为 class PerturbLayerFirst(nn.Module) 的重复类(duplicate class),以区别对待第一层中的噪声模块。大多数改动都发生在 class PerturbLayerFirst(nn.Module) 和 class PerturbResNet(nn.Module) 内。
关于该修改的主要想法是:
- 我们需要更多的噪声掩码。使用三个高度相关(RGB 通道)的基本图像来创建 128 或 256 噪声扰动的响应图是不够的。解决方案参见:
https://github.com/juefeix/pnn.pytorch.update/blob/f1fc626107aa43347875a95c4fae2d24700fa489/models.py#L248 - 噪声等级的选择是次优的,需要为第一层放大。在 MK 的实现中,第一层输入和后续层经历了不同的归一化,且动态范围也非常不同。解决方案如下:
https://github.com/juefeix/pnn.pytorch.update/blob/f1fc626107aa43347875a95c4fae2d24700fa489/models.py#L248
因此,改动之后,带有所有层噪声扰动模块的 PNN 达到了 85.92% 的准确率,而不是 MK 报告中的 72.6%。通过用最优方式控制不同层的噪声等级,我们能够获得更高的准确率。更多细节将在进一步分析后提供。
python main.py --net-type 'perturb_resnet18' --dataset-test 'CIFAR10' --dataset-train 'CIFAR10' --nfilters 256 --batch-size 20 --learning-rate 1e-4 --first_filter_size 0 --filter_size 0 --nmasks 1 --level 0.1 --optim-method Adam --nepochs 450
为什么噪声等级如此重要?
正如我在 PNN 论文中所讨论的,我们获得了噪声等级和卷积滤波器大小之间的关系,即当计算局部卷积运算时要考虑多少邻域(neighbor)?在 PNN 中,可以把加性噪声视为映射函数的参数,该映射函数将输入图上的一个像素映射到输出响应图的对应像素上。正如传统卷积,在每个局部 patch 中,卷积运算通过中心像素的所有邻域与滤波器权重的点积将中心像素映射到响应图中的对应像素上。
以上两种映射函数都可以看做特征提取方法,或者选取度量的方式。如果噪声等级太低,我们就没有选取到有效的输入度量。而如果噪声等级太高,噪声就会掩盖信号,输入中有意义的信息就会受到损失。因此,选择合适的噪声对于 PNN 的性能有很大影响。
在 PNN 的早期研究中,我们仍然按照经验决定噪声等级。
第四部分:为什么 PNN 奏效?
在论文《Local Binary Convolutional Neural Networks》(LBCNN)中,我们尝试回答这个问题:我们真的需要可学习的空间卷积吗?结果表明,我们不需要。使用二值或高斯滤波器加上可学习的通道池化的不可学习随机卷积同样表现很好。据此,下一个自然要问的问题就是:我们真的需要空间卷积吗?也许另一种特征提取技术(和加性噪声一样简单)加上可学习的通道池化也能表现很好?这正是《Perturbative Neural Networks》论文尝试探索的问题。
实际上,该 repo 中运行的第一个脚本已经使用了 PNN 中第一层的不可学习随机卷积。请查看 model.py 的第 513 行。读者可以对那一行进行注释,然后它会返回第一层的通常的可学习卷积。它们实现的准确率是相同的。
可学习通道池化和不可学习卷积滤波器之间的混合让我们重新思考深度 CNN 模型中卷积滤波器的作用。通过多种视觉分类任务,我观察到 LBCNN 和 CNN 可以实现相当的性能。LBCNN 的灵活性或许意味着,我们可以在没有可学习卷积滤波器的条件下完成同样的任务,具备不变滤波器的随机卷积加上可学习通道池化在学习有效的图像表征上已经完全足够。
基于这些观察结果,很自然地,进一步的研究方向之一是完全取代随机卷积运算。在每个局部 patch 中,由于线性操作涉及中心像素的所有邻域和一组随机滤波器权重的点积来产生标量输出,该输出以某种方式携带局部信息(即在响应图中将中心像素映射到相应的输出像素)。或许最简单的替代性线性操作就是加性随机噪声了。这也是 PNN 后续工作的动机,其中我引入了一种非常简单但有效的模块——扰动层(perturbation layer),作为卷积层的替代。扰动层摒弃了传统卷积的工作方式,而是计算非线性激活加性噪声扰动输入的加权线性组合作为响应。
我们对 LBCNN 的实验表明,在深度神经网络中通过随机卷积结合可学习的通道池化进行随机特征提取也可以学习有效的图像特征,并且我们认为 PNN 中的加性随机噪声是这类随机特征提取中最简单的一种方法。
第五部分:结语
这篇文档即将收尾,我不禁开始回忆过去这两个月的经历。我得承认,当 Michael Klachko 决定在 Reddit 上公开他所发现的问题时,我有些震惊,尤其是在那之前我已经同意查看他发现的问题。一周内,那篇 reddit 帖子迅速引起了中国多个主流技术/AI 媒体的关注,并在中国社交媒体上迅速分享,阅读量超过 100 万。一些文章和评论非常严苛刺耳,不过也有一些比较理智公正。坚强如我,彼时彼刻也承受了很大压力。
不过我意识到,作为科研人员,接受大众的审查不是一种选择,而是责任。因此,我真的非常感谢 Michael,他不仅愿意花费时间和精力验证已经发布的方法,更重要的是,当发现事情不对劲时他能够及时说出来。我坚信通过这些努力,我们的社区可以取得真正的进步。
另外,我还想对刚进入这一领域的年轻科研人员或者想进入该领域的大学生(以及高中生)说几句话。这样的事的确会发生,但不应该因此而放弃开源代码或进行开放性研究。开放是 AI 领域迅速发展的核心因素。最近我回了趟中国,有机会和一位高中生聊了聊,他/她热切地跟我探讨批归一化和组归一化的实现细节。我非常震惊。对于所有年轻 AI 研究人员和从业者,我真心地鼓励大家摆脱惯性思维,不要受限于教条,去探索还有待探索的问题,去少有人去的地方,以及最重要的,进行开放性研究,分享自己的代码和研究成果。这样,你就是在帮助社区前进,即使一次只推动一点点。
那么,让我们继续探索、研究、分享吧!