胶囊网络初探

如果你有关注我的 Github,可以看到我和小伙伴们正在做一个中文短文本情感分析 web 应用。恰逢 WWW 2018 收录了一篇《Sentiment Analysis by Capsules》,借着这个机会了解一下“神经网络之父” Geoffrey Hinton 大神花费近十年心血的胶囊网络(Capsule Networks)。如果能够用于我们的应用中就更好了,即使因为缺少计算资源等原因而无法实现,了解前沿技术、吸收大牛思想也是极好的。

先简单介绍一下 Capsule。NIPS 2017 发表的《Dynamic Routing Between Capsules》[1]使得这一概念开始走红,虽然 Capsule 本用于 CV 领域,不过目前也已经在各领域有了积极的尝试。Capsule 是 Hinton“反 CNN”的一面旗帜,他认为导致深度学习如火如荼的 CNN 其实有着重大的缺陷。我接下来就将介绍这个缺陷、Capsule 如何通过其工作原理避免这个缺陷、Capsule 的结构,以及 Capsule 的训练算法与损失函数等。不过因为篇幅有限,这篇文章暂时不会介绍 CapsNet 的具体架构,这部分可以看一下参考资料列的一些或者直接看原论文。顺便一提,Capsule 和胶囊两个词可能在本文中互有使用,但我实际上想用它们指同一个东西。

CNN 的缺陷

如果你了解 CNN(不了解请移至《卷积神经网络 - 吴恩达《深度学习》系列课程笔记》),你会知道 CNN 依靠卷积层检测图像中的特征。前几层学习一些例如边缘的简单特征,而较深的层组合简单特征形成更为复杂的特征,最后再将复杂特征组合并输出分类预测。

而在层与层之间,前一层的激活与下一层神经元的权重相乘并相加,接着传递到非线性激活函数。这种简单的加权和导致 CNN 难以察觉平移和旋转(以及类似变化)后图像的一致性,例如,将一张狗的照片旋转 5 度,训练好的 CNN 可能会认为两张图片都是狗,但是不会认为它们是同一只狗。

CNN 解决这个问题的方法是使用最大池化,对于一定区域内的特征取最大值,以此缓解一定的平移和旋转的影响(平均池化同理)。事实证明,CNN 的效果惊人,以至于“它表现如此优异是一场灾难”。引号内是 Hinton 的原话,因为他认为最大池化损失了很多有价值的信息。最重要的是,CNN 只考虑简单对象的存在,而没有考虑简单对象和复杂对象之间的空间层级关系。举个例子,对于 CNN 而言,下面两张图片都是一张脸,因为它们包含相似的部件,尽管我们知道散落的器官不能被称为脸。

人脸.jpg

以上问题导致的结果是,CNN 难以分类和辨识不同视角的同一物体。对于人类,即使你只看过几张自由女神像的照片,也能够轻松辨认不同角度的自由女神像。

自由女神.jpg

但对于 CNN,这个任务就非常困难了,因为它没有内建对三维空间的理解。它只能与以前训练过的照片进行对比,这导致训练 CNN 往往需要百万数量级的图像才能训练出较为精准的效果,并且当遇到角度奇怪的图像时分类正确率可能受到影响。最重要的是,我们知道神经网络本就是为了尽可能还原人脑的功能,但明显人类根本不需要对每个物体都看如此规模的图片才能学会辨认。

Why Capsule?

基于以上分析,尽管目前 CNN 看似效果非常出色,但是它不会是未来。实际上,Hinton 早在 2011 年就提出 Capsule 的结构[2],但是在当时还没有提出一种算法可以实现并成功学习胶囊网络。而在[1]中,Hinton 提出一种名为动态路由(Dynamic Routing)的算法解决了这个问题。

CNN 通过一层层的过滤,将信息一步步由下而上的进行抽象;而 Hinton 认为人类在识别图像时是遵循类似决策树的方式。Capsule 同样遵循这个结构的思路,每个活动的 Capsule 将选择一个上层的 Capsule 作为父节点。

Capsule 更好地建模神经网络中内部知识表示的分层关系,这使得训练它所需的数据量大大降低。关键的一点是,Capsule 背后的直觉非常简单优雅,易于理解。尽管目前训练胶囊网络速度比起已经成熟的深度学习模型来说要慢很多,但是假以时日,也许胶囊网络能够在更多训练集上快速高效训练。

Capsule 的工作原理

Capsule 的工作原理归纳成一句话就是,所有胶囊检测中的特征的状态的重要信息,都将以向量的形式(神经元输出的则是标量)被胶囊封装

进一步来说,一个胶囊捕捉一个特征。胶囊将特征存在的概率作为其输出向量的长度进行编码,这与目前的神经元同理。而检测出的特征的状态被编码为该向量指向的方向(“实例参数”)。因此,当检测出的特征在图像中平移或旋转,向量长度没有改变(因为概率保持不变),但它的方向改变了。

从标量到向量,这是一个让人初次听闻时拍案叫绝、但仔细思考过后又觉得理所当然的改变。这种改变使得平移或旋转导致的“变化”与“不变”被转化为向量的长度与方向,从而自然分离开来,而不是像 CNN 使用最大池化一样将“变化”全部简单粗暴地抹平为“不变”。

Capsule 的运算步骤

胶囊vs人工神经元.jpg

上图中左边为胶囊,右边为普通神经元。本质上,普通神经元的运算步骤分为 3 步:

  1. 输入标量的标量加权;
  2. 对加权后的标量求和;
  3. 对和进行非线性变换生成新标量。

而胶囊则在向量版的基础上有少许改动,共 4 步:

  1. 输入向量的矩阵乘法;
  2. 输入向量的标量加权;
  3. 对加权后的向量求和;
  4. 对和进行非线性变换生成新向量。

输入向量的矩阵乘法

上层胶囊的输入向量来自下层胶囊。这些向量相乘的权重矩阵 W 编码了低层特征和高层特征之间包括空间关系在内的重要关系。

我在这里有一个疑问:这个 W 怎么得到?如果是通过训练迭代得到,那么用的是什么算法?原 paper 对这点略过不表,有一篇文章说 W 是固定不变的,但是也没有说如何获得。如果有看到这篇文章并有答案的同学,欢迎评论指出或者邮件联系我。

输入向量的标量加权

在这一步中,我们可能比较关心如何通过学习得到权重矩阵 W’。通常神经网络通过反向传播算法学习,而胶囊则使用动态路由算法。我们将在后文详细介绍动态路由算法,这里先简单介绍一下 W’ 中权重 cij 的一些性质:

  1. 权重均为非负标量;
  2. 对每个低层胶囊 i,所有权重 cij 的总和等于 1;
  3. 对每个低层胶囊 i,权重的数量等于高层胶囊的数量。

这三条性质均在表明,这里的权重表示概率。

对加权后的向量求和

除开求的是向量的和(而非标量和),这一步与通常的神经网络没有区别。

对和进行非线性变换生成新向量

Capsule 使用了一个新颖的非线性激活函数——squash 函数。它可以压缩输入向量的长度,而不改变其方向。

squash.png

容易看出,蓝色矩形部分将输入向量缩放至单位长度,而红色矩形部分进行额外的缩放操作。函数图像如下:

suqash-function-graph.png

总结来说,对于上一个胶囊层输出的所有胶囊向量,通过转换矩阵转换为更高层的胶囊向量,最后通过动态路由算法聚合成一个胶囊向量,最后通过 squash 激活函数输出最后结果。

动态路由(Dynamic Routing)算法

动态路由算法的思想是,低层胶囊将其输出发送给对此表示“同意”的高层胶囊。这种同意表现为向量的点积

贴一张论文中对动态路由算法的伪代码描述:

伪代码.jpg

我们只看几个算法中的关键点:

  • 第一行指明算法的输入:l 是低层的层数,\hat u 是低层输出的所有胶囊向量,r 是迭代次数。
  • 第四行使用 Softmax 的原因是强制实施“对每个低层胶囊 i,所有权重 cij 的总和等于 1”这一条性质。
  • 初始化时,所有 cij 均相等,说明底层胶囊被高层胶囊接受的概率相等。随着迭代,这种均匀分布将被改变。
  • 第七行更新了权重,也是路由算法的本质所在。在这一步中,我们查看了每个高层胶囊 j,然后检查每个输入并根据公式更新相应的权重 bij。公式表明,胶囊 j 的当前输出和从低层胶囊 i 处接收的输入的点积,加上旧权重,等于新权重。点积检测胶囊的输入和输出之间的相似性。

论文表示,通过在 MNIST 和 CIFAR 两个数据集上的检验,实践中建议使用 3 次迭代。更多的迭代容易导致过拟合。

代价函数

代价函数.png

对于数字 c(或者说类别 c),代价函数如上图所示。MNIST 数据集中,当数字 c 与标签对应时,Tc = 1,否则为 0;m+ = 0.9,m- = 0.1。λ 取 0.5 以确保训练中的数值稳定性。

结语

了解 CapsNet 的具体架构有助于理解以上内容,所以我还是建议看一下原论文中这部分的内容。另外,虽然原论文相对于很多深度学习论文显得简洁而易于理解,但是有些细节并没有完全解释,我会试着再找一些资料阅读。我的下一个目标是阅读文章开头提到的《Sentiment Analysis by Capsules》。显然这篇新鲜出炉的 paper 没有什么辅助我理解的文章,我打算打印下来仔细读一读。

参考资料

论文

文章

开源代码