文本分類任務中可以利用CNN來提取句子中類似 n-gram 的關鍵信息。
TextCNN的詳細過程原理圖見下:
keras 代碼:
1 def convs_block(data, convs=[3, 3, 4, 5, 5, 7, 7], f=256): 2 pools = [] 3 for c in convs: 4 conv = Activation(activation="relu")(BatchNormalization()( 5 Conv1D(filters=f, kernel_size=c, padding="valid")(data))) 6 pool = GlobalMaxPool1D()(conv) 7 pools.append(pool) 8 return concatenate(pools) 9 10 11 def rnn_v1(seq_length, embed_weight, pretrain=False): 12 13 main_input = Input(shape=(seq_length,), dtype='float64') 14 15 in_dim, out_dim = embed_weight.shape 16 embedding = Embedding(input_dim=in_dim, weights=[ 17 embed_weight], output_dim=out_dim, trainable=False) 18 content = Activation(activation="relu")( 19 BatchNormalization()((TimeDistributed(Dense(256))(embedding(main_input))))) 20 content = Bidirectional(GRU(256))(content) 21 content = Dropout(0.3)(content) 22 fc = Activation(activation="relu")( 23 BatchNormalization()(Dense(256)(content))) 24 main_output = Dense(3, 25 activation='softmax')(fc) 26 27 model = Model(inputs=main_input, outputs=main_output) 28 model.compile(optimizer='adam', 29 loss='categorical_crossentropy', 30 metrics=['accuracy']) 31 model.summary() 32 return model
說明如下:
- 輸入層
如圖所示,,假設句子有 n 個詞,vector的維數為 k ,那么這個矩陣就是 n×k 的。
這個矩陣的類型可以是靜態的(static),也可以是動態的(non static)。靜態就是word vector是固定不變的,而動態則是在模型訓練過程中,word vector也當做是可優化的參數,通常把反向誤差傳播導致word vector中值發生變化的這一過程稱為Fine tune
。
對於未登錄詞的vector,可以用0或者隨機小的正數來填充。
- 第一層卷積層
輸入層通過卷積操作得到若干個Feature Map
,卷積窗口的大小為 m*k ,其中 m表示n_gram中的n,通過卷積將得到F個列數為1的Feature Map,F表示卷積核的個數。
- 池化層
接下來的池化層,文中用了一種稱為Max-over-time Pooling
的方法。這種方法就是簡單地從之前一維的Feature Map
中提出最大的值,文中解釋最大值代表着最重要的信號。
最終池化層的輸出為各個Feature Map
的最大值,即一個一維的向量。polling之后得到的是1*F的一維向量。
- 全連接 + Softmax層
池化層的一維向量的輸出通過全連接的方式,連接一個Softmax層。
最終實現時,我們可以在倒數第二層的全連接部分上使用Dropout
技術,即對全連接層上的權值參數給予L2正則化
的限制。這樣做的好處是防止隱藏層單元自適應(或者對稱),從而減輕過擬合的程度。
實驗部分
1. 數據
實驗用到的數據集如下(具體的名稱和來源可以參考論文):
2. 模型訓練和調參
- 修正線性單元(Rectified linear units)
- 濾波器的h大小:3,4,5;對應的Feature Map的數量為100;
- Dropout率為0.5,L2正則化限制權值大小不超過3;
- mini-batch的大小為50;
這些參數的選擇都是基於SST-2 dev數據集,通過網格搜索方法(Grid Search)得到的最優參數。另外,訓練過程中采用隨機梯度下降方法,基於shuffled mini-batches之上的,使用了Adadelta update rule(Zeiler, 2012)。
3. 預訓練的Word Vector
這里的word vector使用的是公開的數據,即連續詞袋模型(COW)在Google News上的訓練結果。未登錄次的vector值是隨機初始化的。
4. 實驗結果
實驗結果如下圖:
其中,前四個模型是上文中所提出的基本模型的各個變種:
- CNN-rand: 所有的word vector都是隨機初始化的,可以訓練的參數。
- CNN-static: Google的Word2Vector工具(CBOW模型)得到的結果,不可訓練;
- CNN-non-static: Google的Word2Vector工具(CBOW模型)得到的結果,但是會在訓練過程中被
Fine tuned
; - CNN-multichannel: CNN-static和CNN-non-static的混合版本,即兩種類型的輸入;
5. 結論
CNN-static
較與CNN-rand
好,說明pre-training的word vector確實有較大的提升作用(因為pre-training的word vector顯然利用了更大規模的文本數據信息);CNN-non-static
較於CNN-static
大部分要好,說明適當的Fine tune也是有利的,是因為使得vectors更加貼近於具體的任務;CNN-multichannel
較於CNN-single
在小規模的數據集上有更好的表現,實際上CNN-multichannel
體現了一種折中思想,即既不希望Fine tuned的vector距離原始值太遠,但同時保留其一定的變化空間。
值得注意的是,static的vector和non-static的相比,有一些有意思的現象如下表格:
- 原始的word2vector訓練結果中,
bad
對應的最相近詞為good
,原因是這兩個詞在句法上的使用是極其類似的(可以簡單替換,不會出現語句毛病);而在non-static
的版本中,bad
對應的最相近詞為terrible
,這是因為在Fune tune
的過程中,vector的值發生改變從而更加貼切數據集(是一個情感分類的數據集),所以在情感表達的角度這兩個詞會更加接近; - 句子中的
!
最接近一些表達形式較為激進的詞匯,如lush
等;而,
則接近於一些連接詞,這和我們的主觀感受也是相符的。
Kim Y的這個模型很簡單,但是卻有着很好的性能。后續Denny用TensorFlow實現了這個模型的簡單版本,可參考這篇博文;以及Ye Zhang等人對這個模型進行了大量的實驗,並給出了調參的建議,可參考這篇論文。
下面總結一下Ye Zhang等人基於Kim Y的模型做了大量的調參實驗之后的結論:
- 由於模型訓練過程中的隨機性因素,如隨機初始化的權重參數,mini-batch,隨機梯度下降優化算法等,造成模型在數據集上的結果有一定的浮動,如准確率(accuracy)能達到1.5%的浮動,而AUC則有3.4%的浮動;
- 詞向量是使用word2vec還是GloVe,對實驗結果有一定的影響,具體哪個更好依賴於任務本身;
- Filter的大小對模型性能有較大的影響,並且Filter的參數應該是可以更新的;
- Feature Map的數量也有一定影響,但是需要兼顧模型的訓練效率;
- 1-max pooling的方式已經足夠好了,相比於其他的pooling方式而言;
- 正則化的作用微乎其微。
Ye Zhang等人給予模型調參者的建議如下:
- 使用
non-static
版本的word2vec
或者GloVe
要比單純的one-hot representation
取得的效果好得多; - 為了找到最優的過濾器(Filter)大小,可以使用線性搜索的方法。通常過濾器的大小范圍在
1-10
之間,當然對於長句,使用更大的過濾器也是有必要的; Feature Map
的數量在100-600
之間;- 可以盡量多嘗試激活函數,實驗發現
ReLU
和tanh
兩種激活函數表現較佳; - 使用簡單的
1-max pooling
就已經足夠了,可以沒必要設置太復雜的pooling方式; - 當發現增加
Feature Map
的數量使得模型的性能下降時,可以考慮增大正則的力度,如調高dropout
的概率; - 為了檢驗模型的性能水平,多次反復的交叉驗證是必要的,這可以確保模型的高性能並不是偶然。
論文附錄中還附上了各種調參結果,感興趣的可以前往閱讀之。
TextCNN詳細過程:第一層是圖中最左邊的7乘5的句子矩陣,每行是詞向量,維度=5,這個可以類比為圖像中的原始像素點了。然后經過有 filter_size=(2,3,4) 的一維卷積層,每個filter_size 有兩個輸出 channel。第三層是一個1-max pooling層,這樣不同長度句子經過pooling層之后都能變成定長的表示了,最后接一層全連接的 softmax 層,輸出每個類別的概率。
特征:這里的特征就是詞向量,有靜態(static)和非靜態(non-static)方式。static方式采用比如word2vec預訓練的詞向量,訓練過程不更新詞向量,實質上屬於遷移學習了,特別是數據量比較小的情況下,采用靜態的詞向量往往效果不錯。non-static則是在訓練過程中更新詞向量。推薦的方式是 non-static 中的 fine-tunning方式,它是以預訓練(pre-train)的word2vec向量初始化詞向量,訓練過程中調整詞向量,能加速收斂,當然如果有充足的訓練數據和資源,直接隨機初始化詞向量效果也是可以的。
通道(Channels):圖像中可以利用 (R, G, B) 作為不同channel,而文本的輸入的channel通常是不同方式的embedding方式(比如 word2vec或Glove),實踐中也有利用靜態詞向量和fine-tunning詞向量作為不同channel的做法。
一維卷積(conv-1d):圖像是二維數據,經過詞向量表達的文本為一維數據,因此在TextCNN卷積用的是一維卷積。一維卷積帶來的問題是需要設計通過不同 filter_size 的 filter 獲取不同寬度的視野。
Pooling層:利用CNN解決文本分類問題的文章還是很多的,比如這篇 A Convolutional Neural Network for Modelling Sentences 最有意思的輸入是在 pooling 改成 (dynamic) k-max pooling ,pooling階段保留 k 個最大的信息,保留了全局的序列信息。比如在情感分析場景,舉個例子:
“ 我覺得這個地方景色還不錯,但是人也實在太多了 ”
雖然前半部分體現情感是正向的,全局文本表達的是偏負面的情感,利用 k-max pooling能夠很好捕捉這類信息。