溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何用飛槳復(fù)現(xiàn)Capsule Network

發(fā)布時(shí)間:2021-12-22 15:24:48 來(lái)源:億速云 閱讀:124 作者:柒染 欄目:大數(shù)據(jù)

如何用飛槳復(fù)現(xiàn)Capsule Network,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

下面讓我們一起來(lái)探究Capsule Network網(wǎng)絡(luò)結(jié)構(gòu)和原理,并使用飛槳進(jìn)行復(fù)現(xiàn)。

下載安裝命令## CPU版本安裝命令pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle

## GPU版本安裝命令pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu

卷積神經(jīng)網(wǎng)絡(luò)的不足之處

卷積神經(jīng)網(wǎng)絡(luò)(CNN)雖然表現(xiàn)的很優(yōu)異,但是針對(duì)于旋轉(zhuǎn)或元素平移等變換后的圖片,卻無(wú)法做到準(zhǔn)確提取特征。

比如,對(duì)下圖中字母R進(jìn)行旋轉(zhuǎn)、加邊框,CNN會(huì)錯(cuò)誤地認(rèn)為下圖的三個(gè)R是不同的字母。

如何用飛槳復(fù)現(xiàn)Capsule Network

這就引出了位姿的概念。位姿結(jié)合了對(duì)象之間的相對(duì)關(guān)系,在數(shù)值上表示為4維位姿矩陣。三維對(duì)象之間的關(guān)系可以用位姿表示,位姿的本質(zhì)是對(duì)象的平移和旋轉(zhuǎn)。

對(duì)于人類而言,可以輕易辨識(shí)出下圖是自由女神像,盡管所有的圖像顯示的角度都不一樣,這是因?yàn)槿祟悓?duì)圖像的識(shí)別并不依賴視角。雖然從沒(méi)有見(jiàn)過(guò)和下圖一模一樣的圖片,但仍然能立刻知道這些是自由女神像。

如何用飛槳復(fù)現(xiàn)Capsule Network

此外,人造神經(jīng)元輸出單個(gè)標(biāo)量表示結(jié)果,而膠囊可以輸出向量作為結(jié)果。CNN使用卷積層獲取特征矩陣,為了在神經(jīng)元的活動(dòng)中實(shí)現(xiàn)視角不變性,通過(guò)最大池化方法來(lái)達(dá)成這一點(diǎn)。但是使用最大池化的致命缺點(diǎn)就是丟失了有價(jià)值的信息,也沒(méi)有處理特征之間的相對(duì)空間關(guān)系。但是在膠囊網(wǎng)絡(luò)中,特征狀態(tài)的重要信息將以向量的形式被膠囊封裝。

膠囊的工作原理

讓我們比較下膠囊與人造神經(jīng)元。下表中Vector表示向量,scalar表示標(biāo)量,Operation對(duì)比了它們工作原理的差異。

如何用飛槳復(fù)現(xiàn)Capsule Network

下面將詳剖析這4個(gè)步驟的實(shí)現(xiàn)原理:

如何用飛槳復(fù)現(xiàn)Capsule Network

低層膠囊通過(guò)加權(quán)把向量輸入高層膠囊,同時(shí)高層膠囊接收到來(lái)自低層膠囊的向量。所有輸入以紅點(diǎn)和藍(lán)點(diǎn)表示。這些點(diǎn)聚集的地方,意味著低層膠囊的預(yù)測(cè)互相接近。

比如,膠囊JK中都有一組聚集的紅點(diǎn),因?yàn)檫@些膠囊的預(yù)測(cè)很接近。在膠囊J中,低層膠囊的輸出乘以相應(yīng)的矩陣W后,落在了遠(yuǎn)離膠囊J中的紅色聚集區(qū)的地方;而在膠囊K中,它落在紅色聚集區(qū)邊緣,紅色聚集區(qū)表示了這個(gè)高層膠囊的預(yù)測(cè)結(jié)果。低層膠囊具備測(cè)量哪個(gè)高層膠囊更能接受其輸出的機(jī)制,并據(jù)此自動(dòng)調(diào)整權(quán)重,使對(duì)應(yīng)膠囊K的權(quán)重C變高,對(duì)應(yīng)膠囊J的權(quán)重C變低。

關(guān)于權(quán)重,我們需要關(guān)注:

1. 權(quán)重均為非負(fù)標(biāo)量。

2. 對(duì)每個(gè)低層膠囊i而言,所有權(quán)重的總和等于1(經(jīng)過(guò)softmax函數(shù)加權(quán))。

3. 對(duì)每個(gè)低層膠囊i而言,權(quán)重的數(shù)量等于高層膠囊的數(shù)量。

4. 這些權(quán)重的數(shù)值由迭代動(dòng)態(tài)路由算法確定。

對(duì)于每個(gè)低層膠囊i而言,其權(quán)重定義了傳給每個(gè)高層膠囊j的輸出的概率分布。

3. 加權(quán)輸入向量之和

這一步表示輸入的組合,和通常的人工神經(jīng)網(wǎng)絡(luò)類似,只是它是向量的和而不是標(biāo)量的和。

4. 向量到向量的非線性變換

CapsNet的另一大創(chuàng)新是新穎的非線性激活函數(shù),這個(gè)函數(shù)接受一個(gè)向量,然后在不改變方向的前提下,壓縮它的長(zhǎng)度到1以下。

實(shí)現(xiàn)代碼如下:

def squash(self,vector):
         '''
        壓縮向量的函數(shù),類似激活函數(shù),向量歸一化
        Args:
            vector:一個(gè)4維張量 [batch_size,vector_num,vector_units_num,1]
        Returns:
            一個(gè)和x形狀相同,長(zhǎng)度經(jīng)過(guò)壓縮的向量
            輸入向量|v|(向量長(zhǎng)度)越大,輸出|v|越接近1
        '''        vec_abs = fluid.layers.sqrt(fluid.layers.reduce_sum(fluid.layers.square(vector)))  
        scalar_factor = fluid.layers.square(vec_abs) / (1 + fluid.layers.square(vec_abs))  
        vec_squashed = scalar_factor * fluid.layers.elementwise_div(vector, vec_abs) 
        return(vec_squashed)

囊間動(dòng)態(tài)路由(精髓所在)

低層膠囊將其輸出發(fā)送給對(duì)此表示“同意”的高層膠囊。這是動(dòng)態(tài)路由算法的精髓。

如何用飛槳復(fù)現(xiàn)Capsule Network

囊間動(dòng)態(tài)路由算法偽代碼

  • 偽代碼的第一行指明了算法的輸入:低層輸入向量經(jīng)過(guò)矩陣乘法得到的?,以及路由迭代次數(shù)r。最后一行指明了算法的輸出,高層膠囊的向量Vj。

  • 第2行的bij是一個(gè)臨時(shí)變量,存放了低層向量對(duì)高層膠囊的權(quán)重,它的值會(huì)在迭代過(guò)程中逐個(gè)更新,當(dāng)開(kāi)始一輪迭代時(shí),它的值經(jīng)過(guò)softmax轉(zhuǎn)換成cij。在囊間動(dòng)態(tài)路由算法開(kāi)始時(shí),bij的值被初始化為零(但是經(jīng)過(guò)softmax后會(huì)轉(zhuǎn)換成非零且各個(gè)權(quán)重相等的cij)。

  • 第3行表明第4-7行的步驟會(huì)被重復(fù)r次(路由迭代次數(shù))。

  • 第4行計(jì)算低層膠囊向量的對(duì)應(yīng)所有高層膠囊的權(quán)重。bi的值經(jīng)過(guò)softmax后會(huì)轉(zhuǎn)換成非零權(quán)重ci且其元素總和等于1。

  • 如果是第一次迭代,所有系數(shù)cij的值會(huì)相等。例如,如果我們有8個(gè)低層膠囊和10個(gè)高層膠囊,那么所有cij的權(quán)重都將等于0.1。這樣初始化使不確定性達(dá)到最大值:低層膠囊不知道它們的輸出最適合哪個(gè)高層膠囊。當(dāng)然,隨著這一進(jìn)程的重復(fù),這些均勻分布將發(fā)生改變。

  • 第5行,那里將涉及高層膠囊。這一步計(jì)算經(jīng)前一步確定的路由系數(shù)加權(quán)后的輸入向量的總和,得到輸出向量sj。

  • 第7行進(jìn)行更新權(quán)重,這是路由算法的精髓所在。我們將每個(gè)高層膠囊的向量vj與低層原來(lái)的輸入向量?逐元素相乘求和獲得內(nèi)積(也叫點(diǎn)積,點(diǎn)積檢測(cè)膠囊的輸入和輸出之間的相似性(下圖為示意圖)),再用點(diǎn)積結(jié)果更新原來(lái)的權(quán)重bi。這就達(dá)到了低層膠囊將其輸出發(fā)送給具有類似輸出的高層膠囊的效果,刻畫了向量之間的相似性。這一步驟之后,算法跳轉(zhuǎn)到第3步重新開(kāi)始這一流程,并重復(fù)r次。

如何用飛槳復(fù)現(xiàn)Capsule Network   

▲ 點(diǎn)積運(yùn)算即為向量的內(nèi)積(點(diǎn)積)運(yùn)算,

可以表現(xiàn)向量的相似性。

重復(fù)次后,我們計(jì)算出了所有高層膠囊的輸出,并確立正確路由權(quán)重。下面是根據(jù)上述原理實(shí)現(xiàn)的膠囊層:

class Capsule_Layer(fluid.dygraph.Layer):
    def __init__(self,pre_cap_num,pre_vector_units_num,cap_num,vector_units_num):
        '''
        膠囊層的實(shí)現(xiàn)類,可以直接同普通層一樣使用
        Args:
            pre_vector_units_num(int):輸入向量維度 
            vector_units_num(int):輸出向量維度 
            pre_cap_num(int):輸入膠囊數(shù) 
            cap_num(int):輸出膠囊數(shù) 
            routing_iters(int):路由迭代次數(shù),建議3次 
        Notes:
            膠囊數(shù)和向量維度影響著性能,可作為主調(diào)參數(shù)
        '''
        super(Capsule_Layer,self).__init__()
        self.routing_iters = 3
        self.pre_cap_num = pre_cap_num
        self.cap_num = cap_num
        self.pre_vector_units_num = pre_vector_units_num
        for j in range(self.cap_num):
            self.add_sublayer('u_hat_w'+str(j),fluid.dygraph.Linear(\
            input_dim=pre_vector_units_num,output_dim=vector_units_num))


    def squash(self,vector):
        '''
        壓縮向量的函數(shù),類似激活函數(shù),向量歸一化
        Args:
            vector:一個(gè)4維張量 [batch_size,vector_num,vector_units_num,1]
        Returns:
            一個(gè)和x形狀相同,長(zhǎng)度經(jīng)過(guò)壓縮的向量
            輸入向量|v|(向量長(zhǎng)度)越大,輸出|v|越接近1
        '''
        vec_abs = fluid.layers.sqrt(fluid.layers.reduce_sum(fluid.layers.square(vector)))
        scalar_factor = fluid.layers.square(vec_abs) / (1 + fluid.layers.square(vec_abs))
        vec_squashed = scalar_factor * fluid.layers.elementwise_div(vector, vec_abs)
        return(vec_squashed)

    def capsule(self,x,B_ij,j,pre_cap_num):
        '''
        這是動(dòng)態(tài)路由算法的精髓。
        Args:
            x:輸入向量,一個(gè)四維張量 shape = (batch_size,pre_cap_num,pre_vector_units_num,1)
            B_ij: shape = (1,pre_cap_num,cap_num,1)路由分配權(quán)重,這里將會(huì)選取(split)其中的第j組權(quán)重進(jìn)行計(jì)算
            j:表示當(dāng)前計(jì)算第j個(gè)膠囊的路由
            pre_cap_num:輸入膠囊數(shù)
        Returns:
            v_j:經(jīng)過(guò)多次路由迭代之后輸出的4維張量(單個(gè)膠囊)
            B_ij:計(jì)算完路由之后又拼接(concat)回去的權(quán)重
        Notes:
            B_ij,b_ij,C_ij,c_ij注意區(qū)分大小寫哦
        '''
        x = fluid.layers.reshape(x,(x.shape[0],pre_cap_num,-1))
        u_hat = getattr(self,'u_hat_w'+str(j))(x)
        u_hat = fluid.layers.reshape(u_hat,(x.shape[0],pre_cap_num,-1,1))
        shape_list = B_ij.shape#(1,1152,10,1)
        split_size = [j,1,shape_list[2]-j-1]
        for i in range(self.routing_iters):
            C_ij = fluid.layers.softmax(B_ij,axis=2)
            b_il,b_ij,b_ir = fluid.layers.split(B_ij,split_size,dim=2)
            c_il,c_ij,b_ir = fluid.layers.split(C_ij,split_size,dim=2)
            v_j = fluid.layers.elementwise_mul(u_hat,c_ij) 
v_j = fluid.layers.reduce_sum(v_j,dim=1,keep_dim=True)
            v_j = self.squash(v_j)
            v_j_expand = fluid.layers.expand(v_j,(1,pre_cap_num,1,1))
            u_v_produce = fluid.layers.elementwise_mul(u_hat,v_j_expand)
            u_v_produce = fluid.layers.reduce_sum(u_v_produce,dim=2,keep_dim=True) 
            b_ij += fluid.layers.reduce_sum(u_v_produce,dim=0,keep_dim=True)
            B_ij = fluid.layers.concat([b_il,b_ij,b_ir],axis=2)
        return v_j,B_ij

    def forward(self,x):
        '''
        Args:
            x:shape = (batch_size,pre_caps_num,vector_units_num,1) or (batch_size,C,H,W)
                如果是輸入是shape=(batch_size,C,H,W)的張量,
                則將其向量化shape=(batch_size,pre_caps_num,vector_units_num,1)
                滿足:C * H * W = vector_units_num * caps_num
                其中 C >= caps_num
        Returns:
            capsules:一個(gè)包含了caps_num個(gè)膠囊的list
        '''
        if x.shape[3]!=1:
            x = fluid.layers.reshape(x,(x.shape[0],self.pre_cap_num,-1))
            temp_x = fluid.layers.split(x,self.pre_vector_units_num,dim=2)
            temp_x = fluid.layers.concat(temp_x,axis=1)
            x = fluid.layers.reshape(temp_x,(x.shape[0],self.pre_cap_num,-1,1))
            x = self.squash(x)
        B_ij = fluid.layers.ones((1,x.shape[1],self.cap_num,1),dtype='float32')/self.cap_num#
        capsules = []
        for j in range(self.cap_num):
            cap_j,B_ij = self.capsule(x,B_ij,j,self.pre_cap_num)
            capsules.append(cap_j)
        capsules = fluid.layers.concat(capsules,axis=1)
        return capsules

損失函數(shù)

將一個(gè)10維one-hot編碼向量作為標(biāo)簽,該向量由9個(gè)零和1個(gè)一(正確標(biāo)簽)組成。在損失函數(shù)公式中,與正確的標(biāo)簽對(duì)應(yīng)的輸出膠囊,系數(shù)Tc為1。

如果正確標(biāo)簽是9,這意味著第9個(gè)膠囊輸出的損失函數(shù)的Tc為1,其余9個(gè)為0。當(dāng)Tc為1時(shí),公式中損失函數(shù)的右項(xiàng)系數(shù)為零,也就是說(shuō)正確輸出項(xiàng)損失函數(shù)的值只包含了左項(xiàng)計(jì)算;相應(yīng)的左系數(shù)為0,則右項(xiàng)系數(shù)為1,錯(cuò)誤輸出項(xiàng)損失函數(shù)的值只包含了右項(xiàng)計(jì)算。

|v|為膠囊輸出向量的模長(zhǎng),一定程度上表示了類概率的大小,我們?cè)贁M定一個(gè)量m,用這個(gè)變量來(lái)衡量概率是否合適。公式右項(xiàng)包括了一個(gè)lambda系數(shù)以確保訓(xùn)練中的數(shù)值穩(wěn)定性(lambda為固定值0.5),這兩項(xiàng)取平方是為了讓損失函數(shù)符合L2正則。

 def get_loss_v(self,label):
        '''
        計(jì)算邊緣損失
        Args:
            label:shape=(32,10) one-hot形式的標(biāo)簽
        Notes:
            這里我調(diào)用Relu把小于0的值篩除
            m_plus:正確輸出項(xiàng)的概率(|v|)大于這個(gè)值則loss為0,越接近則loss越小
            m_det:錯(cuò)誤輸出項(xiàng)的概率(|v|)小于這個(gè)值則loss為0,越接近則loss越小
            (|v|即膠囊(向量)的模長(zhǎng))
        '''        #計(jì)算左項(xiàng),雖然m+是單個(gè)值,但是可以通過(guò)廣播的形式與label(32,10)作差        max_l =  fluid.layers.relu(train_params['m_plus'] - self.output_caps_v_lenth)
        #平方運(yùn)算后reshape        max_l = fluid.layers.reshape(fluid.layers.square(max_l),(train_params['batch_size'],-1))#32,10        #同樣方法計(jì)算右項(xiàng)        max_r =  fluid.layers.relu(self.output_caps_v_lenth - train_params['m_det'])
        max_r = fluid.layers.reshape(fluid.layers.square(max_r),(train_params['batch_size'],-1))#32,10        #合并的時(shí)候直接用one-hot形式的標(biāo)簽逐元素乘算便可        margin_loss = fluid.layers.elementwise_mul(label,max_l)\
                        + fluid.layers.elementwise_mul(1-label,max_r)*train_params['lambda_val']
        self.margin_loss = fluid.layers.reduce_mean(margin_loss,dim=1)

編碼器

完整的網(wǎng)絡(luò)結(jié)構(gòu)分為編碼器解碼器,我們先來(lái)看看編碼器。

如何用飛槳復(fù)現(xiàn)Capsule Network

1. 輸入圖片28x28首先經(jīng)過(guò)1x256x9x9的卷積層 獲得256個(gè)20x20的特征圖;

2. 用8組256x32x9x9(stride=2)的卷積獲得8組32x6x6的特征圖;

3. 將獲取的特征圖向量化輸入10個(gè)膠囊,這10個(gè)膠囊輸出向量的長(zhǎng)度就是各個(gè)類別的概率。

class Capconv_Net(fluid.dygraph.Layer):
    def __init__(self):
        super(Capconv_Net,self).__init__()
        self.add_sublayer('conv0',fluid.dygraph.Conv2D(\
        num_channels=1,num_filters=256,filter_size=(9,9),padding=0,stride = 1,act='relu'))
                for i in range(8):
            self.add_sublayer('conv_vector_'+str(i),fluid.dygraph.Conv2D(\
            num_channels=256,num_filters=32,filter_size=(9,9),stride=2,padding=0,act='relu'))

    def forward(self,x,v_units_num):
        x = getattr(self,'conv0')(x)
        capsules = []
        for i in range(v_units_num):
            temp_x = getattr(self,'conv_vector_'+str(i))(x)
            capsules.append(fluid.layers.reshape(temp_x,(train_params['batch_size'],-1,1,1)))
        x = fluid.layers.concat(capsules,axis=2)        x = self.squash(x)
        return x

從實(shí)現(xiàn)代碼中我們不難看出特征圖轉(zhuǎn)換成向量實(shí)際的過(guò)程,是將每組二維矩陣展開(kāi)成一維矩陣(當(dāng)然有多個(gè)二維矩陣則展開(kāi)后前后拼接);之后再將所有組的一維矩陣在新的維度拼接形成向量(下圖為示意圖)。根據(jù)下面這個(gè)思路我經(jīng)把8次卷積縮小到了一次卷積,本質(zhì)上脫離循環(huán)只用split和concat方法直接向量化,加快了訓(xùn)練效率。

如何用飛槳復(fù)現(xiàn)Capsule Network

解碼器從正確的膠囊中接受一個(gè)16維向量,輸入經(jīng)過(guò)三個(gè)全連接層得到784個(gè)像素輸出,學(xué)習(xí)重建一張28×28像素的圖像,損失函數(shù)為重建圖像與輸入圖像之間的歐氏距離。

下圖是我自己訓(xùn)練的網(wǎng)絡(luò)重構(gòu)獲得的圖像,上面是輸入網(wǎng)絡(luò)的原圖片,下面是網(wǎng)絡(luò)重建的圖片。

如何用飛槳復(fù)現(xiàn)Capsule Network

再來(lái)玩一下,當(dāng)訓(xùn)練到一半時(shí)將所有圖片轉(zhuǎn)置(可以理解為將圖片水平垂直翻轉(zhuǎn)+旋轉(zhuǎn)角度,改變位姿)的情況,實(shí)驗(yàn)結(jié)論如下。

如何用飛槳復(fù)現(xiàn)Capsule Network

看完上述內(nèi)容,你們掌握如何用飛槳復(fù)現(xiàn)Capsule Network的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI