CNNs for Text Classification

研究問題

關於Convlutional Neural Networks(CNNs)在天然語言處理領域的應用。python

這一次的研究主要是針對文本分類這一個問題所展開。網絡

研究方法

  1. 瞭解卷積神經網絡。
  2. 閱讀paper,深刻理解CNN在NLP中的模型細節。
  3. 類比CNN在CV中的應用,總結二者異同。
  4. 代碼實現

附加:

類比信號與系統,理解卷積的真正意義,體會信號系統與圖像處理中卷積模型所起的不一樣做用。app

擬採用的方法

CNNs的定義

卷積神經網絡本質上是由多個卷積層,通過激活函數後產生輸出的網絡。能夠將其大體劃分爲輸入層,卷積層,池化層,全鏈接層和輸出層。dom

卷積層

其中卷積層的工做,是經過定義一系列卷積核,依次掃過圖像中的每一個區域,產生一個新的特徵矩陣的過程。所以,卷積核在訓練完成後,更像是一個特徵過濾器,在掃過圖像的過程當中,忽略掉與特徵不相符的部分,而將符合這種特徵的部分放大化(一般是分配一個較大的權重)。ide

池化層

池化層一般會緊跟在卷積層以後,對卷積層所產生的特徵矩陣進行子採樣(多是去平均值,或者取最大值),最終獲得一個固定大小的矩陣。函數

池化層主要的做用有兩方面。google

第一,固定大小。經由卷積層產生的特徵矩陣一般是大小不定的,因此咱們沒法將它直接投入到全鏈接層進行後續工做,而池化層在這裏起到了「鏈接器」的做用,它將一個不定大小的矩陣轉化成了固定大小的矩陣,隨時能夠餵食到後續神經網絡。spa

第二,信息提取。池化層在下降輸出維度的同時,保留了原始特徵矩陣總最顯著的信息,並不會由於維度的下降而致使信息的丟失。code

全鏈接層

所謂全鏈接是指,本層全部的神經元都與下一層的神經元相連。由於相對簡單,這裏也再也不贅述。orm

總結

經過觀察下圖咱們能夠發現,一個CNNs會由多個卷積層,多個池化層,經由全鏈接層產生輸出。

全鏈接層起到的做用很容易理解,一般也只是分類,迴歸等任務。那麼介於原始圖像,和分類網絡之間的卷積層和池化層究竟起了什麼做用?

我認爲,它們兩層合起來能夠用「特徵提取層」來歸納,也就是說,卷積層和池化層實際上是一個特徵抽取,數據加工的過程,將原始的圖像通過一系列的處理,加工,才能產生易於分類的數據類型,而後進行分類工做。

CNNs在NLP中的模型

類比CNNs在圖像處理中的應用,NLP中的模型其實是將一個句子以圖的概念理解。

initialize

在初始化特徵矩陣時,句子中的每個單詞的word embeddings做爲矩陣的一行,獲得一個n*k的矩陣,其中n是句子中的單詞數,k是詞嵌入模型的維度,也能夠是one-hot模型,可是考慮到樣本的稀疏度,通常會採用低維的詞嵌入模型。

filter

在CV中,通常咱們的卷積核filters會掃過圖像的局部像素,但在NLP中,一般會只改變filters的長度,而寬度始終等於詞嵌入維度k,也就是說,filter的最小單位一整行,一個完整的word。而一般狀況下,會使用一個長度爲2~5的filter,這是由於實際狀況中,不多會出現5個詞以上的長組合短語。

pooling

CV中的池化操做一般會產生一個n * m的特徵矩陣,但在NLP中,產生的特徵矩陣會是一個n * 1的矩陣。

產生這種不一樣的緣由是由於,在圖像中,傳遞信息的是一個像素塊,而在天然語言處理中,傳遞信息的是一個word,圖像自己就是二維的,因此須要用二維去傳遞信息,可是對於詞而言,第二個維度k其實是詞嵌入的維度,並不會傳遞有關於這個句子的信息。

channel

CV處理中,咱們會遇到RGB圖分爲R,G,B三個channel分析的狀況。在NLP中,彷佛看上去一個channel就足夠來作分析。

可是在原始paper中提到,NLP中的CNNs模型也能夠有多個通道,通常是staticfine-tuned

static通道在backpropagation的過程當中是靜態的,保持不變的,可是fine-tuned通道會隨着訓練發生變化。

除了這兩個通道以外,咱們也能夠增長另外的通道,好比改變句子的語言,或者將句子替換成其餘同義句等等。

實驗

實驗部分用tensorflow實現了CNN-rand模型,數據集採用原paper中的MR數據集,這是一個關於電影評價的數據集,數據集將評價分爲了positive和negetive兩個部分。

因爲這個數據集自己較小,並且沒有dev,所以抽取了其中的10%做爲dev。

Embedding Layer

第一部分是embedding layer,將一個詞映射成低維word embedding。這裏沒有采用google的word2vec是由於這個模型自己較爲簡單,並且筆記本算力有限,因此本身構建了一個簡單的embedding layer。

with tf.device('/cpu:0'), tf.name_scope("embedding"):
    self.W = tf.Variable(
        tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),
        name="W")
    self.embedded_chars = tf.nn.embedding_lookup(self.W, self.input_x)
    self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)
複製代碼

W是一個隨機均勻分佈,維度爲vocab_size * embedding_size的初始化矩陣。

tf.nn.embedding_lookup方法建立了一個詞嵌入矩陣,它的維度和W一致,返回的類型爲[None, sequence_length, embedding_size]

tf.expand_dims(self.embedded_chars, -1)在返回值的最後一個維度插入1,變成[None, sequence_length, embedding_size, 1],這裏的1表明的通道數,在後續con2d卷積方法中會使用。

Convolution and Max-Pooling Layer

卷積和池化模型的創建。

由於是CNN-rand模型,因此通道數爲1,且採用窄卷積的方式進行卷積操做。

pooled_outputs = []
        for i, filter_size in enumerate(filter_sizes):
            with tf.name_scope("conv-maxpool-%s" % filter_size):
                # Convolution Layer
                filter_shape = [filter_size, embedding_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(
                    self.embedded_chars_expanded,
                    W,
                    strides=[1, 1, 1, 1],
                    padding="VALID",
                    name="conv")
                # Apply nonlinearity
                h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
                # Max-pooling
                pooled = tf.nn.max_pool(
                    h,
                    ksize=[1, sequence_length - filter_size + 1, 1, 1],
                    strides=[1, 1, 1, 1],
                    padding='VALID',
                    name="pool")
                pooled_outputs.append(pooled)

        # Combine all the pooled features
        num_filters_total = num_filters * len(filter_sizes)
        self.h_pool = tf.concat(pooled_outputs, 3)
        self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])
複製代碼

這裏的filter_size = "3,4,5",是採用長度分別爲3,4,5的三種filter對詞嵌入矩陣進行卷積。

W是卷積核,用截斷正態分佈進行初始化。b是偏置數,h爲卷積輸出通過激活函數之後的結果。

tf.nn.conv2d中的stridespadding參數,分別表示步長和卷積方式。'VALID'是窄卷積,產生的輸出類型爲[1, sequence_length - filter_size +1, 1, 1]

卷積結果通過一個ReLU激活後,放入池化層。

池化層是一個Max-pooling,會將卷積輸出的n * 1的矩陣中取出一個最大值。而後將同一個filter的Max-pooling結果鏈接起來,最終獲得的類型爲[filter_num, 1]

最後將全部filter的結果相連,獲得最終通過Convolution和Pooling的矩陣。

討論

和原paper中給的76%比較接近,偏差產生的緣由可能在於我沒有采用交叉驗證,數據集較小的狀況下可能發生過擬合的狀況。

經過這一次課題研究,我試圖去尋找CV和NLP在使用CNNs時的共性,我我的認爲,他們都是經過特徵的部分提取去創造特徵來進行最終的分類訓練。

可是在CV中,卷積核最終的訓練形態比較容易理解,應該是一個圖像的局部特徵,好比一個圓,一個三角形,或是一條線等等。但在NLP中,卻很難去定義卷積核最終到底產生了什麼特性,它提取出來的特徵究竟是什麼,這一點是我作完這一次研究後所困擾的。

整體而言,CNNs在NLP中的應用是將文本以圖像的表示形式進行處理,雖然可能在一些方面的概念比較模糊,但卷積操做的速度,也使得CNNs模型在文本分類這一方向取得了不小的進步。

相關文章
相關標籤/搜索