前言
本篇文章會從代碼的角度說明如何基于TFlearn使用LSTM進行文本的情感分類。如果對于TFLearn和LSTM都不熟悉死嗦,沒有關(guān)系,先硬著頭皮將代碼看下(使用LSTM對IMDB數(shù)據(jù)集進行情感分類)耕蝉。從代碼的角度看都是很簡潔的铐刘,所以即使不熟悉,多看看代碼镰惦,當(dāng)代碼已經(jīng)熟練于心了迷守,后面如果有一天你漠然回首理解了其中的不解后,你的記憶更加深刻旺入。所以不懂兑凿、不熟悉沒關(guān)系,堅持下去就回明白的茵瘾。
由于實例代碼使用的是IMDB數(shù)據(jù)集礼华,所以這里會優(yōu)先介紹 一下這個數(shù)據(jù)集。
IMDB數(shù)據(jù)集
該數(shù)據(jù)集包含了電影的評論以及評論對應(yīng)的情感分類的標(biāo)簽(0,1分類)拗秘。作者的初衷是希望該數(shù)據(jù)集會成為情緒分類的一個基準(zhǔn)圣絮。這里介紹該數(shù)據(jù)集如何生成的以及如何使用提供的文件。
核心數(shù)據(jù)集包含了5萬條評論數(shù)據(jù)雕旨,這些數(shù)據(jù)被均分成訓(xùn)練集和測試集(訓(xùn)練和測試集各2.5萬)扮匠。標(biāo)簽也是均衡分布的(正負樣本各2.5萬)。也提供了5萬條無標(biāo)簽數(shù)據(jù)奸腺,以用于無監(jiān)督學(xué)習(xí)餐禁。
在數(shù)據(jù)集中,每個電影最多收集30條評論突照,因為同一個電影的評論往往具有相關(guān)性帮非。同時訓(xùn)練集和測試集采集的是不同的電影,所以嘗試去記住和電影強相關(guān)的詞匯以及相關(guān)的標(biāo)簽是不會取得顯著的提升效果的讹蘑。
在訓(xùn)練和測試集中末盔,負面結(jié)果的分值<=4,正面結(jié)果的分值>=10.中性的評論沒有包含在測試和訓(xùn)練集合中。在無監(jiān)督的數(shù)據(jù)集中包含任意評分的評論座慰。
對于下載下來的數(shù)據(jù)集的文件結(jié)構(gòu)大致如下:
有兩個頂級文件夾[train/, test/],對應(yīng)訓(xùn)練集和測試集陨舱。每個都包含了[pos/, neg/]目錄宅广,在這些文件夾中掖举,評論數(shù)據(jù)以如下方式存儲:[[id]_[rating].txt]。這里id表示唯一性ID痹兜,rating表示評分,例如[test/pos/200_8.txt]表示正面評論益缎,id是200谜慌,評分是8分。
無監(jiān)督數(shù)據(jù)集中[train/unsup/]所有的評分都是0莺奔,因為所有的評分都被省略了欣范。
數(shù)據(jù)集中也包含了每個評論對應(yīng)電影的評論頁面的URL,由于電影的評論數(shù)據(jù)是動態(tài)變化的令哟,所以不能指定評論的URL恼琼,只能指定電影評論頁面的URL。評論文件在如下文件中:
[urls_[pos, neg, unsup].txt]
對于評論的數(shù)據(jù)文件屏富,數(shù)據(jù)集中已經(jīng)包含了訓(xùn)練好的詞袋模型(BoW).這些數(shù)據(jù)存儲在.feat文件中晴竞。
每個.feat文件都是LIBSVM格式,一種用于標(biāo)記數(shù)據(jù)的ascii的稀疏向量格式役听。
這些文件中的特征索引從0開始颓鲜,且特征索引對應(yīng)的詞匯對應(yīng)著[imdb.vocab]中相應(yīng)的詞匯。所以一個在.feat文件中以0:7的形式表示[imdb.vocab]中的第一個單詞,在該評論中出現(xiàn)7次
LIBSVM相關(guān)資料參見:LIBSVM
數(shù)據(jù)集中也包含了一個[imdbEr.txt]文件典予,這里存儲了[imdb.vocab]中每個詞的情感評分甜滨。預(yù)期評級是了解數(shù)據(jù)集中單詞的平均極性的好方法。
數(shù)據(jù)集介紹就到這里瘤袖,下面開始代碼解讀衣摩。
代碼解讀
# -*- coding: utf-8 -*-
"""
https://www.tensorflow.org/versions/master/programmers_guide/embedding
https://github.com/tflearn/tflearn/blob/master/tflearn/datasets/imdb.py
采用LSTM進行情感分類的實例,數(shù)據(jù)集采用IMDB數(shù)據(jù)集
LSTM論文鏈接:
http://deeplearning.cs.cmu.edu/pdfs/Hochreiter97_lstm.pdf
IMDB數(shù)據(jù)集鏈接
http://ai.stanford.edu/~amaas/data/sentiment/
"""
引入相關(guān)的模塊和方法
from __future__ import division, print_function, absolute_import
import tflearn
from tflearn.data_utils import to_categorical, pad_sequences
from tflearn.datasets import imdb
獲得測試和訓(xùn)練數(shù)據(jù)
# 導(dǎo)入IMDB的數(shù)據(jù)集,n_words表示構(gòu)建詞向量的時候捂敌,考慮最常用的10000個詞,valid_portion表示訓(xùn)練過程中采用1/10的數(shù)據(jù)進行驗證
# load_data的結(jié)果是train[0][0:]表示訓(xùn)練數(shù)據(jù),train[1][0:]表示對應(yīng)的標(biāo)簽艾扮,即train[0]是訓(xùn)練矩陣,train[1]是標(biāo)簽矩陣
train, test, _ = imdb.load_data(path='imdb.pkl', n_words=10000,valid_portion=0.1)
# 獲得訓(xùn)練集對應(yīng)的數(shù)據(jù)和標(biāo)簽
trainX, trainY = train
# 獲得測試集對應(yīng)的數(shù)據(jù)和標(biāo)簽
testX, testY = test
數(shù)據(jù)預(yù)處理
# 進行數(shù)據(jù)處理占婉,補充長度泡嘴,長度全為100,不足的0補位,每條訓(xùn)練數(shù)據(jù)都變成100位的向量
trainX = pad_sequences(trainX, maxlen=100, value=0.)
testX = pad_sequences(testX, maxlen=100, value=0.)
# 將數(shù)據(jù)的打標(biāo)轉(zhuǎn)化為向量 原來是0->[1,0],原來是1->[0,1]
trainY = to_categorical(trainY, nb_classes=2)
testY = to_categorical(testY, nb_classes=2)
網(wǎng)絡(luò)構(gòu)建
# 構(gòu)建網(wǎng)絡(luò)
# 1.先指定輸入數(shù)據(jù)數(shù)據(jù)量大小不指定逆济,和placeholder類似酌予,在運行時指定,每個向量100維 <tf.Tensor 'InputData/X:0' shape=(?, 100) dtype=float32>
net = tflearn.input_data([None, 100])
# 2.進行詞嵌套奖慌,相當(dāng)于將離散的變?yōu)檫B續(xù)的抛虫,輸入詞詞有10000個ID,每個ID對應(yīng)一個詞简僧,將每個詞變?yōu)橐粋€128維的向量
# <tf.Tensor 'Embedding/embedding_lookup:0' shape=(?, 100, 128) dtype=float32>
#Embedding可以將離散的輸入應(yīng)用于機器學(xué)習(xí)處理方法中建椰。傳統(tǒng)的分類器和神經(jīng)網(wǎng)絡(luò)一般來講更適合處理連續(xù)的向量,
#如果有些離散對象自然被編碼為離散的原子岛马,例如獨特的ID棉姐,它們不利于機器學(xué)習(xí)的使用和泛化屠列。
#可以理解embedding是將非矢量對象轉(zhuǎn)換為利于機器學(xué)習(xí)處理的輸入。
net = tflearn.embedding(net, input_dim=10000, output_dim=128)
# 3.LSTM伞矩,輸出<tf.Tensor 'LSTM/LSTM/cond_199/Merge:0' shape=(?, 128) dtype=float32>
net = tflearn.lstm(net, 128, dropout=0.8)
# 4.全連接層 輸出<tf.Tensor 'FullyConnected/Softmax:0' shape=(?, 2) dtype=float32>
# 就是將學(xué)到的特征表示映射到樣本標(biāo)記空間
net = tflearn.fully_connected(net, 2, activation='softmax')
# 5.回歸層 指定優(yōu)化方法脸哀、學(xué)習(xí)速率(步長)、以及損失函數(shù)
net = tflearn.regression(net, optimizer='adam', learning_rate=0.001,loss='categorical_crossentropy')
構(gòu)建模型并訓(xùn)練
# 構(gòu)建深度模型扭吁,tensorboard需要的日志文件存儲在/tmp/tflearn_logs中
model = tflearn.DNN(net, tensorboard_verbose=0)
# 訓(xùn)練模型,指定訓(xùn)練數(shù)據(jù)集盲镶、測試數(shù)據(jù)集
model.fit(trainX, trainY, validation_set=(testX, testY), show_metric=True,batch_size=32)
到這里整個代碼就結(jié)束了,這里有兩個地方需要說明一下侥袜,一個是embedding,一個是fully_connected溉贿,分別表示詞嵌套和全連接枫吧。這里對這兩部分簡要說下,不會進行詳盡的公式推導(dǎo)和闡釋宇色。
首先說說這里的embedding.
在注釋部分已經(jīng)注釋的比較明確了九杂,其目的就是將要表示的東西進行向量化表示。原來每個字用一個ID表示宣蠕,這樣能表示的信息太少了例隆,不能夠表達詞所在語料內(nèi)更多的意思,比如和那個詞更相近。通過詞嵌套將一個單一的ID表示為一個128緯度(此處是128)的向量抢蚀,能夠表達更多的意思镀层。詞嵌套是向量化的一個重要手段,這個技巧一定要掌握的皿曲。
再聊聊這里的fully_connected
全連接層(fully connected layers唱逢,F(xiàn)C),在整個神經(jīng)網(wǎng)絡(luò)中起到類似于“分類器”的作用屋休。如果說卷積層坞古、池化層和激活函數(shù)層等操作是將原始數(shù)據(jù)映射到隱層特征空間的話,那么全連接層則起到將學(xué)到的“分布式特征表示”映射到樣本標(biāo)記空間的作用劫樟。在代碼中tflearn.fully_connected(net, 2,activation='softmax')痪枫,這里的第二個參數(shù)2,表示的就是輸出神經(jīng)元的個數(shù)毅哗,即訓(xùn)練集中對應(yīng)的打標(biāo)的向量听怕,也就是說將學(xué)習(xí)到的分布式特征轉(zhuǎn)化為一個只包含兩個元素的向量。
全連接層的每一個結(jié)點都與上一層的所有結(jié)點相連虑绵,用來把前邊提取到的特征綜合起來尿瞭。由于其全相連的特性,一般全連接層的參數(shù)也是最多的翅睛。不負責(zé)任的講声搁,全連接層一般由兩部分組成黑竞,即線性部分和非線性部分。線性部分主要做線性轉(zhuǎn)換疏旨,輸入用X表示很魂,輸出用Z表示。
線性部分的運算方法基本上就是線性加權(quán)求和的感覺檐涝,如果對于一個輸入向量
x=[x_0,x_1,...x_n]^T遏匆,
線性部分的輸出向量是
z=[z_0,z_1,z_2,...z_m]^T,
那么線性部分的參數(shù)就可以想象一個m*n的矩陣W谁榜,再加上一個偏置項
b=[b_0,...b_m]^T
于是有:
W*x+b=z
對于非線性部分幅聘,那當(dāng)然是做非線性變換了,輸入用線性部分的輸出Z表示窃植,輸出用Y表示帝蒿,假設(shè)用sigmoid作為非線性激活,那么有
Y = sigmoid(Z)
那么為什么要有非線性部分呢巷怜?個人理解葛超,其一是作數(shù)據(jù)的歸一化。不管前面的線性部分做了怎樣的工作延塑,到了非線性這里绣张,所有的數(shù)值將被限制在一個范圍內(nèi),這樣后面的網(wǎng)絡(luò)層如果要基于前面層的數(shù)據(jù)繼續(xù)計算关带,這個數(shù)值就相對可控了胖替。其二就是打破之前的線性映射關(guān)系。如果全連接層沒有非線性部分豫缨,只有線性部分独令,我們在模型中疊加多層神經(jīng)網(wǎng)絡(luò)是沒有意義的,我們假設(shè)有一個2層全連接神經(jīng)網(wǎng)絡(luò)好芭,其中沒有非線性層燃箭,那么對于第一層有:
W^0*x^0+b^0=z^1
對于第二層有:
W^1*z^1+b^1=z^2
兩式合并,有:
W^1*(W^0*x^0+b^0)+b^1=z^2
W^1*W^0*x^0+(W^1*b^0+b^1)=z^2
所以我們只要令:
W^{0'}=W^1*W^0 ,
b^{0'}=W^1*b^0+b^1舍败,
就可以用一層神經(jīng)網(wǎng)絡(luò)表示之前的兩層神經(jīng)網(wǎng)絡(luò)了招狸。所以非線性層的加入,使得多層神經(jīng)網(wǎng)絡(luò)的存在有了意義邻薯。
關(guān)于非線性激活常用的函數(shù)裙戏,以及什么樣的激活函數(shù)才是好的激活函數(shù)后面會專門介紹,這里不再贅述厕诡。
我們在看一下tflearn中fully_connected的函數(shù)的參數(shù)解釋累榜,當(dāng)然tensorflow中也有相關(guān)函數(shù),這里暫不進行注解。
tflearn.layers.core.fully_connected
incoming: 輸入Tensor壹罚,維度不小于2
n_units: 整型葛作,表示輸出神經(jīng)元個數(shù)
activation: 激活函數(shù),輸入激活函數(shù)的名稱或者函數(shù)定義(自定義非線性激活函數(shù))猖凛,默認值:'linear',參見 tflearn.activations
bias: 布爾值赂蠢,表示是否使用偏置項
weights_init: 初始化權(quán)重W的參數(shù),String 或者一個Tensor,默認是truncated_normal辨泳,參見tflearn.initializations
bias_init: 初始化偏置項虱岂,String或Tensor,默認'zeros'。參見tflearn.initializations
regularizer: 規(guī)范化函數(shù)菠红,String或者一個Tensor,對權(quán)重W進行規(guī)范化操作量瓜,默認不進行,參見tflearn.regularizers
weight_decay: 浮點數(shù)途乃,規(guī)范化方法的衰減參數(shù),默認值 0.001.
trainable: 可選參數(shù)扔傅,布爾值耍共,如果為True,那么變量將會加到圖模型中猎塞。同tf.Variable的trainable
restore: bool. If True, this layer weights will be restored when loading a model.
reuse: 可選參數(shù)试读,布爾類型,如果指定了Scope且當(dāng)前參數(shù)置為True,那么該layer的變量可復(fù)用
scope: 可選參數(shù)荠耽,String類型钩骇,定義layer的Scope(指定Scope可以在不同層間共享變量,但需要注意Scope可被覆蓋铝量,需自行控制其唯一性)
name: 可選值倘屹,表示該層的名稱,默認值: 'FullyConnected'.
總結(jié)
基于tflearn進行深度學(xué)習(xí)相關(guān)功能的實現(xiàn)慢叨,要比基于原生的tensorflow要簡單的多纽匙,封裝的比較干凈。本篇只介紹了使用LSTM進行NLP的相關(guān)任務(wù)處理拍谐,讀者完全可以自行下載代碼進行改造烛缔,這里希望讀者能夠了解embedding和fully_connected的意思,知道其作用轩拨,如果希望深入了解践瓷,可以參考相關(guān)論文和他人注解的blog,本篇就寫到這里亡蓉,歡迎拍磚晕翠。