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代碼理解、默寫出來。今天寫完算是完成了一件事情。本文大可能是拾人牙慧,可是也夾帶了本身的一些私貨,感謝這些博主和開源代碼貢獻者。