固定了feature map的數量,每當使用一個size=3和stride=2進行maxpooling進行池化時,每個卷積層的計算時間減半(數據大小減半),從而形成一個金字塔。
這就是論文題目所謂的 Pyramid 。
好啦,看似問題都解決了,目標成功達成。剩下的我們就只需要重複的進行等長卷積+等長卷積+使用一個size=3和stride=2進行maxpooling進行池化就可以啦,DPCNN就可以捕捉文本的長距離依賴啦!
Shortcut connections with pre-activation
但是!如果問題真的這麼簡單的話,深度學習就一下子少了超級多的難點了。
(1) 初始化CNN的時,往往各層權重都初始化為很小的值,這導致了最開始的網路中,後續幾乎每層的輸入都是接近0,這時的網路輸出沒有意義;
(2) 小權重阻礙了梯度的傳播,使得網路的初始訓練階段往往要迭代好久才能啟動;
(3) 就算網路啟動完成,由於深度網路中仿射矩陣(每兩層間的連接邊)近似連乘,訓練過程中網路也非常容易發生梯度爆炸或彌散問題。
當然,上述這幾點問題本質就是梯度彌散問題。那麼如何解決深度CNN網路的梯度彌散問題呢?當然是膜一下何愷明大神,然後把ResNet的精華拿來用啦! ResNet中提出的shortcut-connection/ skip-connection/ residual-connection(殘差連接)就是一種非常簡單、合理、有效的解決方案。
類似地,為了使深度網路的訓練成為可能,作者為了恆等映射,所以使用加法進行shortcut connections ,即z+f(z) ,其中 f 用的是兩層的等長卷積。這樣就可以極大的緩解了梯度消失問題。
另外,作者也使用了 pre-activation ,這個最初在何凱明的「Identity Mappings in Deep Residual Networks上提及,有興趣的大家可以看看這個的原理。直觀上,這種「線性」簡化了深度網路的訓練,類似於LSTM中constant error carousels的作用。而且實驗證明 pre-activation優於post-activation。
整體來說,巧妙的結構設計,使得這個模型不需要為了維度匹配問題而擔憂。
Region embedding
同時DPCNN的底層貌似保持了跟TextCNN一樣的結構,這裡作者將TextCNN的包含多尺寸卷積濾波器的卷積層的卷積結果稱之為Region embedding ,意思就是對一個文本區域/片段(比如3gram)進行一組卷積操作後生成的embedding。
另外,作者為了進一步提高性能,還使用了tv-embedding (two-views embedding)進一步提高DPCNN的accuracy 。
上述介紹了DPCNN的整體架構,可見DPCNN的架構之精美。本文是在原始論文以及知乎上的一篇文章的基礎上進行整理。本文可能也會有很多錯誤,如果有錯誤,歡迎大家指出來!建議大家為了更好的理解DPCNN ,看一下原始論文和參考裡面的知乎。
用Keras實現DPCNN網路
這裡參考了一下kaggle的代碼,模型一共用了七層,模型的參數與論文不太相同。這裡濾波器通道個數為64(論文中為256),具體的參數可以參考下面的代碼,部分我寫了注釋。
def CNN ( x ):
block = Conv1D ( filter_nr , kernel_size = filter_size , padding = same , activation = linear ,
kernel_regularizer = conv_kern_reg , bias_regularizer = conv_bias_reg )( x )
block = BatchNormalization ()( block )
block = PReLU ()( block )
block = Conv1D ( filter_nr , kernel_size = filter_size , padding = same , activation = linear ,
kernel_regularizer = conv_kern_reg , bias_regularizer = conv_bias_reg )( block )
block = BatchNormalization ()( block )
block = PReLU ()( block )
return block
def DPCNN ():
filter_nr = 64 #濾波器通道個數
filter_size = 3 #卷積核
max_pool_size = 3 #池化層的pooling_size
max_pool_strides = 2 #池化層的步長
dense_nr = 256 #全連接層
spatial_dropout = 0.2
dense_dropout = 0.5
train_embed = False
conv_kern_reg = regularizers . l2 ( 0.00001 )
conv_bias_reg = regularizers . l2 ( 0.00001 )
comment = Input ( shape = ( maxlen ,))
emb_comment = Embedding ( max_features , embed_size , weights = [ embedding_matrix ], trainable = train_embed )( comment )
emb_comment = SpatialDropout1D ( spatial_dropout )( emb_comment )
#region embedding層
resize_emb = Conv1D ( filter_nr , kernel_size = 1 , padding = same , activation = linear ,
kernel_regularizer = conv_kern_reg , bias_regularizer = conv_bias_reg )( emb_comment )
resize_emb = PReLU ()( resize_emb )
#第一層
block1 = CNN ( emb_comment )
block1_output = add ([ block1 , resize_emb ])
block1_output = MaxPooling1D ( pool_size = max_pool_size , strides = max_pool_strides )( block1_output )
#第二層
block2 = CNN ( block1_output )
block2_output = add ([ block2 , block1_output ])
block2_output = MaxPooling1D ( pool_size = max_pool_size , strides = max_pool_strides )( block2_output )
#第三層
block3 = CNN ( block2_output )
block3_output = add ([ block3 , block2_output ])
block3_output = MaxPooling1D ( pool_size = max_pool_size , strides = max_pool_strides )( block3_output )
#第四層
block4 = CNN ( block3_output )
block4_output = add ([ block4 , block3_output ])
block4_output = MaxPooling1D ( pool_size = max_pool_size , strides = max_pool_strides )( block4_output )
#第五層
block5 = CNN ( block4_output )
block5_output = add ([ block5 , block4_output ])
block5_output = MaxPooling1D ( pool_size = max_pool_size , strides = max_pool_strides )( block5_output )
#第六層
block6 = CNN ( block5_output )
block6_output = add ([ block6 , block5_output ])
block6_output = MaxPooling1D ( pool_size = max_pool_size , strides = max_pool_strides )( block6_output )
#第七層
block7 = CNN ( block6_output )
block7_output = add ([ block7 , block6_output ])
output = GlobalMaxPooling1D ()( block7_output )
#全連接層
output = Dense ( dense_nr , activation = linear )( output )
output = BatchNormalization ()( output )
output = PReLU ()( output )
output = Dropout ( dense_dropout )( output )
output = Dense ( 6 , activation = sigmoid )( output )
model = Model ( comment , output )
model . summary ()
model . compile ( loss = binary_crossentropy ,
optimizer = optimizers . Adam (),
metrics = [ accuracy ])
return model
DPCNN實戰
上面我們用keras實現了我們的DPCNN網路,這裡我們藉助kaggle的有毒評論文本分類競賽來實戰下我們的DPCNN網路。
如果您需要有毒評論文本分類數據,可以關注"AI演算法之心",後臺回復 "ToxicComment"(建議複製)獲取。
具體地代碼,大家可以去我的GitHub上面找到源碼:
hecongqing/TextClassification ?
github.com歡迎大家關注我的個人公眾號,將會同步更新:
公眾號: AI演算法之心
參考:
https://ai.tencent.com/ailab/media/publications/ACL3-Brady.pdf
https://zhuanlan.zhihu.com/p/35457093
https://www.kaggle.com/michaelsnell/conv1d-dpcnn-in-keras
推薦閱讀: