TextCNN文本分類(keras實現)

前言:

深度學習模型在計算機視覺語音識別方面取得了卓越的成就,在 NLP 領域也是可以的。將卷積神經網絡CNN應用到文本分類任務,利用多個不同size的kernel來提取句子中的關鍵信息(類似 n-gram 的關鍵信息,從而能夠更好地捕捉局部相關性。

文本分類是自然語言處理領域最活躍的研究方向之一,目前文本分類在工業界的應用場景非常普遍,從新聞的分類、商品評論信息的情感分類到微博信息打標籤輔助推薦系統,瞭解文本分類技術是NLP初學者比較好的切入點,較簡單且應用場景高頻。

一、論文筆記

1、Yoon Kim在2014年 “Convolutional Neural Networks for Sentence Classification” 論文中提出TextCNN(利用卷積神經網絡對文本進行分類的算法)(該論文翻譯)。

上圖很好地詮釋了模型的框架。假設我們有一些句子需要對其進行分類。句子中每個詞是由n維詞向量組成的,也就是說輸入矩陣大小爲m*n,其中m爲句子長度。CNN需要對輸入樣本進行卷積操作,對於文本數據,filter不再橫向滑動,僅僅是向下移動,有點類似於N-gram在提取詞與詞間的局部相關性。圖中共有三種步長策略,分別是2,3,4,每個步長都有兩個filter(實際訓練時filter數量會很多)。在不同詞窗上應用不同filter,最終得到6個卷積後的向量。然後對每一個向量進行最大化池化操作並拼接各個池化值,最終得到這個句子的特徵表示,將這個句子向量丟給分類器進行分類,至此完成整個流程。

(1)嵌入層(Embedding Layer)

通過一個隱藏層, 將 one-hot 編碼的詞投影到一個低維空間中,本質上是特徵提取器,在指定維度中編碼語義特徵。 這樣, 語義相近的詞, 它們的歐氏距離或餘弦距離也比較近。(作者使用的單詞向量是預訓練的,方法爲fasttext得到的單詞向量,當然也可以使用word2vec和GloVe方法訓練得到的單詞向量)。

(2)卷積層(Convolution Laye)

在處理圖像數據時,CNN使用的卷積核的寬度和高度的一樣的,但是在text-CNN中,卷積核的寬度是與詞向量的維度一致!這是因爲我們輸入的每一行向量代表一個詞,在抽取特徵的過程中,詞做爲文本的最小粒度。而高度和CNN一樣,可以自行設置(通常取值2,3,4,5),高度就類似於n-gram了。由於我們的輸入是一個句子,句子中相鄰的詞之間關聯性很高,因此,當我們用卷積核進行卷積時,不僅考慮了詞義而且考慮了詞序及其上下文(類似於skip-gram和CBOW模型的思想)。

(3)池化層(Pooling Layer)

因爲在卷積層過程中我們使用了不同高度的卷積核,使得我們通過卷積層後得到的向量維度會不一致,所以在池化層中,我們使用1-Max-pooling對每個特徵向量池化成一個值,即抽取每個特徵向量的最大值表示該特徵,而且認爲這個最大值表示的是最重要的特徵。當我們對所有特徵向量進行1-Max-Pooling之後,還需要將每個值給拼接起來。得到池化層最終的特徵向量。在池化層到全連接層之前可以加上dropout防止過擬合。

(4)全連接層(Fully connected layer)

全連接層跟其他模型一樣,假設有兩層全連接層,第一層可以加上’relu’作爲激活函數,第二層則使用softmax激活函數得到屬於每個類的概率。

(5)TextCNN的小變種

在詞向量構造方面可以有以下不同的方式: CNN-rand: 隨機初始化每個單詞的詞向量通過後續的訓練去調整。 CNN-static: 使用預先訓練好的詞向量,如word2vec訓練出來的詞向量,在訓練過程中不再調整該詞向量。 CNN-non-static: 使用預先訓練好的詞向量,並在訓練過程進一步進行調整。 CNN-multichannel: 將static與non-static作爲兩通道的詞向量。

(6)參數與超參數
sequence_length (
Q: 對於CNN, 輸入與輸出都是固定的,可每個句子長短不一, 怎麼處理? A: 需要做定長處理, 比如定爲n, 超過的截斷, 不足的補0. 注意補充的0對後面的結果沒有影響,因爲後面的max-pooling只會輸出最大值,補零的項會被過濾掉)
num_classes (多分類, 分爲幾類)
vocabulary_size (語料庫的詞典大小, 記爲|D|)
embedding_size (將詞向量的維度, 由原始的 |D| 降維到 embedding_size)
filter_size_arr (多個不同size的filter)

2、2015年“A Sensitivity Analysis of (and Practitioners' Guide to) Convolutional Neural Networks for Sentence Classification”論文詳細地闡述了關於TextCNN模型的調參心得。

(1)TextCNN詳細過程:

  • Embedding:第一層是圖中最左邊的7乘5的句子矩陣,每行是詞向量,維度=5,這個可以類比爲圖像中的原始像素點。
  • Convolution:然後經過 kernel_sizes=(2,3,4) 的一維卷積層,每個kernel_size 有兩個輸出 channel。
  • MaxPolling:第三層是一個1-max pooling層,這樣不同長度句子經過pooling層之後都能變成定長的表示。
  • FullConnection and Softmax:最後接一層全連接的 softmax 層,輸出每個類別的概率。

(2)論文調參結論:

  • 使用預訓練的word2vec 、 GloVe初始化效果會更好。一般不直接使用One-hot。
  • 卷積核的大小影響較大,一般取1~10,對於句子較長的文本,則應選擇大一些。
  • 卷積核的數量也有較大的影響,一般取100~600 ,同時一般使用Dropout(0~0.5)。
  • 激活函數一般選用ReLU 和 tanh。
  • 池化使用1-max pooling。
  • 隨着feature map數量增加,性能減少時,試着嘗試大於0.5的Dropout。
  • 評估模型性能時,記得使用交叉驗證。

二、Keras文本預處理

1、讀取數據集

2、使用Tokenizer將文字轉換成數字特徵

使用Keras的Tokenizer模塊實現轉換。當我們創建了一個Tokenizer對象後,使用該對象的fit_on_texts()函數,可以將輸入的文本中的每個詞編號,編號是根據詞頻的,詞頻越大,編號越小。使用word_index屬性可以看到每次詞對應的編碼。

3、將數據集中的每條文本轉換爲數字列表,使用每個詞的編號進行編號

使用該對象的texts_to_sequences()函數,將每條文本轉變成一個向量。 

4、使用pad_sequences()讓每句數字影評長度相同

由於每句話的長度不唯一,需要將每句話的長度設置一個固定值。將超過固定值的部分截掉,不足的在最前面用0填充。

5、使用Embedding層將每個詞編碼轉換爲詞向量

Embedding層基於上文所得的詞編碼,對每個詞進行one-hot編碼,每個詞都會是一個vocabulary_size維的向量;然後通過神經網絡的訓練迭代更新得到一個合適的權重矩陣(具體實現過程可以參考skip-gram模型),行大小爲vocabulary_size,列大小爲詞向量的維度,將本來以one-hot編碼的詞向量映射到低維空間,得到低維詞向量。需要聲明一點的是Embedding層是作爲模型的第一層,在訓練模型的同時,得到該語料庫的詞向量。當然,也可以使用已經預訓練好的詞向量表示現有語料庫中的詞。

文本預處理目的:將每個樣本轉換爲一個數字矩陣,矩陣的每一行表示一個詞向量。

Keras文本預處理代碼實現:

from sklearn.model_selection import train_test_split
import pandas as pd
import jieba
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences

if __name__=='__main__':
    dataset = pd.read_csv('sentiment_analysis/data_train.csv', sep='\t',names=['ID', 'type', 'review', 'label']).astype(str)
    cw = lambda x: list(jieba.cut(x))
    dataset['words'] = dataset['review'].apply(cw)
    tokenizer=Tokenizer()  #創建一個Tokenizer對象
    #fit_on_texts函數可以將輸入的文本中的每個詞編號,編號是根據詞頻的,詞頻越大,編號越小
    tokenizer.fit_on_texts(dataset['words'])
    vocab=tokenizer.word_index #得到每個詞的編號
    x_train, x_test, y_train, y_test = train_test_split(dataset['words'], dataset['label'], test_size=0.1)
    # 將每個樣本中的每個詞轉換爲數字列表,使用每個詞的編號進行編號
    x_train_word_ids=tokenizer.texts_to_sequences(x_train)
    x_test_word_ids = tokenizer.texts_to_sequences(x_test)
    #序列模式
    # 每條樣本長度不唯一,將每條樣本的長度設置一個固定值
    x_train_padded_seqs=pad_sequences(x_train_word_ids,maxlen=50) #將超過固定值的部分截掉,不足的在最前面用0填充
    x_test_padded_seqs=pad_sequences(x_test_word_ids, maxlen=50)

三、基於keras的TextCNN模型的構建、訓練與測試

1、基礎版CNN(模仿LeNet-5)

LeNet-5是卷積神經網絡的作者Yann LeCun用於MNIST識別任務提出的模型。模型很簡單,就是卷積池化層的堆疊,最後加上幾層全連接層。將其運用在文本分類任務中。

#構建CNN分類模型(LeNet-5)
#模型結構:嵌入-卷積池化*2-dropout-BN-全連接-dropout-全連接
def CNN_model(x_train_padded_seqs, y_train, x_test_padded_seqs, y_test):
    model = Sequential()
    model.add(Embedding(len(vocab) + 1, 300, input_length=50)) #使用Embeeding層將每個詞編碼轉換爲詞向量
    model.add(Conv1D(256, 5, padding='same'))
    model.add(MaxPooling1D(3, 3, padding='same'))
    model.add(Conv1D(128, 5, padding='same'))
    model.add(MaxPooling1D(3, 3, padding='same'))
    model.add(Conv1D(64, 3, padding='same'))
    model.add(Flatten())
    model.add(Dropout(0.1))
    model.add(BatchNormalization())  # (批)規範化層
    model.add(Dense(256, activation='relu'))
    model.add(Dropout(0.1))
    model.add(Dense(3, activation='softmax'))
    model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    one_hot_labels = keras.utils.to_categorical(y_train, num_classes=3)  # 將標籤轉換爲one-hot編碼
    model.fit(x_train_padded_seqs, one_hot_labels,epochs=5, batch_size=800)
    y_predict = model.predict_classes(x_test_padded_seqs)  # 預測的是類別,結果就是類別號
    y_predict = list(map(str, y_predict))
    print('準確率', metrics.accuracy_score(y_test, y_predict))
    print('平均f1-score:', metrics.f1_score(y_test, y_predict, average='weighted'))

2、簡單版TextCNN

#構建TextCNN模型
#模型結構:詞嵌入-卷積池化*3-拼接-全連接-dropout-全連接
def TextCNN_model_1(x_train_padded_seqs,y_train,x_test_padded_seqs,y_test):
    main_input = Input(shape=(50,), dtype='float64')
    # 詞嵌入(使用預訓練的詞向量)
    embedder = Embedding(len(vocab) + 1, 300, input_length=50, trainable=False)
    embed = embedder(main_input)
    # 詞窗大小分別爲3,4,5
    cnn1 = Conv1D(256, 3, padding='same', strides=1, activation='relu')(embed)
    cnn1 = MaxPooling1D(pool_size=48)(cnn1)
    cnn2 = Conv1D(256, 4, padding='same', strides=1, activation='relu')(embed)
    cnn2 = MaxPooling1D(pool_size=47)(cnn2)
    cnn3 = Conv1D(256, 5, padding='same', strides=1, activation='relu')(embed)
    cnn3 = MaxPooling1D(pool_size=46)(cnn3)
    # 合併三個模型的輸出向量
    cnn = concatenate([cnn1, cnn2, cnn3], axis=-1)
    flat = Flatten()(cnn)
    drop = Dropout(0.2)(flat)
    main_output = Dense(3, activation='softmax')(drop)
    model = Model(inputs=main_input, outputs=main_output)
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

    one_hot_labels = keras.utils.to_categorical(y_train, num_classes=3)  # 將標籤轉換爲one-hot編碼
    model.fit(x_train_padded_seqs, one_hot_labels, batch_size=800, epochs=10)
    #y_test_onehot = keras.utils.to_categorical(y_test, num_classes=3)  # 將標籤轉換爲one-hot編碼
    result = model.predict(x_test_padded_seqs)  # 預測樣本屬於每個類別的概率
    result_labels = np.argmax(result, axis=1)  # 獲得最大概率對應的標籤
    y_predict = list(map(str, result_labels))
    print('準確率', metrics.accuracy_score(y_test, y_predict))
    print('平均f1-score:', metrics.f1_score(y_test, y_predict, average='weighted'))

3、使用Word2Vec詞向量的TextCNN

w2v_model=Word2Vec.load('sentiment_analysis/w2v_model.pkl')
# 預訓練的詞向量中沒有出現的詞用0向量表示
embedding_matrix = np.zeros((len(vocab) + 1, 300))
for word, i in vocab.items():
    try:
        embedding_vector = w2v_model[str(word)]
        embedding_matrix[i] = embedding_vector
    except KeyError:
        continue

#構建TextCNN模型
def TextCNN_model_2(x_train_padded_seqs,y_train,x_test_padded_seqs,y_test,embedding_matrix):
    # 模型結構:詞嵌入-卷積池化*3-拼接-全連接-dropout-全連接
    main_input = Input(shape=(50,), dtype='float64')
    # 詞嵌入(使用預訓練的詞向量)
    embedder = Embedding(len(vocab) + 1, 300, input_length=50, weights=[embedding_matrix], trainable=False)
    #embedder = Embedding(len(vocab) + 1, 300, input_length=50, trainable=False)
    embed = embedder(main_input)
    # 詞窗大小分別爲3,4,5
    cnn1 = Conv1D(256, 3, padding='same', strides=1, activation='relu')(embed)
    cnn1 = MaxPooling1D(pool_size=38)(cnn1)
    cnn2 = Conv1D(256, 4, padding='same', strides=1, activation='relu')(embed)
    cnn2 = MaxPooling1D(pool_size=37)(cnn2)
    cnn3 = Conv1D(256, 5, padding='same', strides=1, activation='relu')(embed)
    cnn3 = MaxPooling1D(pool_size=36)(cnn3)
    # 合併三個模型的輸出向量
    cnn = concatenate([cnn1, cnn2, cnn3], axis=-1)
    flat = Flatten()(cnn)
    drop = Dropout(0.2)(flat)
    main_output = Dense(3, activation='softmax')(drop)
    model = Model(inputs=main_input, outputs=main_output)
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

    one_hot_labels = keras.utils.to_categorical(y_train, num_classes=3)  # 將標籤轉換爲one-hot編碼
    model.fit(x_train_padded_seqs, one_hot_labels, batch_size=800, epochs=20)
    #y_test_onehot = keras.utils.to_categorical(y_test, num_classes=3)  # 將標籤轉換爲one-hot編碼
    result = model.predict(x_test_padded_seqs)  # 預測樣本屬於每個類別的概率
    result_labels = np.argmax(result, axis=1)  # 獲得最大概率對應的標籤
    y_predict = list(map(str, result_labels))
    print('準確率', metrics.accuracy_score(y_test, y_predict))
    print('平均f1-score:', metrics.f1_score(y_test, y_predict, average='weighted'))

四、使用keras的plot_model()畫出的TextCNN模型結構圖

1、環境配置

(1)安裝graphviz模塊

首先,命令行pip install graphviz;其次,安裝graphviz軟件,官網下載:graphviz-2.38.msi ;最後,將安裝目錄中的graphviz-2.38\release\bin添加進Path環境變量

(2)安裝pydot模塊

命令行pip install pydot

(3)在運行程序中加入下面兩行代碼

import os
os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'

2、使用plot_model()畫出模型圖

from keras.utils import plot_model
    #生成一個模型圖,第一個參數爲模型,第二個參數爲要生成圖片的路徑及文件名,還可以指定兩個參數:
    #show_shapes:指定是否顯示輸出數據的形狀,默認爲False
    #show_layer_names:指定是否顯示層名稱,默認爲True
    plot_model(model,to_file='sentiment_analysis/model.png',show_shapes=True,show_layer_names=False)

模型圖如下:

五、keras模型的保存與加載

from keras.models import load_model

#模型的保存
model.save('model.h5')

#模型的加載
model=load_model('model.h5')

 

 

 

 

 

參考學習資料:

(1)Keras之文本分類實現

(2)使用Keras進行深度學習

(3)NLP論文

(4)卷積神經網絡(CNN)在句子建模上的應用

(5)用深度學習(CNN RNN Attention)解決大規模文本分類問題 - 綜述和實踐

(6)深度學習在文本分類中的應用

(7)深度學習與文本分類總結第一篇--常用模型總結

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章