任務:
- 卷積運算的定義、動機(稀疏權重、參數(shù)共享、等變表示)。一維卷積運算和二維卷積運算。
- 反卷積(tf.nn.conv2d_transpose)
- 池化運算的定義、種類(最大池化、平均池化等)、動機。
- Text-CNN的原理。
- 利用Text-CNN模型來進行文本分類。
文本分類實現(xiàn)數(shù)據(jù)集處理流程圖
-
數(shù)據(jù)處理步驟+每一步處理的目的如下圖所示:
文本分類實現(xiàn)數(shù)據(jù)集處理流程圖 - 數(shù)據(jù)處理部分實現(xiàn)
- 讀數(shù)據(jù)
其中數(shù)據(jù)是已經(jīng)jiebe分詞+去停用詞后的結果
def read_file(path):
with open(path, 'r', encoding="UTF-8") as f:
data = []
labels = []
for line in f:
if line.split('\t')[1] == '':
continue
data.append(line.split('\t')[0])
labels.append(line.split('\t')[1])
return data, labels
# 讀文件
data, labels = read_file('E:/task6/merge.txt')
- 利用tokenizer將文字轉換為數(shù)字特征
- 將文字轉換為數(shù)字特征要將整個數(shù)據(jù)集放入,這樣保證訓練集、驗證集、測試集文字編碼一致
- 其中同時返回對應單詞和數(shù)字的映射關系,以便在轉化詞向量過程中獲取詞向量列表
def get_tokenizer(data):
tokenizer = Tokenizer(num_words=None)
tokenizer.fit_on_texts(data)
text_seq = tokenizer.texts_to_sequences(data)
# 對應的單詞和數(shù)字的映射關系
word_index = tokenizer.word_index
index_word = tokenizer.index_word
return word_index, index_word, text_seq
# 利用tokenizer將文字轉換為數(shù)字特征
word_index, index_word, X_train_text_seq = get_tokenizer(data)
- FastText Embedding
def get_fasttext_voc(data, word_index):
'''
利用fasttext獲取詞向量
'''
fasttext_model = FastText([data],
size=FASTEXT_SIZE, # 需要學習的嵌入大小(默認為100)
window=3, # 上下文窗口大小(默認5)
min_count=1, # 忽略出現(xiàn)次數(shù)低于此值的單詞(默認值5)
iter=10, # epoch(默認5)
min_n = 3, # char ngram的最小長度(默認值3)
max_n = 6, # char ngram的最大長度(默認值6)
word_ngrams = 0) # 如果為1,使用子單詞(n-grams)信息豐富單詞向量。如果是0,這就相當于Word2Vec
# 獲取詞向量列表
wordEmbedding = np.zeros((len(word_index) + 1, FASTEXT_SIZE))
for word, i in word_index.items():
if word in fasttext_model:
wordEmbedding[i] = fasttext_model[word]
return wordEmbedding
# fasttext embedding
wordEmbedding = get_fasttext_voc(data, word_index)
- 讓每個文本長度相同
# 讓每個文本長度相同
X_train_text_seq = pad_sequences(X_train_text_seq, maxlen=LEN_WORDS, padding='post', truncating='post')
- 劃分數(shù)據(jù)集
# 劃分數(shù)據(jù)集
X_train, X_test, y_train, y_test = train_test_split(X_train_text_seq,
labels,
test_size = 0.2,
random_state=33)
- 對類別變量編碼
def get_label_num(data):
data = [i.replace('\n', '') for i in data]
y_labels = list(set(data))
le = preprocessing.LabelEncoder()
le.fit(y_labels)
num_labels = len(y_labels)
data_labels = to_categorical([le.transform([x])[0] for x in data], num_labels)
return data_labels
# 對類別變量編碼
y_train_label = get_label_num(y_train)
y_test_label = get_label_num(y_test)
CNN
實現(xiàn)基礎版CNN:
LeNet-5 是卷積神經(jīng)網(wǎng)絡的作者Yann LeCun用于MNIST識別任務提出的模型。模型很簡單,就是卷積池化層的堆疊,最后加上幾層全連接層。將其運用在文本分類任務中。
其中模型結構如下所示:
實現(xiàn)過程如下:
def model_CNN(X_train, X_test, y_train, y_test, index_word, embedding_matrix):
'''
模型結構:
嵌入層:將詞編碼數(shù)據(jù)轉換為固定尺寸的稠密向量,同時把詞向量矩陣加載到Embedding層
卷積池化層:256 * 3 * 3
卷積池化層:128 * 3 * 3
Dropout:0.1
BatchNormalization: 批量標準化層,在每一個批次的數(shù)據(jù)中標準化前一層的激活項
全連接:256,'relu'
分類器:2, 'softmax'
'''
model = Sequential()
model.add(Embedding(len(index_word) + 1, # imput_dim: 詞匯表大小,即最大整數(shù)index+1
FASTEXT_SIZE, # output_dim: 詞向量的維度
weights=[embedding_matrix], # 加載詞向量矩陣
input_length=LEN_WORDS, # input_lenth: 輸入序列的長度
trainable=False)) # 設置trainable=False使得這個編碼層不可再訓練
# filters:輸出空間的維度,kernel_size: 1D 卷積窗口的長度,padding:"same" 表示填充輸入以使輸出具有與原始輸入相同的長度
model.add(Conv1D(256, 3, padding='same'))
# pool_size:最大池化的窗口大小, strides:作為縮小比例的因數(shù)
model.add(MaxPooling1D(3, 3, padding='same'))
model.add(Conv1D(128, 3, padding='same'))
model.add(MaxPooling1D(3, 3, padding='same'))
model.add(Conv1D(64, 3, padding='same'))
model.add(Flatten())
# rate: 在 0 和 1 之間浮動。需要丟棄的輸入比例
model.add(Dropout(0.1))
model.add(BatchNormalization())
# units: 正整數(shù),輸出空間維度
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.1))
model.add(Dense(2, activation='softmax'))
# 配置訓練模型
model.compile(loss='categorical_crossentropy', # 表示目標應該是分類格式的
optimizer='adam', # 隨機優(yōu)化的一種方法
metrics=['accuracy'] # 模型評估標準
)
# 給定數(shù)量的迭代訓練模型
model.summary()
model.fit(X_train, y_train,
batch_size=32,
epochs=15,
validation_data=(X_test, y_test))
Text-CNN
- 原理
Yoon Kim在2014年 “Convolutional Neural Networks for Sentence Classification” 論文中提出TextCNN(利用卷積神經(jīng)網(wǎng)絡對文本進行分類的算法)論文翻譯版,其中網(wǎng)絡結構如下所示:
假設我們有一些句子需要對其進行分類。句子中每個詞是由n維詞向量組成的,也就是說輸入矩陣大小為m*n,其中m為句子長度。CNN需要對輸入樣本進行卷積操作,對于文本數(shù)據(jù),filter不再橫向滑動,僅僅是向下移動,有點類似于N-gram在提取詞與詞間的局部相關性。圖中共有三種步長策略,分別是2,3,4,每個步長都有兩個filter(實際訓練時filter數(shù)量會很多)。在不同詞窗上應用不同filter,最終得到6個卷積后的向量。然后對每一個向量進行最大化池化操作并拼接各個池化值,最終得到這個句子的特征表示,將這個句子向量丟給分類器進行分類,至此完成整個流程。
其中每個層的作用,參照:https://blog.csdn.net/asialee_bird/article/details/88813385
模型結構如下所示:

TextCNN網(wǎng)絡結構
實現(xiàn)過程如下:
def model_TextCNN(X_train, X_test, y_train, y_test, index_word, embedding_matrix):
'''
模型結構:
詞嵌入,
卷積池化 * 3:256 * 3 * 4
拼接三個模型的輸出向量,
全連接,
Dropout,
全連接
'''
# shape: 一個尺寸元組(整數(shù))表明期望的輸入是按批次的LEN_WORDS維向量
main_input = Input(shape=(LEN_WORDS, ), dtype='float32')
embed = Embedding(len(index_word) + 1,
FASTEXT_SIZE,
weights=[embedding_matrix],
input_length=LEN_WORDS,
trainable=False)(main_input)
# 詞窗大小分別為3,4,5
# strides指明卷積的步長
cnn1 = Conv1D(256, 3, padding='same', strides=1, activation='relu')(embed)
cnn1 = MaxPooling1D(pool_size=4)(cnn1)
cnn2 = Conv1D(256, 4, padding='same', strides=1, activation='relu')(embed)
cnn2 = MaxPooling1D(pool_size=4)(cnn2)
cnn3 = Conv1D(256, 5, padding='same', strides=1, activation='relu')(embed)
cnn3 = MaxPooling1D(pool_size=4)(cnn3)
# 合并三個模型的輸出向量
cnn = concatenate([cnn1,cnn2,cnn3], axis=-1)
flat = Flatten()(cnn)
drop = Dropout(0.1)(flat)
main_output = Dense(2, activation='softmax')(drop)
model = Model(inputs=main_input, output=main_output)
# 配置訓練模型
model.compile(loss='categorical_crossentropy', # 表示目標應該是分類格式的
optimizer='adam', # 隨機優(yōu)化的一種方法
metrics=['accuracy'] # 模型評估標準
)
# 給定數(shù)量的迭代訓練模型
model.summary()
model.fit(X_train, y_train,
batch_size=32,
epochs=15,
validation_data=(X_test, y_test))
完整代碼見:github
參考資料:


