tensorflow多層卷積網絡實現CNN文本分類

1.實驗數據獲取:
這裏的實驗數據是本人自己提取的,具體方式是:
(大家可以根據自己喜好進行如下步驟)
1.選取3個不同類別的文本,每類500篇,共1500篇。
2.使用TF-IDF或詞頻等方式,從每個類型的文本中選出100個特徵詞,3個類別,共300個特徵詞。將300個特徵詞存入一個list中。
3.使用300個特徵詞的列表去遍歷每一篇文本,如果第x個特徵詞在該文本中出現次數爲n,則對應該文本的特徵list的第x爲記爲n。1500篇文本,1500個特徵list分別對應每個文本。
4.對每個文本設置標籤,使用one-hot方式表示即可
5.將其文本順序打亂即可,但保證其對應關係不變 文本——文本特徵list——標籤

例如:
這裏我使用TF-IDF從3個不同類別的文本中提取到的300個特徵詞:
在這裏插入圖片描述
我們甚至可以從這些詞中看出這三類文本的類別分別爲:房產,星座,遊戲
當然,有些詞的區分性不是太好,我們可以通過增加文本數量,設置更精確的停用詞實現更好的效果。

提取一篇文本的特徵list:
在這裏插入圖片描述
文本特徵list的每一位與特徵詞list的每一位一一對應,例如文本特徵list[1]=11,對應特徵詞list[1]=“一個”,即表示"一個"這個詞在該文本中出現11次。

再對這篇文本設置標籤:
[‘0’, ‘1’, ‘0’]
表明該文本屬於第二類。

2.代碼實現:
關於理論實現這裏就不做講解(如果想要學習,推薦深度學習入門——基於Python的理論與實現-圖靈教育-[日]齋藤康毅 著),
直接上代碼,具體解釋在註釋中:
代碼參考於:
Tensorflow中文社區
BiliBili視頻 深度學習框架Tensorflow學習與應用

import tensorflow as tf

import dataN     #這裏是自己寫的獲取數據的python文件,可以獲取到1200個訓練數據,訓練標籤。300個測試數據,測試標籤
train_dataN,train_labelN,test_dataN,test_labelN=dataN.dataone()

#placeholder爲tensor操作創建佔位符,可以在TensorFlow運行某一計算時根據該佔位符輸入具體的值
x=tf.placeholder("float",shape=[None,300])
#None表示一次傳入數據的個數未知,他會根據實際情況自動設置
y_=tf.placeholder("float",shape=[None,3])
#y_爲正確解標籤,x爲輸入的數據

#定義兩個函數用於初始化W,b:
def weight_variable(shape):
    initial=tf.truncated_normal(shape,stddev=0.1)  #正態分佈
    return tf.Variable(initial)
def bias_variable(shape):
    initial=tf.constant(0.1,shape=shape)
    return tf.Variable(initial)

#卷積層:
def conv2d(x,W):
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding="SAME")
# x input tersor of shape [batch,in_height,in_wight,in_channels]
# stride步幅,padd填充,x輸入數據,W權重張量|濾波器,0邊距(padding size)的模板
# strides[0]=strides[3]=1 (默認), strides[1]代表x方向的步長,strides[2]代表y方向的步長
# SAME就是一種padding方法,另一個是VALID,在實際中多采用SAME使卷積層不改變數據大小
# 如果padding設置爲SAME,則輸入圖片大小和輸出圖片大小是一致的,如果是VALID則圖片經過濾波器後可能會變小
# 大家可以去了解下SAME和VALID兩種方式(這很重要!!!)

#池化層:
def max_pool_2x2(x):
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
#ksize窗口大小|過濾器大小,stride步幅,pdd填充,x輸入數據
#ksize[0]=ksize=[3]=1(默認),ksize[1]代表x方向長,ksize[2]代表y方向長

x_text=tf.reshape(x,[-1,10,30,1])   #改變x的形狀,[1*300,通道數1]變爲[10*30,通道數1],-1表示batch(一個批次)數量未知,1表示通道數爲1

#第一層:::
W_conv1=weight_variable([2,6,1,10])
# 卷積的權重張量形狀是[2,6,1,10],前兩個維度是採樣窗口的大小,接着是輸入的通道數目,最後是輸出的通道數目(表示使用多少卷積核),這裏使用10個卷積核從一個平面抽取特徵,得到10個通道
# 10就是指卷積核的數量,每種卷積只對某些特徵敏感,獲取的特徵有限。
# 因此將多種不同的卷積核分別對圖像進行處理,就能獲得更多的特徵。
# 每個卷積核按照規則掃描完圖像後,就輸出一張特徵圖像(feature map),
# 因此10也指輸出的特徵圖
b_conv1=bias_variable([10])
#對於每一個輸出通道都有一個對應的偏置量,前面因爲每張圖片生成10個特徵,這裏也要對應10個偏置值

h_conv1=tf.nn.relu(conv2d(x_text,W_conv1)+b_conv1)
#這裏的卷積層不改變大小,即數據仍然爲10*30,但因爲使用了10個卷積核進行特徵抽取,產生了10個通道
h_pool1=max_pool_2x2(h_conv1)
#池化得到10個5*15的平面

#第二層:::
W_conv2=weight_variable([2,2,10,20])
#2x2的採樣窗口,20個卷積核從10個平面抽取特徵,輸出20個特徵平面
#輸入的是10個2*15的矩陣,這層要輸出的矩陣個數爲20
b_conv2=bias_variable([20])
#每一個卷積覈對應一個偏置量
h_conv2=tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)
#這裏的卷積層不改變數據大小
h_pool2=max_pool_2x2(h_conv2)
#池化輸出20個3*8的矩陣

#連接層:::初始化第一個全連接層
W_fc1=weight_variable([3*8*20,500])
# 上一層(卷積層)傳入3*8*20個神經元,我們設置全連接層有500個神經元
b_fc1=bias_variable([500])

h_pool2_flat=tf.reshape(h_pool2,[-1,3*8*20])
#把池化層2的輸出扁平化化爲1維,-1表示batch數量未知
h_fc1=tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)
# 第一個全連接層的輸出。得到一個長度爲500的向量

keep_prob=tf.placeholder("float")
h_fc1_drop=tf.nn.dropout(h_fc1,keep_prob)
#dropout方法能在運行中自動忽略一些神經元,防止過擬合

#初始化第二個全連接層:
W_fc2=weight_variable([500,3])
#500對應上一層的輸出,3對應這一場的輸出爲3類
b_fc2=bias_variable([3])

prediction=tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)
#計算概率

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=prediction))
#計算 logits 和 labels 之間的 softmax 交叉熵。
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
#學習率爲1e-4,使用梯度下降的方式最小化交叉熵
correct_prediction = tf.equal(tf.argmax(prediction,1), tf.argmax(y_,1))
#求正確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

with tf.Session() as sess:  #啓動圖
    sess.run(tf.initialize_all_variables()) #初始化全部變量
    for i in range(120):    #我們共1200個訓練數據,這裏我們10個爲1批次batch 進行
        TrainData_batch = train_dataN[i * 10:(i + 1) * 10]
        label_batch = train_labelN[i * 10:(i + 1) * 10]
        sess.run(train_step,feed_dict={x:TrainData_batch,y_:label_batch,keep_prob:0.7})
        #keep_prob:0.7 只有70%的神經元工作

        """
        if i%4==0:  #訓練每進行4個批次,我們就傳入一個批次的測試數據,檢測當前的正確率
            t=int(i/4)
            TestData_batch=test_dataN[t * 10:(t + 1) * 10]
            TestLabel_batch=test_labelN[t * 10:(t + 1) * 10]
            #pre=sess.run([prediction,y_],feed_dict={x:TrainData_batch,y_:TestLabel_batch,keep_prob:1.0})
            #print(pre)  pre爲預測結果和真實標籤,大家可以打印出來看看
            acc=sess.run(accuracy,feed_dict={x:TrainData_batch,y_:TestLabel_batch,keep_prob:1.0})
            print("Iter "+str(t),"   accuracy= "+str(acc))
        """
    print(sess.run(accuracy,feed_dict={x:test_dataN,y_:test_labelN,keep_prob:1.0}))
   #再訓練完成後,一次性將所有測試數據傳入進行測試,得到總的正確率。
   #注意!使用該方式,則不能使用使用上方 檢測當前的正確率,不然會造成正確率偏大(因爲測試數據也經過了訓練)

3.運行結果:
通過測試,我得到的測試數據總的正確率爲70%~80%左右。
而每進行4個批次,傳入一個批次的測試數據時正確率總時很低,並且沒有呈現明顯的遞增變化,這可能因爲測試和訓練的數據過少造成:
在這裏插入圖片描述
4.需要注意的點:
以下僅僅是我在學習過程中產生的思考和總結,並不代表正確答案,大家如果有更好解答或其它理解可以在評論區討論。
1.通過CNN的方式實現文本分類是否具有可靠性?
我認爲可靠性並不如CNN對圖片的分類,因爲文本是1維數據,爲了通過卷積神經網絡,需要將其變化爲多維度,而在變化後(比如1x300變爲10x30),這樣的變化有很多種。而無論怎麼改變,原本是1維的數據,在多維度上究竟有何聯繫?我認爲,這樣意義不大的變換並不會帶來什麼更好的結果。
對此,我設定了一個簡單的神經網絡,不使用卷積,對上述文本進行了分類實驗
其代碼如下(講解就不再給出了):

import dataN
import tensorflow as tf

train_dataN,train_labelN,test_dataN,test_labelN=dataN.dataone()
sess = tf.InteractiveSession()

x = tf.placeholder("float", shape=[None, 300])
y_ = tf.placeholder("float", shape=[None, 3])

W = tf.Variable(tf.zeros([300, 3]))
b = tf.Variable(tf.zeros([3]))

sess.run(tf.global_variables_initializer())
y = tf.nn.softmax(tf.matmul(x, W) + b)
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(cross_entropy)

correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

for i in range(120):
    TrainData_batch = train_dataN[i * 10:(i + 1) * 10]
    label_batch = train_labelN[i * 10:(i + 1) * 10]
    train_step.run(feed_dict={x: TrainData_batch, y_: label_batch})


for t in range(30):
    TestData_batch = test_dataN[t * 10:(t + 1) * 10]
    TestLabel_batch = test_labelN[t * 10:(t + 1) * 10]
    result = sess.run(correct_prediction, feed_dict={x: TestData_batch, y_: TestLabel_batch})
    print(result)

對測試數據的運行結果如下:
在這裏插入圖片描述
我們可以發現,通過簡單的神經網絡訓練,反而得到了更優的效果。但這並不能完全說明上述猜測,或許多層卷積網絡有更好的文本分類方式,在之後的學習中或許能解此疑惑。

2.關於卷積層和池化層的輸出大小:
這裏就不進行講解了,在瞭解卷積和池化的原理,和padding方法SAME和VALID後,我們就能自己推導得出

3.卷積層和全連接層的作用:
卷積取的是局部特徵,全連接就是把以前的局部特徵重新通過權值矩陣組裝成完整的圖。(來自知乎)

4.全連接層神經元個數對結果的影響:
以下結果僅針對上述代碼和數據:
2048個神經元,正確率:75%~85%
1024個神經元,正確率:75%~85%
500個神經元,正確率:70%~80%
100個神經元,正確率:60%~80%
50個神經元,正確率:50%~70%
神經元是否是越多越好呢?
在測試到4096個神經元時,結果出現了不穩定線性,有的很高超過了85%,有的甚至不到50%
在詢問了一位大佬後,得到了這樣的結果:
神經元數量的設置就是玄學,並不是越高或者越低越好,正確率也不一定與神經元數量呈正比,往往通過多次調試進行選擇。

5.關於數據:
在數據傳入之前,一定要對數據進行亂序處理!
在未經過亂序處理或者打亂不完全或有規則的打亂,神經網絡往往會學習到這些規律性變化,對於滿足這些規律的測試文本,往往能獲得到驚人的效果。
在之前,我嘗試過使用【第一類文本,第二類文本,第三類文本,第一類文本…】的有規律性方式打亂訓練文本和測試文本,再經過簡單的神經網絡(無卷積),這時其正確率就已經達到了98%以上,這樣的結果明顯是不和常規的,僅僅對有此規律的文本起作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章