卷積神經網絡文本分類

前言

卷積神經網絡(CNN)在圖像處理領域取得了很大的成績,它的卷積和池化結構能很好提取圖像的信息,而在 NLP 領域循環神經網絡(RNN)則使用的更多,RNN 及其各類變種因擁有記憶功能使得它們更擅長處理上下文。但 NLP 領域不少方面使用 CNN 取得了出色的效果,好比語義分析、查詢檢索、文本分類等任務。這篇文章看看如何用 CNN 進行文本分類。git

模型結構

模型結構能夠經過下圖一層層來看,總共由4部分組成,包括了輸入層、卷積層、池化層和全鏈接層。 github

這裏寫圖片描述

輸入層

圖中最左邊的部分即爲輸入層,總的來講輸入層就是句子對應的矩陣。通常不會使用 ont-hot 向量來表示單詞,而是使用 k 維的分佈式詞向量。那麼對於一個長度爲 n 的句子,則構成一個 n × k 的矩陣。bash

因此,能夠設 x_i 爲句子的第 i 個單詞,它爲 k 維向量。那麼一個句子爲 x_{1:n}=x_1 \bigoplus x_2 \bigoplus ...\bigoplus x_n,其中\bigoplus表示串聯的意思。網絡

另外,根據對詞向量的做用能夠分爲兩種模式:靜態和非靜態。靜態模式的意思就是我直接使用第三方發佈的詞向量或者本身訓練的詞向量來初始化矩陣,而且在每次訓練的過程當中不對反向偏差傳播產生做用,不改變詞向量,整個訓練過程詞向量都是固定不變的。而非靜態模式則不一樣,一樣是使用詞向量來初始化矩陣後,在此後的每次訓練過程當中,根據反向偏差傳播會對詞向量進行微調,整個訓練過程詞向量都會更新。併發

卷積層

圖中第二部分爲卷積層,卷積層的做用就是用於提取句子的特徵。主要是經過一個 h × k 的卷積核 w 在輸入層從上到下進行滑動進行卷積操做,經過該卷積操做獲得一個 feature map。feature map 的列爲1,行爲 (n-h+1),即 c=(c_1,c_2,...c_{n-h+1}),其中 c_i=f(w \cdot x_{i:i+h-1}+b)app

上圖中輸入層上紅色框框就是卷積操做的卷積核,能夠看到它是 2 × k 維的,運算後變爲 feature map 的一個元素。除此以外,還能夠將 h 定爲3,此時卷積核變爲 3 × k 維,如圖中黃色框框部分。相同維度的能夠有若干個參數不一樣的卷積核,因此最終在每種維度下均可以獲得若干個 feature map。dom

卷積操做的意義是什麼?能夠看到它實際上是根據 h 大小不一樣提取不一樣長度相鄰單詞的特徵,這個其實能夠跟 n-gram 語言模型對應起來。機器學習

池化層

圖中第三部分爲池化層,池化層的做用是對特徵作進一步提取,將最重要的特徵提取出來。這裏使用的是 max-over-time pooling 操做,即取出 feature map 中的最大值做爲最重要的特徵,即\widehat{c} = max\{c\}。因此最終對於每一個 feature map 池化後都獲得一個一維向量,取最大值做爲特徵也解決了不一樣句子長短的問題,儘管短的句子會用 0 進行填充,但經過取最大值消除了該問題。分佈式

前面的經過卷積層的多個不一樣卷積核操做獲得若干 feature map,而再通過池化層處理後獲得若干個一維向量。ide

全鏈接層

圖中最後部分爲全鏈接層,全鏈接層經過使用 softmax 分類器獲得各個分類的機率。前面的池化層的輸出以全鏈接的形式連到 softmax 層,softmax 層定義好分類。

防止過擬合

爲了防止過擬合,訓練過程當中在倒數第二層使用 dropout 技術,它將隨機丟棄隱含層的某些節點使其不 work。具體作法能夠在網絡向前傳輸時將一些節點設置爲0,好比倒數第二層上,z=[\widehat{c}_1,\widehat{c}_2...,\widehat{c}_m],這裏假設咱們有 m 個卷積核。經過 dropout 後爲,y=w\cdot(z\circ r)+b,其中\circ和 r 實現了掩碼的功能,即 r 是大小與 z 相同的一個向量,它的值爲隨機的0或1,0對應的節點爲丟棄的節點。

同時,還能夠在全鏈接層使用L2正則化來約束權值向量w。

主要實現代碼

構建圖

首先,構建須要的佔位符和常量,其中佔位符包括輸入佔位符、標籤佔位符和 dropout 佔位符,L2 正則損失常量。

train_inputs = tf.placeholder(tf.int32, [None, sequence_length])
train_labels = tf.placeholder(tf.float32, [None, classes_num])
keep_prob = tf.placeholder(tf.float32)
l2_loss = tf.constant(0.0)
複製代碼

接着咱們會須要一個嵌入層將詞彙嵌入到指定的維度空間上,維度由 embedding_size 指定。同時 vocabulary_size 爲詞彙大小,這樣就能夠將全部單詞都映射到指定的維數空間上。嵌入層經過tf.nn.embedding_lookup就能找到輸入對應的詞空間向量了,這裏稍微解釋下embedding_lookup操做,它會從詞彙中取到 inputs 每一個元素對應的詞向量,inputs 爲2維的話,經過該操做後變爲3維,由於已經將詞用 embedding_size 維向量表示了。此外,因爲要調用卷積操做,這裏將結果擴展了一維。

with tf.device('/cpu:0'):
    embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
    embed = tf.nn.embedding_lookup(embeddings, train_inputs)
    conv_inputs = tf.expand_dims(embed, -1)
複製代碼

接着開始作卷積操做及池化操做。因爲咱們會定義若干個卷積核,並且每一個高度的都有若干個卷積核,因此咱們會獲得不少不一樣的 feature map,而後對這些 feature map 作 max-over-time pooling 操做,最終獲得池化後的 feature。

features_pooled = []
for filter_height, filter_num in zip(filters_height, filter_num_per_height):
    conv_filter = tf.Variable(tf.truncated_normal([filter_height, embedding_size, 1, filter_num], stddev=0.1))
    conv = tf.nn.conv2d(conv_inputs, conv_filter, strides=[1, 1, 1, 1], padding="VALID")
    bias = tf.Variable(tf.constant(0.1, shape=[filter_num]))
    feature_map = tf.nn.relu(tf.nn.bias_add(conv, bias))
    feature_pooled = tf.nn.max_pool(feature_map, ksize=[1, sequence_length - filter_height + 1, 1, 1],
                                        strides=[1, 1, 1, 1],
                                        padding='VALID')
    features_pooled.append(feature_pooled)
複製代碼

如今網絡就剩下全鏈接層了,其中要先進行 dropout 操做來暫時使得一些節點失效,接着作線性計算獲得分數,從而獲得預測。

filter_num_total = sum(filter_num_per_height)
features_pooled_flat = tf.reshape(tf.concat(features_pooled, 3), [-1, filter_num_total])
features_pooled_flat_drop = tf.nn.dropout(features_pooled_flat, keep_prob)
W = tf.get_variable("W", shape=[filter_num_total, classes_num], initializer=tf.contrib.layers.xavier_initializer())
b = tf.Variable(tf.constant(0.1, shape=[classes_num]))
scores = tf.nn.xw_plus_b(features_pooled_flat_drop, W, b)
複製代碼

最後計算損失,一個是L2正則損失一個是交叉熵損失,經過二者求得總的損失。而且計算獲得準確率。

l2_loss += tf.nn.l2_loss(W)
l2_loss += tf.nn.l2_loss(b)
losses = tf.nn.softmax_cross_entropy_with_logits(logits=scores, labels=train_labels)
loss = tf.reduce_mean(losses) + l2_lambda * l2_loss
predictions = tf.argmax(scores, 1)
correct_predictions = tf.equal(predictions, tf.argmax(train_labels, 1))
accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"))
複製代碼

github

github附上完整代碼。

https://github.com/sea-boat/nlp_lab/tree/master/cnn_text_classify

reference

Convolutional Neural Networks for Sentence Classification

-------------推薦閱讀------------

個人2017文章彙總——機器學習篇

個人2017文章彙總——Java及中間件

個人2017文章彙總——深度學習篇

個人2017文章彙總——JDK源碼篇

個人2017文章彙總——天然語言處理篇

個人2017文章彙總——Java併發篇

------------------廣告時間----------------

知識星球:遠洋號

公衆號的菜單已分爲「分佈式」、「機器學習」、「深度學習」、「NLP」、「Java深度」、「Java併發核心」、「JDK源碼」、「Tomcat內核」等,可能有一款適合你的胃口。

爲何寫《Tomcat內核設計剖析》

歡迎關注:

這裏寫圖片描述
相關文章
相關標籤/搜索