Capsule Network

Capsule Network最大的特点在于vector in vector out & 动态路由算法。git

vector in vector out

所谓vector in vector out指的是将原先使用标量表示的神经元变为使用向量表示的神经元。这也便是所谓的“Capsule”,“vector in vector out”或者“胶囊”所要表达的意思。按照Hinton的理解,每个胶囊表示一个属性,而胶囊的向量则表示该特征的某些“含义”。好比,以前咱们使用标量表示有没有羽毛,如今咱们使用向量来表示,不只表示有没有,还表示了有什么颜色,什么材料的特征。也就是说将神经元从标量改成向量后,在特征提取时,对单个特征的表达更为丰富了。github

这有些像NLP中的词向量,以前使用的是one hot表示一个词,只能表示有没有该词而已,引入了word2vec不但表示有没有,并且可以表示该词的“意思”,表意更加丰富了。算法

部分人叫“Capsule Network胶囊网络”为“张量网络”网络

  • 层层抽象,层层分类ide

    上图展现了特征$u_1$的链接,目前从上一层传来的特征$u_1$假设表示羽毛,下一层抽取获得的$v_1,v_2,v_3,v_4$分别表示猫、狗、兔、鸟4个种类。能够很容易想到softmax: $$ (p_{1|1},p_{2|1},p_{3|1},p_{4|1})=\frac{1}{Z_1}(e^{u_1^T v_1},e^{u_1^T v_2},e^{u_1^T v_3},e^{u_1^T v_4}),\quad Z_1=\sum_{i=0}^4e^{u_1^T v_i} $$ 咱们通常选择最大的那个几率就好了,可是单靠这一个特征是不够的,咱们须要综合各个特征,能够把上面的softmax对各个特征都作一遍,得到$(p_{1|2},p_{2|2},p_{3|2},p_{4|2}),(p_{1|3},p_{2|3},p_{3|3},p_{4|3})...(p_{1|5},p_{2|5},p_{3|5},p_{4|5})$。这时就须要融合这些特征,很容易想到“加权和”函数

    对于特征$u_i$,得到的特征分布为$(p_{1|i},p_{2|i},p_{3|i},p_{4|i})$,加权和就是:$\sum_i u_ip_{j|i}(j=1,2,3,4)$。对于但愿得到向上一层的特征$v_j$,这里paper中使用了一种squash函数处理后,就变成了向上一层的特征$v_j$,也即: $$ v_j=squash(\sum_i p_{j|i}u_i)=squash(\sum_i\frac{e^{u_i^T v_j}}{Z_i}u_i) $$ 所谓squash函数就是Capsule Network中的激活函数,和ReLU, tanh, sigmoid函数做用相似,原文中的函数以下: $$ squash(x)=\frac{||x||^2}{1+||x||^2}\frac{x}{||x||} $$ 后半部分$\frac{x}{||x||}$就是将向量模变为1,前半部分$\frac{||x||^2}{1+||x||^2}$是一个缩减函数,函数值始终小于1。在$||x||$近于0时有放大做用,$||x||$越大,越没有影响。ui

    关于前半部分$\frac{||x||^2}{1+||x||^2}$,将模长压缩至0~1有不少方法,好比$tan||x||,1-e^{-||x||}$,是否是$\frac{||x||^2}{1+||x||^2}$这种压缩方式最好;若是在中间层,这个压缩处理是否有必要,因为有动态路由在里面,即便去掉squash函数也具备非线性;另外分母上的常数1哪来的?可不能够尝试其余常数实验效果,这会是一个超参数吗url

    动态路由

    注意到: $$ v_j=squash(\sum_i p_{j|i}u_i)=squash(\sum_i\frac{e^{u_i^T v_j}}{Z_i}u_i) $$ 为了求$v_j$须要求softmax,而为了求softmax又须要知道$v_j$,彷佛陷入了鸡生蛋,蛋生鸡的问题,这就是动态路由(Dynamic Routing)要解决的问题,它可以“自主更新”参数,从而达到Hinton放弃梯度降低的目标。spa

    看一个NLP的例子,考虑一个向量$(x_1,x_2,...,x_n)$,如今但愿将n个向量整合成一个向量$x$(encoder)。简化下状况,仅仅使用原来向量的线性组合,也就是: $$ x=\sum_{i=1}^n \lambda_ix_i $$ 这里的$\lambda_i$至关于衡量了$x$与$x_i$的类似度,衡量了$x_i$在最终的$x$中的重要程度,这也是一个鸡生蛋,蛋生鸡的问题,解决方法就是迭代。定义一个基于softmax的类似度指标,而后“加权和”: $$ x=\sum_{i=1}^n\frac{e^{x^Tx_i}}{Z}x_i $$ 一开始,咱们将$x$初始化为各个$x_i$的均值,而后将$x$带入右侧,左侧获得一个新的$x$,而后再将其带入右侧,如此反复,通常迭代有限次便可收敛。.net

    为了获得获得各个$v_j$,一开始先将它们初始化为$u_i$的均值,而后带入softmax迭代求解便可。事实上,输出是输入的聚类结果,而聚类一般都须要迭代算法,这个迭代算法便是“动态路由”。至于这个动态路由的细节,实际上是不固定的,取决于聚类的算法。在Dynamic Routing Between Capsules. NIPS 2017 一文中,路由算法为: $$ \begin{aligned} &动态路由算法version1\ \ &初始化b_{ij}=0 \ &迭代r次:\ &\qquad c_i \gets softmax(b_i);\ &\qquad s_j\gets\sum_ic_{ij}u_i;\ &\qquad v_j\gets squash(s_j);\ &\qquad b_{ij}\gets b_{ij}+u_i^Tv_j \end{aligned} $$ 这里的$c_{ij}$便是前文中的$p_{j|i}$。

    这里有人认为原文中的$b_{ij}\gets b_{ij}+u_i^Tv_j$有误,应为$b_{ij}\gets u_i^Tv_j$。没有看懂什么意思,不做评价。

    补充下聚类算法K-Means的伪代码: $$ \begin{aligned} 输入:&样本集D={x_1,x_2,...,x_m};\ &聚类簇数k.\ 过程:\ &从D中随机选择k个样本做为初始均值向量{\mu_1,\mu_2,...,\mu_k}\ &repeat\ &\quad 令C_i=\phi(1\leq i \leq k)\ &\quad for\ j=1,2,...,m\ do\ &\qquad 计算样本x_j 与各个均值向量\mu_i(1\leq i\leq k)的距离:d_{ij}=||x_j-\mu_i||2;\ &\qquad 根据距离最近的均值向量肯定x_j的簇标记:\lambda_j=argmax{i\in{1,2,...,k}}d_{ij};\ &\qquad 将样本x_j划入相应的簇:C_{\lambda_j}=C_{\lambda_j}\cup{x_j};\ &\quad end\ for\ &\quad for\ j=1,2,...,k\ do\ &\qquad 计算新的均值向量:\mu_{i}'=\frac{1}{|C_i|}\sum_{x\in C_i}x;\ &\qquad 更新均值向量:\mu_i\gets\mu_{i}'\ & until\ 当前全部均值向量均再也不更新 \end{aligned} $$ 类比下,这里的$x_1,x_2,...,x_m$能够看做是上一层传来的特征$u_i$;而得到的聚类中心,即均值向量$\mu_i$能够看做是这层抽取获得的特征$v_j$。

    按照这个算法,$v_j$可以迭代的算出来,那就真的抛弃反向传播了,但事实上$v_j$是做为输入$u_i$的某种聚类中心出现的,若是如此,各个$v_j$就都同样了。上面类比的K-Means算法得到聚类中心是须要一个参数*“聚类簇数k”,这在动态路由中是没有的,相似的,神经元须要从多个角度看输入,从而获得不一样的聚类中心。能够想象一个立方体,从正面得到一个中心点,从侧面又得到一个,从上面的面又能够得到一个中心点。为了实现“多角度看特征”,能够在上一层胶囊传入下一层以前,乘上一个矩阵*作变换,这种变换就是所谓的仿射变换,它给神经元看特征提供了“观察角度”,这个矩阵如今仍是须要反向传播训练获得的。那么, $$ v_j=squash(\sum_i p_{j|i}u_i)=squash(\sum_i\frac{e^{u_i^T v_j}}{Z_i}u_i) $$ 就要变为: $$ v_j=squash(\sum_i\frac{e^{\hat{u_{j|i}}^Tv_j}}{Z_i}\hat{u_{j|i}}),\ 其中\hat{u_{j|i}}=W_{ji}u_i $$ 这里的$W_{ji}$是待训练的矩阵,这里的乘法是矩阵乘法,矩阵乘向量,所以,Capsule Network变成了:

    完整的动态路由算法(Dynamic Routing Between Capsules. NIPS 2017中的动态路由): $$ \begin{aligned} &动态路由算法version2\ \ &初始化b_{ij}=0 \ &迭代r次:\ &\qquad c_i \gets softmax(b_i);\ &\qquad s_j\gets\sum_ic_{ij}\hat{u_{j|i}};\ &\qquad v_j\gets squash(s_j);\ &\qquad b_{ij}\gets b_{ij}+u_i^Tv_j \end{aligned} $$ 就是$s_j\gets\sum_ic_{ij}u_i$变为了$s_j\gets\sum_ic_{ij}\hat{u_{j|i}}$,新出来的$\hat{u_{j|i}}$由$\hat{u_{j|i}}=W_{ji}u_i$求得。

    这样的Capsule层至关于普通神经网络中的全链接层。

    进一步的,咱们但愿上一层的胶囊乘的*“观察角度矩阵”*$W$可以对上一层全部胶囊共享,就是变成这样:

    这就是权值共享版的Capsule Network,所谓共享版,是指对于上一层传来的胶囊$u_i$使用的变换矩阵是公用的,即$W_{ji}\equiv W_j$。这样计算上面的$\hat{u_{j|i}}$就变成了$\hat{u_{j|i}}=W_{j}u_i$。

    能够看到,Capsule Network因为须要$W$作仿射变换,而$W$是经过反向传播得到的,所以Capsule Network仍是存在反向传播的。

总结

  • Capsule Network将底层神经元由标量表示变为向量表示,这个向量表示就是*“胶囊”*。基于此,部分人认为“张量网络”更为通俗易懂。

  • Capsule Network与传统神经网络的对比:

    • Capsule Network的输入输出都是向量了,而非传统网络的标量。
    • 上一层传入后,Capsule Network须要作一下仿射变换(Affine Transformation),提供不一样的观察角度,而传统神经网络并无这回事。
    • Capsule Network的非线性激活使用的是squash函数,而传统的神经网络是ReLU, tanh, sigmoid等。
  • 原文中的动态路由伪代码:

    对应的简图:

  • 实验上,Dynamic Routing Between Capsules. NIPS 2017 验证了利用capsule做为神经元表示能个得到特征更为丰富的语义,好比可以捕获到笔触粗细,数字旋转等信息。Capsule Network在该文4.1中显示可以去噪,并且代表Capsule Network能给出误判的缘由。Capsule Network还能有效地对重叠数字分割。

实现

以普遍传播的CapsNet-Tensorflow,使用MNIST数据集,手写数字识别为例。整个网络分为两个卷积层(包括普通的卷积层Conv1和Capsule版的卷积层PrimaryCaps)和一个全链接层(Capsule版的全链接层DigitCaps)

  • Conv1 layer

    Input size kernel size conv stride Channel padding size activation pooling
    28x28x1 9x9 1x1 256 [0,0,0,0] ReLU No

    $$[None,28,28,1] -> [None,20,20,256]$$

    参数数量:9x9x256+256=20992

    备注: $$ 输出神经元个数=(输入神经元个数-卷积核大小+2补零个数)/步长+1\ 20=(28-9+20)/1+1 $$

  • PrimaryCaps layer

Input size kernel size conv stride Channels non-linearity func routing
20x20x256 9x9 2x2 32 squash No

$$ [None,20,20,256]\rightarrow None,6,6,32\rightarrow \left{\begin{matrix} [None,6,6,1,32]\ [None,6,6,1,32]\ ...\ [None,6,6,1,32] \end{matrix}\right.\rightarrow [None,6,6,8,32] $$

参数数量:9x9x256x32x8+8x32=5,308,672

至关于8个卷积层并行卷积,每次从各个卷积层拿出一个通道拼合出来,这就能获得了8维的向量,也就是标量变胶囊了。[None,6,6,8,32]的axis=0是batch_size;axis=1和axis=2是feature map的大小;axis=3是胶囊的维度,传统的神经网络这一维是没有的,或者能够认为是1;axis=4是通道数。代码实现:

  • DigitCaps layer

    至关于全链接层,上一层6x6x32个capsule进,10个capsule出(表示0~9数字的几率)

    输入:上一层PrimaryCaps输出的6x6x32个capsule,每一个capsule维度为[8,1]

    输出:10个capsule,维度为[16,1]。输出既然是向量而非传统的一个个标量,这里使用的是模长表示0~9数字的几率。为何使用模长,回忆上文中求$s_j$(没有使用squash激活的$v_j$)时,$s_j\leftarrow \sum_i c_{ij}\hat{u_{j|i}}$,这里$c_{ij}$是softmax获得的几率,表示底层capsule在高层capsule中的重要程度,到了最后一层,就能够理解为哪一个capsule是几率最大的输出。全部的capsule都是通过了spuash将模规范化到0~1之间的,只有$c_{ij}$越大,向量模才能越大。这里就是有人所说的神经元“竞争激活”的概念,$c_{ij}$越大,神经元就被激活。

    $$ [None,1152,8,1]\rightarrow[None,10,16,1] $$ 参数数量:

    1152x10个$W_{ij}$: 1152x10x(8x16)=1,474,560

    1152x10个$c_{ij}/b_{ij}$: 1152x10x1=11,520

改进的方向

  • squash 函数,存在的必要存疑,以及对该函数自己的改进
  • 动态路由,如今其实是给了一个框,这个“路由”其实是一个聚类,如今已经有了用EM作动态路由了,这是能够试试的坑
  • 现有动态路由中有个$b_{ij}\gets b_{ij}+u_i^Tv_j$,有个哥们推导了一番,说这东西应该改成$b_{ij}\gets u_i^Tv_j$,这样使得迭代轮数不是超参数,而是变成迭代轮数越大越好
  • 神经元原先是一个标量,如今用向量表示,可不能够用矩阵,n维张量表示?

Connect

Email: cncmn@sina.cn

GitHub: cnlinxi@github

后记

花了一周多理解Capsule Network,而且动手照着别人的Capsule Network代码理解、默写出来。今天写完算是完成了一件事情。本文大可能是拾人牙慧,可是也夹带了本身的一些私货,感谢这些博主和开源代码贡献者。

揭开迷雾,来一顿美味的Capsule盛宴 CapsNet-Tensorflow

相关文章
相关标签/搜索