前言
本篇文章會(huì)從代碼的角度說(shuō)明如何基于TFlearn使用LSTM進(jìn)行文本的情感分類(lèi)。如果對(duì)于TFLearn和LSTM都不熟悉,沒(méi)有關(guān)系,先硬著頭皮將代碼看下(使用LSTM對(duì)IMDB數(shù)據(jù)集進(jìn)行情感分類(lèi))。從代碼的角度看都是很簡(jiǎn)潔的,所以即使不熟悉,多看看代碼,當(dāng)代碼已經(jīng)熟練于心了,后面如果有一天你漠然回首理解了其中的不解后,你的記憶更加深刻。所以不懂、不熟悉沒(méi)關(guān)系,堅(jiān)持下去就回明白的。
由于實(shí)例代碼使用的是IMDB數(shù)據(jù)集,所以這里會(huì)優(yōu)先介紹 一下這個(gè)數(shù)據(jù)集。
IMDB數(shù)據(jù)集
該數(shù)據(jù)集包含了電影的評(píng)論以及評(píng)論對(duì)應(yīng)的情感分類(lèi)的標(biāo)簽(0,1分類(lèi))。作者的初衷是希望該數(shù)據(jù)集會(huì)成為情緒分類(lèi)的一個(gè)基準(zhǔn)。這里介紹該數(shù)據(jù)集如何生成的以及如何使用提供的文件。
核心數(shù)據(jù)集包含了5萬(wàn)條評(píng)論數(shù)據(jù),這些數(shù)據(jù)被均分成訓(xùn)練集和測(cè)試集(訓(xùn)練和測(cè)試集各2.5萬(wàn))。標(biāo)簽也是均衡分布的(正負(fù)樣本各2.5萬(wàn))。也提供了5萬(wàn)條無(wú)標(biāo)簽數(shù)據(jù),以用于無(wú)監(jiān)督學(xué)習(xí)。
在數(shù)據(jù)集中,每個(gè)電影最多收集30條評(píng)論,因?yàn)橥粋€(gè)電影的評(píng)論往往具有相關(guān)性。同時(shí)訓(xùn)練集和測(cè)試集采集的是不同的電影,所以嘗試去記住和電影強(qiáng)相關(guān)的詞匯以及相關(guān)的標(biāo)簽是不會(huì)取得顯著的提升效果的。
在訓(xùn)練和測(cè)試集中,負(fù)面結(jié)果的分值<=4,正面結(jié)果的分值>=10.中性的評(píng)論沒(méi)有包含在測(cè)試和訓(xùn)練集合中。在無(wú)監(jiān)督的數(shù)據(jù)集中包含任意評(píng)分的評(píng)論。
對(duì)于下載下來(lái)的數(shù)據(jù)集的文件結(jié)構(gòu)大致如下:
有兩個(gè)頂級(jí)文件夾[train/, test/],對(duì)應(yīng)訓(xùn)練集和測(cè)試集。每個(gè)都包含了[pos/, neg/]目錄,在這些文件夾中,評(píng)論數(shù)據(jù)以如下方式存儲(chǔ):[[id]_[rating].txt]。這里id表示唯一性ID,rating表示評(píng)分,例如[test/pos/200_8.txt]表示正面評(píng)論,id是200,評(píng)分是8分。
無(wú)監(jiān)督數(shù)據(jù)集中[train/unsup/]所有的評(píng)分都是0,因?yàn)樗械脑u(píng)分都被省略了。
數(shù)據(jù)集中也包含了每個(gè)評(píng)論對(duì)應(yīng)電影的評(píng)論頁(yè)面的URL,由于電影的評(píng)論數(shù)據(jù)是動(dòng)態(tài)變化的,所以不能指定評(píng)論的URL,只能指定電影評(píng)論頁(yè)面的URL。評(píng)論文件在如下文件中:
[urls_[pos, neg, unsup].txt]
對(duì)于評(píng)論的數(shù)據(jù)文件,數(shù)據(jù)集中已經(jīng)包含了訓(xùn)練好的詞袋模型(BoW).這些數(shù)據(jù)存儲(chǔ)在.feat文件中。
每個(gè).feat文件都是LIBSVM格式,一種用于標(biāo)記數(shù)據(jù)的ascii的稀疏向量格式。
這些文件中的特征索引從0開(kāi)始,且特征索引對(duì)應(yīng)的詞匯對(duì)應(yīng)著[imdb.vocab]中相應(yīng)的詞匯。所以一個(gè)在.feat文件中以0:7的形式表示[imdb.vocab]中的第一個(gè)單詞,在該評(píng)論中出現(xiàn)7次
LIBSVM相關(guān)資料參見(jiàn):LIBSVM
數(shù)據(jù)集中也包含了一個(gè)[imdbEr.txt]文件,這里存儲(chǔ)了[imdb.vocab]中每個(gè)詞的情感評(píng)分。預(yù)期評(píng)級(jí)是了解數(shù)據(jù)集中單詞的平均極性的好方法。
數(shù)據(jù)集介紹就到這里,下面開(kāi)始代碼解讀。
代碼解讀
# -*- coding: utf-8 -*-
"""
https://www.tensorflow.org/versions/master/programmers_guide/embedding
https://github.com/tflearn/tflearn/blob/master/tflearn/datasets/imdb.py
采用LSTM進(jìn)行情感分類(lèi)的實(shí)例,數(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
獲得測(cè)試和訓(xùn)練數(shù)據(jù)
# 導(dǎo)入IMDB的數(shù)據(jù)集,n_words表示構(gòu)建詞向量的時(shí)候,考慮最常用的10000個(gè)詞,valid_portion表示訓(xùn)練過(guò)程中采用1/10的數(shù)據(jù)進(jìn)行驗(yàn)證
# load_data的結(jié)果是train[0][0:]表示訓(xùn)練數(shù)據(jù),train[1][0:]表示對(duì)應(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)練集對(duì)應(yīng)的數(shù)據(jù)和標(biāo)簽
trainX, trainY = train
# 獲得測(cè)試集對(duì)應(yīng)的數(shù)據(jù)和標(biāo)簽
testX, testY = test
數(shù)據(jù)預(yù)處理
# 進(jìn)行數(shù)據(jù)處理,補(bǔ)充長(zhǎng)度,長(zhǎng)度全為100,不足的0補(bǔ)位,每條訓(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)化為向量 原來(lái)是0->[1,0],原來(lái)是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類(lèi)似,在運(yùn)行時(shí)指定,每個(gè)向量100維 <tf.Tensor 'InputData/X:0' shape=(?, 100) dtype=float32>
net = tflearn.input_data([None, 100])
# 2.進(jìn)行詞嵌套,相當(dāng)于將離散的變?yōu)檫B續(xù)的,輸入詞詞有10000個(gè)ID,每個(gè)ID對(duì)應(yīng)一個(gè)詞,將每個(gè)詞變?yōu)橐粋€(gè)128維的向量
# <tf.Tensor 'Embedding/embedding_lookup:0' shape=(?, 100, 128) dtype=float32>
#Embedding可以將離散的輸入應(yīng)用于機(jī)器學(xué)習(xí)處理方法中。傳統(tǒng)的分類(lèi)器和神經(jīng)網(wǎng)絡(luò)一般來(lái)講更適合處理連續(xù)的向量,
#如果有些離散對(duì)象自然被編碼為離散的原子,例如獨(dú)特的ID,它們不利于機(jī)器學(xué)習(xí)的使用和泛化。
#可以理解embedding是將非矢量對(duì)象轉(zhuǎn)換為利于機(jī)器學(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í)速率(步長(zhǎng))、以及損失函數(shù)
net = tflearn.regression(net, optimizer='adam', learning_rate=0.001,loss='categorical_crossentropy')
構(gòu)建模型并訓(xùn)練
# 構(gòu)建深度模型,tensorboard需要的日志文件存儲(chǔ)在/tmp/tflearn_logs中
model = tflearn.DNN(net, tensorboard_verbose=0)
# 訓(xùn)練模型,指定訓(xùn)練數(shù)據(jù)集、測(cè)試數(shù)據(jù)集
model.fit(trainX, trainY, validation_set=(testX, testY), show_metric=True,batch_size=32)
到這里整個(gè)代碼就結(jié)束了,這里有兩個(gè)地方需要說(shuō)明一下,一個(gè)是embedding,一個(gè)是fully_connected,分別表示詞嵌套和全連接。這里對(duì)這兩部分簡(jiǎn)要說(shuō)下,不會(huì)進(jìn)行詳盡的公式推導(dǎo)和闡釋。
首先說(shuō)說(shuō)這里的embedding.
在注釋部分已經(jīng)注釋的比較明確了,其目的就是將要表示的東西進(jìn)行向量化表示。原來(lái)每個(gè)字用一個(gè)ID表示,這樣能表示的信息太少了,不能夠表達(dá)詞所在語(yǔ)料內(nèi)更多的意思,比如和那個(gè)詞更相近。通過(guò)詞嵌套將一個(gè)單一的ID表示為一個(gè)128緯度(此處是128)的向量,能夠表達(dá)更多的意思。詞嵌套是向量化的一個(gè)重要手段,這個(gè)技巧一定要掌握的。
再聊聊這里的fully_connected
全連接層(fully connected layers,F(xiàn)C),在整個(gè)神經(jīng)網(wǎng)絡(luò)中起到類(lèi)似于“分類(lèi)器”的作用。如果說(shuō)卷積層、池化層和激活函數(shù)層等操作是將原始數(shù)據(jù)映射到隱層特征空間的話,那么全連接層則起到將學(xué)到的“分布式特征表示”映射到樣本標(biāo)記空間的作用。在代碼中tflearn.fully_connected(net, 2,activation='softmax'),這里的第二個(gè)參數(shù)2,表示的就是輸出神經(jīng)元的個(gè)數(shù),即訓(xùn)練集中對(duì)應(yīng)的打標(biāo)的向量,也就是說(shuō)將學(xué)習(xí)到的分布式特征轉(zhuǎn)化為一個(gè)只包含兩個(gè)元素的向量。
全連接層的每一個(gè)結(jié)點(diǎn)都與上一層的所有結(jié)點(diǎn)相連,用來(lái)把前邊提取到的特征綜合起來(lái)。由于其全相連的特性,一般全連接層的參數(shù)也是最多的。不負(fù)責(zé)任的講,全連接層一般由兩部分組成,即線性部分和非線性部分。線性部分主要做線性轉(zhuǎn)換,輸入用X表示,輸出用Z表示。
線性部分的運(yùn)算方法基本上就是線性加權(quán)求和的感覺(jué),如果對(duì)于一個(gè)輸入向量
x=[x_0,x_1,...x_n]^T,
線性部分的輸出向量是
z=[z_0,z_1,z_2,...z_m]^T,
那么線性部分的參數(shù)就可以想象一個(gè)m*n的矩陣W,再加上一個(gè)偏置項(xiàng)
b=[b_0,...b_m]^T
于是有:
W*x+b=z
對(duì)于非線性部分,那當(dāng)然是做非線性變換了,輸入用線性部分的輸出Z表示,輸出用Y表示,假設(shè)用sigmoid作為非線性激活,那么有
Y = sigmoid(Z)
那么為什么要有非線性部分呢?個(gè)人理解,其一是作數(shù)據(jù)的歸一化。不管前面的線性部分做了怎樣的工作,到了非線性這里,所有的數(shù)值將被限制在一個(gè)范圍內(nèi),這樣后面的網(wǎng)絡(luò)層如果要基于前面層的數(shù)據(jù)繼續(xù)計(jì)算,這個(gè)數(shù)值就相對(duì)可控了。其二就是打破之前的線性映射關(guān)系。如果全連接層沒(méi)有非線性部分,只有線性部分,我們?cè)谀P椭携B加多層神經(jīng)網(wǎng)絡(luò)是沒(méi)有意義的,我們假設(shè)有一個(gè)2層全連接神經(jīng)網(wǎng)絡(luò),其中沒(méi)有非線性層,那么對(duì)于第一層有:
W^0*x^0+b^0=z^1
對(duì)于第二層有:
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ù)后面會(huì)專(zhuān)門(mén)介紹,這里不再贅述。
我們?cè)诳匆幌聇flearn中fully_connected的函數(shù)的參數(shù)解釋?zhuān)?dāng)然tensorflow中也有相關(guān)函數(shù),這里暫不進(jìn)行注解。
tflearn.layers.core.fully_connected
incoming: 輸入Tensor,維度不小于2
n_units: 整型,表示輸出神經(jīng)元個(gè)數(shù)
activation: 激活函數(shù),輸入激活函數(shù)的名稱(chēng)或者函數(shù)定義(自定義非線性激活函數(shù)),默認(rèn)值:'linear',參見(jiàn) tflearn.activations
bias: 布爾值,表示是否使用偏置項(xiàng)
weights_init: 初始化權(quán)重W的參數(shù),String 或者一個(gè)Tensor,默認(rèn)是truncated_normal,參見(jiàn)tflearn.initializations
bias_init: 初始化偏置項(xiàng),String或Tensor,默認(rèn)'zeros'。參見(jiàn)tflearn.initializations
regularizer: 規(guī)范化函數(shù),String或者一個(gè)Tensor,對(duì)權(quán)重W進(jìn)行規(guī)范化操作,默認(rèn)不進(jìn)行,參見(jiàn)tflearn.regularizers
weight_decay: 浮點(diǎn)數(shù),規(guī)范化方法的衰減參數(shù),默認(rèn)值 0.001.
trainable: 可選參數(shù),布爾值,如果為T(mén)rue,那么變量將會(huì)加到圖模型中。同tf.Variable的trainable
restore: bool. If True, this layer weights will be restored when loading a model.
reuse: 可選參數(shù),布爾類(lèi)型,如果指定了Scope且當(dāng)前參數(shù)置為T(mén)rue,那么該layer的變量可復(fù)用
scope: 可選參數(shù),String類(lèi)型,定義layer的Scope(指定Scope可以在不同層間共享變量,但需要注意Scope可被覆蓋,需自行控制其唯一性)
name: 可選值,表示該層的名稱(chēng),默認(rèn)值: 'FullyConnected'.
總結(jié)
基于tflearn進(jìn)行深度學(xué)習(xí)相關(guān)功能的實(shí)現(xiàn),要比基于原生的tensorflow要簡(jiǎn)單的多,封裝的比較干凈。本篇只介紹了使用LSTM進(jìn)行NLP的相關(guān)任務(wù)處理,讀者完全可以自行下載代碼進(jìn)行改造,這里希望讀者能夠了解embedding和fully_connected的意思,知道其作用,如果希望深入了解,可以參考相關(guān)論文和他人注解的blog,本篇就寫(xiě)到這里,歡迎拍磚。