1.實驗數(shù)據(jù)獲取:
這里的實驗數(shù)據(jù)是本人自己提取的,具體方式是:
(大家可以根據(jù)自己喜好進(jìn)行如下步驟)
1.選取3個不同類別的文本,每類500篇,共1500篇。
2.使用TF-IDF或詞頻等方式,從每個類型的文本中選出100個特征詞,3個類別,共300個特征詞。將300個特征詞存入一個list中。
3.使用300個特征詞的列表去遍歷每一篇文本,如果第x個特征詞在該文本中出現(xiàn)次數(shù)為n,則對應(yīng)該文本的特征list的第x為記為n。1500篇文本,1500個特征list分別對應(yīng)每個文本。
4.對每個文本設(shè)置標(biāo)簽,使用one-hot方式表示即可
5.將其文本順序打亂即可,但保證其對應(yīng)關(guān)系不變 文本——文本特征list——標(biāo)簽
例如:
這里我使用TF-IDF從3個不同類別的文本中提取到的300個特征詞:
我們甚至可以從這些詞中看出這三類文本的類別分別為:房產(chǎn),星座,游戲
當(dāng)然,有些詞的區(qū)分性不是太好,我們可以通過增加文本數(shù)量,設(shè)置更精確的停用詞實現(xiàn)更好的效果。
提取一篇文本的特征list:
文本特征list的每一位與特征詞list的每一位一一對應(yīng),例如文本特征list[1]=11,對應(yīng)特征詞list[1]=“一個”,即表示"一個"這個詞在該文本中出現(xiàn)11次。
再對這篇文本設(shè)置標(biāo)簽:
[‘0’, ‘1’, ‘0’]
表明該文本屬于第二類。
2.代碼實現(xiàn):
關(guān)于理論實現(xiàn)這里就不做講解(如果想要學(xué)習(xí),推薦深度學(xué)習(xí)入門——基于Python的理論與實現(xiàn)-圖靈教育-[日]齋藤康毅 著),
直接上代碼,具體解釋在注釋中:
代碼參考于:
Tensorflow中文社區(qū)
BiliBili視頻 深度學(xué)習(xí)框架Tensorflow學(xué)習(xí)與應(yīng)用
import tensorflow as tfimport dataN #這里是自己寫的獲取數(shù)據(jù)的python文件,可以獲取到1200個訓(xùn)練數(shù)據(jù),訓(xùn)練標(biāo)簽。300個測試數(shù)據(jù),測試標(biāo)簽
train_dataN,train_labelN,test_dataN,test_labelN=dataN.dataone()#placeholder為tensor操作創(chuàng)建占位符,可以在TensorFlow運(yùn)行某一計算時根據(jù)該占位符輸入具體的值
x=tf.placeholder("float",shape=[None,300])
#None表示一次傳入數(shù)據(jù)的個數(shù)未知,他會根據(jù)實際情況自動設(shè)置
y_=tf.placeholder("float",shape=[None,3])
#y_為正確解標(biāo)簽,x為輸入的數(shù)據(jù)#定義兩個函數(shù)用于初始化W,b:
def weight_variable(shape):initial=tf.truncated_normal(shape,stddev=0.1) #正態(tài)分布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輸入數(shù)據(jù),W權(quán)重張量|濾波器,0邊距(padding size)的模板
# strides[0]=strides[3]=1 (默認(rèn)), strides[1]代表x方向的步長,strides[2]代表y方向的步長
# SAME就是一種padding方法,另一個是VALID,在實際中多采用SAME使卷積層不改變數(shù)據(jù)大小
# 如果padding設(shè)置為SAME,則輸入圖片大小和輸出圖片大小是一致的,如果是VALID則圖片經(jīng)過濾波器后可能會變小
# 大家可以去了解下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輸入數(shù)據(jù)
#ksize[0]=ksize=[3]=1(默認(rèn)),ksize[1]代表x方向長,ksize[2]代表y方向長x_text=tf.reshape(x,[-1,10,30,1]) #改變x的形狀,[1*300,通道數(shù)1]變?yōu)閇10*30,通道數(shù)1],-1表示batch(一個批次)數(shù)量未知,1表示通道數(shù)為1#第一層:::
W_conv1=weight_variable([2,6,1,10])
# 卷積的權(quán)重張量形狀是[2,6,1,10],前兩個維度是采樣窗口的大小,接著是輸入的通道數(shù)目,最后是輸出的通道數(shù)目(表示使用多少卷積核),這里使用10個卷積核從一個平面抽取特征,得到10個通道
# 10就是指卷積核的數(shù)量,每種卷積只對某些特征敏感,獲取的特征有限。
# 因此將多種不同的卷積核分別對圖像進(jìn)行處理,就能獲得更多的特征。
# 每個卷積核按照規(guī)則掃描完圖像后,就輸出一張?zhí)卣鲌D像(feature map),
# 因此10也指輸出的特征圖
b_conv1=bias_variable([10])
#對于每一個輸出通道都有一個對應(yīng)的偏置量,前面因為每張圖片生成10個特征,這里也要對應(yīng)10個偏置值h_conv1=tf.nn.relu(conv2d(x_text,W_conv1)+b_conv1)
#這里的卷積層不改變大小,即數(shù)據(jù)仍然為10*30,但因為使用了10個卷積核進(jìn)行特征抽取,產(chǎn)生了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的矩陣,這層要輸出的矩陣個數(shù)為20
b_conv2=bias_variable([20])
#每一個卷積核對應(yīng)一個偏置量
h_conv2=tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)
#這里的卷積層不改變數(shù)據(jù)大小
h_pool2=max_pool_2x2(h_conv2)
#池化輸出20個3*8的矩陣#連接層:::初始化第一個全連接層
W_fc1=weight_variable([3*8*20,500])
# 上一層(卷積層)傳入3*8*20個神經(jīng)元,我們設(shè)置全連接層有500個神經(jīng)元
b_fc1=bias_variable([500])h_pool2_flat=tf.reshape(h_pool2,[-1,3*8*20])
#把池化層2的輸出扁平化化為1維,-1表示batch數(shù)量未知
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方法能在運(yùn)行中自動忽略一些神經(jīng)元,防止過擬合#初始化第二個全連接層:
W_fc2=weight_variable([500,3])
#500對應(yīng)上一層的輸出,3對應(yīng)這一場的輸出為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)
#學(xué)習(xí)率為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個訓(xùn)練數(shù)據(jù),這里我們10個為1批次batch 進(jìn)行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%的神經(jīng)元工作"""if i%4==0: #訓(xùn)練每進(jìn)行4個批次,我們就傳入一個批次的測試數(shù)據(jù),檢測當(dāng)前的正確率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為預(yù)測結(jié)果和真實標(biāo)簽,大家可以打印出來看看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}))#再訓(xùn)練完成后,一次性將所有測試數(shù)據(jù)傳入進(jìn)行測試,得到總的正確率。#注意!使用該方式,則不能使用使用上方 檢測當(dāng)前的正確率,不然會造成正確率偏大(因為測試數(shù)據(jù)也經(jīng)過了訓(xùn)練)
3.運(yùn)行結(jié)果:
通過測試,我得到的測試數(shù)據(jù)總的正確率為70%~80%左右。
而每進(jìn)行4個批次,傳入一個批次的測試數(shù)據(jù)時正確率總時很低,并且沒有呈現(xiàn)明顯的遞增變化,這可能因為測試和訓(xùn)練的數(shù)據(jù)過少造成:
4.需要注意的點:
以下僅僅是我在學(xué)習(xí)過程中產(chǎn)生的思考和總結(jié),并不代表正確答案,大家如果有更好解答或其它理解可以在評論區(qū)討論。
1.通過CNN的方式實現(xiàn)文本分類是否具有可靠性?
我認(rèn)為可靠性并不如CNN對圖片的分類,因為文本是1維數(shù)據(jù),為了通過卷積神經(jīng)網(wǎng)絡(luò),需要將其變化為多維度,而在變化后(比如1x300變?yōu)?0x30),這樣的變化有很多種。而無論怎么改變,原本是1維的數(shù)據(jù),在多維度上究竟有何聯(lián)系?我認(rèn)為,這樣意義不大的變換并不會帶來什么更好的結(jié)果。
對此,我設(shè)定了一個簡單的神經(jīng)網(wǎng)絡(luò),不使用卷積,對上述文本進(jìn)行了分類實驗
其代碼如下(講解就不再給出了):
import dataN
import tensorflow as tftrain_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)
對測試數(shù)據(jù)的運(yùn)行結(jié)果如下:
我們可以發(fā)現(xiàn),通過簡單的神經(jīng)網(wǎng)絡(luò)訓(xùn)練,反而得到了更優(yōu)的效果。但這并不能完全說明上述猜測,或許多層卷積網(wǎng)絡(luò)有更好的文本分類方式,在之后的學(xué)習(xí)中或許能解此疑惑。
2.關(guān)于卷積層和池化層的輸出大小:
這里就不進(jìn)行講解了,在了解卷積和池化的原理,和padding方法SAME和VALID后,我們就能自己推導(dǎo)得出
3.卷積層和全連接層的作用:
卷積取的是局部特征,全連接就是把以前的局部特征重新通過權(quán)值矩陣組裝成完整的圖。(來自知乎)
4.全連接層神經(jīng)元個數(shù)對結(jié)果的影響:
以下結(jié)果僅針對上述代碼和數(shù)據(jù):
2048個神經(jīng)元,正確率:75%~85%
1024個神經(jīng)元,正確率:75%~85%
500個神經(jīng)元,正確率:70%~80%
100個神經(jīng)元,正確率:60%~80%
50個神經(jīng)元,正確率:50%~70%
神經(jīng)元是否是越多越好呢?
在測試到4096個神經(jīng)元時,結(jié)果出現(xiàn)了不穩(wěn)定線性,有的很高超過了85%,有的甚至不到50%
在詢問了一位大佬后,得到了這樣的結(jié)果:
神經(jīng)元數(shù)量的設(shè)置就是玄學(xué),并不是越高或者越低越好,正確率也不一定與神經(jīng)元數(shù)量呈正比,往往通過多次調(diào)試進(jìn)行選擇。
5.關(guān)于數(shù)據(jù):
在數(shù)據(jù)傳入之前,一定要對數(shù)據(jù)進(jìn)行亂序處理!
在未經(jīng)過亂序處理或者打亂不完全或有規(guī)則的打亂,神經(jīng)網(wǎng)絡(luò)往往會學(xué)習(xí)到這些規(guī)律性變化,對于滿足這些規(guī)律的測試文本,往往能獲得到驚人的效果。
在之前,我嘗試過使用【第一類文本,第二類文本,第三類文本,第一類文本…】的有規(guī)律性方式打亂訓(xùn)練文本和測試文本,再經(jīng)過簡單的神經(jīng)網(wǎng)絡(luò)(無卷積),這時其正確率就已經(jīng)達(dá)到了98%以上,這樣的結(jié)果明顯是不和常規(guī)的,僅僅對有此規(guī)律的文本起作用。
總結(jié)
以上是生活随笔為你收集整理的tensorflow多层卷积网络实现CNN文本分类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。