本文主要內容
- 簡略介紹卷積神經網絡(CNN, Convolutional Neural Network)處理文本信息的過程
- 使用CNN進行文本分類任務,並對代碼進行註釋
- 本文代碼【https://github.com/540117253/Chinese-Text-Classification 】
一、CNN概述
CNN文本編碼器的結構如圖1所示。在第一層,詞映射函數將評論的每個單詞映射爲維向量,然後將給定的評論文本轉化爲長度固定爲的詞嵌入矩陣(只截取評論文本中的前個單詞,如果長度不足的文本則進行填充處理)。
詞映射層後的是卷積層,其包含個神經元,每個神經元對應的卷積核用於對詞向量進行卷積運算提取特徵。假設是文本長度爲的詞嵌入矩陣,第個神經元產生的特徵爲:
其中爲偏倚項,表示卷積運算,是非線性激活函數。
最終在滑動窗口的作用下,第個神經元產生的特徵爲。將該特徵進行max-pooling運算,其主要用於捕獲擁有最大值的最重要的特徵,其定義爲:
最後卷積層的最終輸出爲個神經元輸出的拼接結果,其定義爲:
通常會接着送入全連接層,其中包含權重矩陣和偏置項,具體公式爲:
整體上,CNN的卷積核大小一般爲3或者5(即一次卷積運算僅計算3個單詞或5個單詞的信息),其僅採用一個卷積核就能通過滑動窗口的方式來掃描整個文本,因此整個文本可以看作是共享同一個卷積核的一組參數,能很好地節省內存空間。然而一次卷積運算僅能包含卷積窗口內的單詞,當輸入的文本越長,卷積窗口滑動到文本的尾部時所丟失掉的前文信息就越多。因此對於文本數據,一般採用循環神經網絡RNN,其比CNN的文本信息提取能力更優秀。
二、CNN文本分類實例
2.1 數據集介紹
1. 下載地址:
【https://github.com/skdjfla/toutiao-text-classfication-dataset 】
2. 格式:
6552431613437805063_!_102_!_news_entertainment_!_謝娜爲李浩菲澄清網絡謠言,之後她的兩個行爲給自己加分_!_佟麗婭,網絡謠言,快樂大本營,李浩菲,謝娜,觀衆們
每行爲一條數據,以_!_分割的個字段,從前往後分別是 新聞ID,分類code(見下文),分類名稱(見下文),新聞字符串(僅含標題),新聞關鍵詞
分類code與名稱:
100 民生 故事 news_story
101 文化 文化 news_culture
102 娛樂 娛樂 news_entertainment
103 體育 體育 news_sports
104 財經 財經 news_finance
106 房產 房產 news_house
107 汽車 汽車 news_car
108 教育 教育 news_edu
109 科技 科技 news_tech
110 軍事 軍事 news_military
112 旅遊 旅遊 news_travel
113 國際 國際 news_world
114 證券 股票 stock
115 農業 三農 news_agriculture
116 電競 遊戲 news_game
2.2 預訓練詞向量
預訓練詞向量使用的是,基於ACL-2018模型在百度百科訓練的詞向量。
下載地址:【 https://github.com/Embedding/Chinese-Word-Vectors 】
2.3 數據預處理
- 清除無用字符,並且進行分詞處理
- 建立整個數據集的字典,key=word, value=詞語的編號
- 對進行截斷或補0處理,確保每條樣本的長度爲maxlen
- 序列化樣本的標籤,例如“體育類新聞”的類別編號爲1,“娛樂類新聞”的類別編號爲2
- 將處理好的數據轉化爲DataFrame格式,並保存到硬盤
2.4 CNN模型的定義
'''
Text => CNN => Fully_Connected => Softmax
參數:
filter_sizes:卷積核的大小
num_filters:卷積核的個數
embedded_size:詞向量的維度
dict_size:數據集的單詞個數
maxlen:每條樣本的最大單詞數
label_num:樣本類別的數量
learning_rate:梯度優化器的初始學習率
'''
class CNN:
def __init__(self, filter_sizes, num_filters, embedded_size,
dict_size, maxlen, label_num, learning_rate):
# print('model_Name:', 'CNN')
self.droput_rate = 0.5
'''
Convulutional Neural Network
'''
def cnn (input_emb, filter_sizes, num_filters):
pooled_outputs = []
for i, filter_size in enumerate(filter_sizes):
filter_shape = [filter_size, embedded_size, 1, num_filters]
W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")
b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")
conv = tf.nn.conv2d(
input_emb,
W,
strides=[1, 1, 1, 1],
padding="VALID",
name="conv") # shape(conv) = [None, sequence_length - filter_size + 1, 1, num_filters]
h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
word_num = input_emb.shape.as_list()[1]
pooled = tf.nn.max_pool(
h,
ksize=[1, word_num - filter_size + 1, 1, 1],
strides=[1, 1, 1, 1],
padding='VALID',
name="pool") # shape(pooled) = [None, 1, 1, num_filters]
pooled_outputs.append(pooled)
num_filters_total = num_filters * len(filter_sizes)
h_pool = tf.concat(pooled_outputs,3)
h_pool_flat = tf.reshape(h_pool, [-1, num_filters_total]) # shape = [None,num_filters_total]
cnn_fea = tf.nn.dropout(h_pool_flat, keep_prob = self.droput_rate)
return cnn_fea
self.X = tf.placeholder(tf.int32, [None, maxlen], name='input_x')
self.Y = tf.placeholder(tf.int64, [None])
self.encoder_embeddings = tf.Variable(tf.random_uniform([dict_size, embedded_size], -1, 1), trainable=False)
encoder_embedded = tf.nn.embedding_lookup(self.encoder_embeddings, self.X)
# 由於conv2d需要一個四維的輸入數據,因此需要手動添加一個維度。
encoder_embedded = tf.expand_dims(encoder_embedded, -1) # shape(encoder_embedded) = [None, user_review_num*u_n_words, embedding_size, 1]
outputs = cnn(input_emb = encoder_embedded, filter_sizes = filter_sizes, num_filters = num_filters)
self.logits = keras.layers.Dense(label_num, use_bias=True)(outputs)
self.probability = tf.nn.softmax(self.logits, name='probability')
self.cost = tf.nn.sparse_softmax_cross_entropy_with_logits(
labels = self.Y,
logits = self.logits)
self.cost = tf.reduce_mean(self.cost)
self.optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate).minimize(self.cost)
self.pre_y = tf.argmax(self.logits, 1, name='pre_y')
correct_pred = tf.equal(self.pre_y, self.Y)
self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
2.5 訓練模型
- 將預處理好的數據集切分爲80%的訓練集,10%作爲驗證集,10%作爲測試集
- 每使用一次訓練集進行訓練後,就使用驗證集進行測試。
- 當驗證集的準確率連續下降5次,就停止步驟2,然後使用測試集的結果作爲模型的最終性能。