1. 程式人生 > >CNN情感分析(文字分類)

CNN情感分析(文字分類)

   這篇文章翻譯至denny britz的部落格。

一、資料預處理

  這個情感分析的資料集來自Rotten Tomatoes的電影評論,總共10662個樣本,一半正例,一半負例,詞彙的數目大概2萬個。
  任何機器學習能夠得到很好的執行,資料預處理都很重要。首先,簡單介紹下其資料預處理的過程:
  1、從原始檔案中載入正例和負例;
  2、對資料進行清理;
  3、設定最大句子長度是59,不足的補符號;
  4、建立一個字典,對映字到0-18765。
  簡單說明一下,第3步保證輸入的句子長度一致,CNN本來是處理影象的,所以,我們要做的像個圖片一樣,因此要求長度一致。

二、CNN模型設計

CNN模型

  這個圖已經畫的相當6了。
  第一層詞嵌入,用word2vec把詞語對映為128維的向量。第二層是卷積層,這個卷積層比較特別,用了多個過濾器的大小。例如,每次分別劃分3,4或5個字詞;第三層,池化層,在這裡採用的最大池化,並將所有卷積層的結果放在一個長的特徵向量上,然後加上dropout正則,最後用softmax輸出結果。
  下面是程式碼實現。
  
  為了更好的處理超引數,我們把程式碼都放在一個TextCNN類裡面:

import tensorflow as tf
import numpy as np

class TextCNN(object)
:
""" A CNN for text classification. Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer. """ def __init__( self, sequence_length, num_classes, vocab_size, embedding_size, filter_sizes, num_filters): # Implementation...

  sequence_length-句子的長度,59;
  num_classes-分類的數目,2;
  vocab_size-字典的長度;
  embedding_size-我們對映的向量維度大小,128;
  filter_sizes-卷積層過濾器的大小,[3, 4, 5];
  num_filters-過濾器的深度。

1、輸入
  定義輸入資料的placeholder和dropout。

# Placeholders for input, output and dropout

self.input_x=tf.placeholder(tf.int32[None,sequence_length], name="input_x")
self.input_y=tf.placeholder(tf.float32[None,num_classes], name="input_y")
self.dropout_keep_prob=tf.placeholder(tf.float32,name="dropout_keep_prob")

  tf.placeholder相當於一個坑,等著填呢。第二個引數是tensor的shape,None表示這個維度可以是任意數。在樣例中,第一個維度是批資料的大小,用None表示,可以是任意大小的批資料。
2、詞嵌入層

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

  這裡用的隨機生成的vector。最後的tf.expand_dims()來擴充資料,生成[[None, sequence_length, embedding_size, 1]這種格式。因為TensorFlow的局基層conv2d需要一個4維的tensor(batch,width,height,channel)
3、卷積池化層

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 over the outputs
        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(3, pooled_outputs)
self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])

我們先建立卷積層,然後是最大池化層。因為我們卷積層的過濾器選擇了多個,所以我們需要對它們進行迭代,為每一個過濾器都建立一個layer,然後把結果匯聚到一個大的向量裡。
  這裡需要說明的是,和影象不同的地方,就是我們文字不能跨行過濾,因為那樣會沒有意義。
4、設定dropout(訓練時候用)

# Add dropout
with tf.name_scope("dropout"):
    self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob)

5、評分和預測(最後的全連線層)

with tf.name_scope("output"):
    W = tf.Variable(tf.truncated_normal([num_filters_total, num_classes], stddev=0.1), name="W")
    b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")
    self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")
    self.predictions = tf.argmax(self.scores, 1, name="predictions")

6、設定損失函式和準確率計算

# Calculate mean cross-entropy loss
with tf.name_scope("loss"):
    losses = tf.nn.softmax_cross_entropy_with_logits(self.scores, self.input_y)
    self.loss = tf.reduce_mean(losses)

  這裡使用的交叉熵損失函式

# Calculate Accuracy
with tf.name_scope("accuracy"):
    correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1))
    self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")

 剩下的,按照常規步驟走就可以了。