1. 程式人生 > >image caption解讀系列(二):《Knowing When to Look: Adaptive Attention via A Visual Sentinel for Image Capt》

image caption解讀系列(二):《Knowing When to Look: Adaptive Attention via A Visual Sentinel for Image Capt》

本文主要是在這篇部落格的基礎上結合程式碼進行分析。

文章依然採用了encoder-decoder的框架。作者認為decoder的時候非視覺詞多依賴的是語義資訊而不是視覺資訊。而且,在生成caption的過程中,非視覺詞的梯度會誤導或者降低視覺資訊的有效性。因此,本文提出了帶有視覺標記的自適應的attention模型(adative attention model with a visual sentinel),在每一個time step,模型決定更依賴於影象還是visual sentinel。其中,visual sentinel存放了decoder已經知道的資訊。 

本文的貢獻在於 : 1、提出了帶有視覺標記的自適應的attention模型

2、提出了新的spatial attention機制

3、提出了LSTM的擴充套件,在hidden state以外加入了一個額外的visual sentinel vector

模型結構:

1、首先是encoder部分:

encoder分別提取影象的全域性特徵和區域性特徵。採用的是resnet-152,去除最後兩層,提取卷積特徵。

v_g代表全域性特徵([batch_size,256]),v代表區域性特徵([batch_size,49,512])。

區域性特徵分為v1,v2.v3.....v49

        A = self.resnet_conv( images )#[batch_size,2048,7,7]

        
        a_g = self.avgpool( A )             #[batch_size,2048,1,1]
        a_g = a_g.view( a_g.size(0), -1 )   #[batch_size,2048]
        
        
        # V = [ v_1, v_2, ..., v_49 ]
        V = A.view( A.size( 0 ), A.size( 1 ), -1 ).transpose( 1,2 )#[2, 49, 2048]
          
        V = F.relu( self.affine_a( self.dropout( V ) ) )#[2, 49, 512]  
        v_g = F.relu( self.affine_b( self.dropout( a_g ) ) )
        
        return V, v_g   
        #V:[batch_size, 49, 512(hidden size)]   v_g:[batch_size,256(embeding size)]

2、其次是decoder部分

先上模型結構圖

首先LSTM的輸入不再只是當前時刻的word_embedding,而是和全域性影象特徵cat起來。

作者在LSTM中加入sentinel(哨兵機制),產生s_t:

具體做法是:

相應程式碼為:

g_t的定義與公式略有不同。

        # g_t = sigmoid( W_x * x_t + W_h * h_(t-1) )        
        gate_t = self.affine_x( self.dropout( x_t ) ) + self.affine_h( self.dropout( h_t_1 ) )
        gate_t = F.sigmoid( gate_t )
        
        # Sentinel embedding
        s_t =  gate_t * F.tanh( cell_t )
        
        return s_t

attention機制:

輸入各個區域性特徵V,各個時刻的hidden_state、s_t

對於普通的attention:

基於hidden_state,decoder會關注影象的不同區域,ct就是該區域經過CNN後提取出的feature map。

 

這就是在不使用自適應時對各個區域性特徵加權得到的最終影象特徵。

相關程式碼:

        # W_v * V + W_g * h_t * 1^T
        content_v = self.affine_v( self.dropout( V ) ).unsqueeze( 1 ) \
                    + self.affine_g( self.dropout( h_t ) ).unsqueeze( 2 )
        
        # z_t = W_h * tanh( content_v )
        z_t = self.affine_h( self.dropout( F.tanh( content_v ) ) ).squeeze( 3 )
        alpha_t = F.softmax( z_t.view( -1, z_t.size( 2 ) ) ).view( z_t.size( 0 ), z_t.size( 1 ), -1 )
        
        # Construct c_t: B x seq x hidden_size
        c_t = torch.bmm( alpha_t, V ).squeeze( 2 )

在本文中,作者使用了自適應,context vector變為:

權重也變為:

上述公式可以簡化為:

相應程式碼:

        # W_s * s_t + W_g * h_t
        content_s = self.affine_s( self.dropout( s_t ) ) + self.affine_g( self.dropout( h_t ) )
        # w_t * tanh( content_s )
        z_t_extended = self.affine_h( self.dropout( F.tanh( content_s ) ) )
        
        # Attention score between sentinel and image content
        extended = torch.cat( ( z_t, z_t_extended ), dim=2 )
        alpha_hat_t = F.softmax( extended.view( -1, extended.size( 2 ) ) ).view( extended.size( 0 ), extended.size( 1 ), -1 )
        beta_t = alpha_hat_t[ :, :, -1 ]
        
        # c_hat_t = beta * s_t + ( 1 - beta ) * c_t
        beta_t = beta_t.unsqueeze( 2 )
        c_hat_t = beta_t * s_t + ( 1 - beta_t ) * c_t

        return c_hat_t, alpha_t, beta_t

最後計算單詞的概率分佈:

相應公式為

相應程式碼為:

scores = self.mlp( self.dropout( c_hat + hiddens ) )#最後單詞的概率分佈

計算損失函式:

loss = LMcriterion( packed_scores[0], targets )#評價

最後放一下程式碼連結