雷鋒網(wǎng)按:本文源自一位數(shù)據(jù)科學(xué)開發(fā)者的個人博客,,雷鋒網(wǎng)編譯,。 許多開發(fā)者向新手建議:如果你想要入門機(jī)器學(xué)習(xí),就必須先了解一些關(guān)鍵算法的工作原理,,然后再開始動手實(shí)踐,。但我不這么認(rèn)為。 我覺得實(shí)踐高于理論,,新手首先要做的是了解整個模型的工作流程,,數(shù)據(jù)大致是怎樣流動的,,經(jīng)過了哪些關(guān)鍵的結(jié)點(diǎn),最后的結(jié)果在哪里獲取,,并立即開始動手實(shí)踐,,構(gòu)建自己的機(jī)器學(xué)習(xí)模型。至于算法和函數(shù)內(nèi)部的實(shí)現(xiàn)機(jī)制,,可以等了解整個流程之后,,在實(shí)踐中進(jìn)行更深入的學(xué)習(xí)和掌握。 那么問題來了,,既然作為初學(xué)者不需要掌握算法細(xì)節(jié),,但實(shí)現(xiàn)模型的過程中又必須用到相關(guān)算法,怎么辦呢,?答案是借助于互聯(lián)網(wǎng)上已經(jīng)實(shí)現(xiàn)好的函數(shù)庫,,例如 TensorFlow。 在本文中,,我們將利用 TensorFlow 實(shí)現(xiàn)一個基于深度神經(jīng)網(wǎng)絡(luò)(DNN)的文本分類模型,,希望對各位初學(xué)者有所幫助。文中所涉完整代碼已經(jīng)在 GitHub 上開源,,感興趣的朋友可以在以下鏈接中下載: http:///RXiP3Om 下面是正式的教程內(nèi)容: 關(guān)于 TensorFlowTensorFlow 是谷歌旗下一個開源的機(jī)器學(xué)習(xí)框架,。從它的名字就能看出這個框架基本的工作原理:由多維數(shù)組構(gòu)成的張量(tensor)在圖(graph)結(jié)點(diǎn)之間定向流動(flow),從輸入走到輸出,。 在 TensorFlow 中,,每次運(yùn)算都可以用數(shù)據(jù)流圖(dataflow graph)的方式表示。每個數(shù)據(jù)流圖都有以下兩個重要元素:
如下圖所示,,這里我們以一個簡單的例子說明什么計算流圖具體是怎樣運(yùn)行的。 假設(shè)圖中的 x=[1,3,6],,y=[1,1,1],。由于 tf.Tensor 被用來表示運(yùn)算數(shù)據(jù),因此在 TensorFlow 中我們會首先定義兩個 tf.Tensor 常量對象存放數(shù)據(jù),。然后再用 tf.Operation 對象定義圖中的加法運(yùn)算,,具體代碼如下:
現(xiàn)在,我們已經(jīng)定義了數(shù)據(jù)流圖的兩個重要元素:tf.Operation 和 tf.Tensor,,那么如何構(gòu)建圖本身呢,,具體代碼如下:
至此我們已經(jīng)完成了數(shù)據(jù)流圖的定義,,在 TensorFlow 中,,只有先定義了圖,,才能進(jìn)行后續(xù)的計算操作(即驅(qū)動數(shù)據(jù)在圖的結(jié)點(diǎn)間定向流動)。這里 TensorFlow 又規(guī)定,,要進(jìn)行后續(xù)的計算,,必須通過 tf.Session 來統(tǒng)一管理,因此我們還要定義一個 tf.Session 對象,,即會話,。 在 TensorFlow 中,tf.Session 專門用來封裝 tf.Operation 在 tf.Tensor 基礎(chǔ)上執(zhí)行的操作環(huán)境,。因此,,在定義 tf.Session 對象時,也需要傳入相應(yīng)的數(shù)據(jù)流圖(可以通過 graph 參數(shù)傳入),,本例中具體的代碼如下:
定義好 tf.Session 之后,,我們可以通過 tf.Session.run 方法來執(zhí)行對應(yīng)的數(shù)據(jù)流圖。run 方法可以通過 fetches 參數(shù)傳入相應(yīng) tf.Operation 對象,,并導(dǎo)入與 tf.Operation 相關(guān)的所有 tf.Tensor 對象,,然后遞歸執(zhí)行與當(dāng)前 tf.Operation 有依賴關(guān)系的所有操作。本例中具體執(zhí)行的是求和操作,,實(shí)現(xiàn)代碼如下:
可以看到運(yùn)算結(jié)果是 [2 4 7],。 關(guān)于預(yù)測模型了解 TensorFlow 的基本原理之后,下面的任務(wù)是如何構(gòu)建一個預(yù)測模型,。簡單來說,,機(jī)器學(xué)習(xí)算法 + 數(shù)據(jù)就等于預(yù)測模型。構(gòu)建預(yù)測模型的流程如下圖所示: 如圖,,經(jīng)過數(shù)據(jù)訓(xùn)練的機(jī)器學(xué)習(xí)算法就是模型,。訓(xùn)練好一個模型之后,輸入待預(yù)測數(shù)據(jù),,就能得到相應(yīng)的預(yù)測結(jié)果,。大體流程如下圖所示: 在本例中,我們將要構(gòu)建的模型需要根據(jù)輸入文本,,輸出相應(yīng)的類別,,即完成文本分類的工作。因此這里的輸入應(yīng)該是文本(text),,輸出是類別(category),。更具體地說,本例中我們已經(jīng)事先獲取了標(biāo)記數(shù)據(jù)(即一些已經(jīng)標(biāo)明了類別的文本段),,然后用這些數(shù)據(jù)對算法進(jìn)行訓(xùn)練,,最后再用訓(xùn)練好的模型對新文本分類。這一過程也就是通常所說的監(jiān)督學(xué)習(xí)(supervised learning),。另外,,由于我們的任務(wù)是對文本數(shù)據(jù)進(jìn)行分類,,所以也屬于分類問題的范疇。 為了構(gòu)建該文本分類模型,,下面我們需要介紹一些神經(jīng)網(wǎng)絡(luò)的基礎(chǔ)知識,。 關(guān)于神經(jīng)網(wǎng)絡(luò)從本質(zhì)上說,神經(jīng)網(wǎng)絡(luò)是計算模型(computational model)的一種,。(注:這里所謂計算模型是指通過數(shù)學(xué)語言和數(shù)學(xué)概念描述系統(tǒng)的方法)并且這種計算模型還能夠自動完成學(xué)習(xí)和訓(xùn)練,,不需要精確編程。 最原始也是最基礎(chǔ)的一個神經(jīng)網(wǎng)絡(luò)算法模型是感知機(jī)模型(Perceptron),,關(guān)于感知機(jī)模型的詳細(xì)介紹請參見這篇博客: http:///R5MphRp 由于神經(jīng)網(wǎng)絡(luò)模型是模擬人類大腦神經(jīng)系統(tǒng)的組織結(jié)構(gòu)而提出的,,因此它與人類的腦神經(jīng)網(wǎng)絡(luò)具有相似的結(jié)構(gòu)。 如上圖所示,,一般的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)可以分為三層:輸入層,、隱蔽層(hidden layer)和輸出層。 為了深入理解神經(jīng)網(wǎng)絡(luò)究竟是如何工作的,,我們需要利用 TensorFlow 自己親手構(gòu)建一個神經(jīng)網(wǎng)絡(luò)模型,,下面介紹一個具體的實(shí)例。(本例部分內(nèi)容源自 GitHub 上的一段開源代碼:鏈接) 本例中,,我們有兩個隱蔽層(關(guān)于隱蔽層層數(shù)的選擇是另一個問題,,更詳細(xì)的內(nèi)容可以參考:鏈接)。概括地說,,隱蔽層的主要作用是將輸入層的數(shù)據(jù)轉(zhuǎn)換成一種輸出層更便于利用的形式,。 如圖所示,本例中輸入層的每個結(jié)點(diǎn)都代表了輸入文本中的一個詞,,接下來是第一個隱蔽層,。這里需要注意的是,第一層隱蔽層的結(jié)點(diǎn)個數(shù)選擇也是一項重要的任務(wù),,通常被稱為特征選擇,。 圖中的每個結(jié)點(diǎn)(也被稱為神經(jīng)元),都會搭配一個權(quán)重,。而我們下面所謂訓(xùn)練過程其實(shí)就是不斷調(diào)整這些權(quán)重值,,讓模型的實(shí)際輸出和預(yù)想輸出更匹配的過程。當(dāng)然,,除了權(quán)重之外,,整個網(wǎng)絡(luò)還要加上一個偏差值。(關(guān)于偏差的詳細(xì)介紹詳見:鏈接) 對每個結(jié)點(diǎn)做加權(quán)和并加上一個偏差值之后,,還需要經(jīng)過激活函數(shù)(activation function)的處理才能輸出到下一層 ,。實(shí)際上,這里激活函數(shù)確定了每個結(jié)點(diǎn)的最終輸出情況,同時為整個模型加入了非線性元素,。如果用臺燈來做比喻的話,,激活函數(shù)的作用就相當(dāng)于開關(guān)。實(shí)際研究中根據(jù)應(yīng)用的具體場景和特點(diǎn),,有各種不同的激活函數(shù)可供選擇,,這里屏蔽層選擇的是 ReLu 函數(shù),。 另外圖中還顯示了第二個隱蔽層,,它的功能和第一層并沒有本質(zhì)區(qū)別,唯一的不同就是它的輸入是第一層的輸出,,而第一層的輸入則是原始數(shù)據(jù),。 最后是輸出層,本例中應(yīng)用了獨(dú)熱編碼的方式來對結(jié)果進(jìn)行分類,。這里所謂獨(dú)熱編碼是指每個向量中只有一個元素是 1,,其他均為 0 的編碼方式。例如我們要將文本數(shù)據(jù)分為三個類別(體育,、航空和電腦繪圖),,則編碼結(jié)果為: 這里獨(dú)熱編碼的好處是:輸出結(jié)點(diǎn)的個數(shù)恰好等于輸出類別的個數(shù)。此外,,輸出層和前面的隱蔽層結(jié)構(gòu)類似,,我們也要為每個結(jié)點(diǎn)搭配一個權(quán)重值,加上恰當(dāng)?shù)钠?,最后通過激活函數(shù)的處理,。 但本例中輸出層的激活函數(shù)與隱蔽層的激活函數(shù)不同。由于本例的最終目的是輸出每個文本對應(yīng)的類別信息,,而這里所有類別之間又是互斥的關(guān)系,。基于這些特點(diǎn),,我們在輸出層選擇了 Softmax 函數(shù)作為激活函數(shù),。該函數(shù)的特點(diǎn)是可以將輸出值轉(zhuǎn)換為 0-1 之間的一個小數(shù)值,并且這些小數(shù)值的和為 1,。于是正好可以用這些小數(shù)表示每個類別的可能性分布情況,。假如剛才提到的三個類別原本的輸出值為 1.2、0.9 和 0.4,,則通過 Softmax 函數(shù)的處理后,,得到的結(jié)果為: 可以看到這三個小數(shù)的和正好為 1。 到目前為止,,我們已經(jīng)明確了該神經(jīng)網(wǎng)絡(luò)的數(shù)據(jù)流圖,,下面為具體的代碼實(shí)現(xiàn): 神經(jīng)網(wǎng)絡(luò)的訓(xùn)練 如前所述,模型訓(xùn)練中一項非常重要的任務(wù)就是調(diào)整結(jié)點(diǎn)的權(quán)重。本節(jié)我們將介紹如何在 TensorFlow 中實(shí)現(xiàn)這一過程,。 在 TensorFlow 中,,結(jié)點(diǎn)權(quán)重和偏差值以變量的形式存儲,即 tf.Variable 對象,。在數(shù)據(jù)流圖調(diào)用 run 函數(shù)的時候,,這些值將保持不變。在一般的機(jī)器學(xué)習(xí)場景中,,權(quán)重值和偏差值的初始取值都通過正太分布確定,。具體代碼如下圖所示:
以初始值運(yùn)行神經(jīng)網(wǎng)絡(luò)之后,會得到一個實(shí)際輸出值 z,,而我們的期望輸出值是 expected,,這時我們需要做的就是計算兩者之間的誤差,并通過調(diào)整權(quán)重等參數(shù)使之最小化,。一般計算誤差的方法有很多,,這里因為我們處理的是分類問題,因此采用交叉熵誤差,。(關(guān)于為什么分類問題選用交叉熵,,參見:連接) 在 TensorFlow 中,我們可以通過調(diào)用 tf.nn.softmax_cross_entropy_with_logits 函數(shù)來計算交叉熵誤差,,因為這里我們的激活函數(shù)選擇了 Softmax ,,因此誤差函數(shù)中出現(xiàn)了 softmax_ 前綴。具體代碼如下(代碼中我們同時調(diào)用了 tf.reduced_mean() 函數(shù)來計算平均誤差):
得到誤差之后,,下面的任務(wù)是如何使之最小化,。這里我們選擇的方法是最常用的隨機(jī)梯度下降法,其直觀的原理圖如下所示: 同樣,,用來計算梯度下降的方法也有很多,,這里我們采用了 Adaptive Moment Estimation (Adam) 優(yōu)化法,即自適應(yīng)矩估計的優(yōu)化方法,,具體在 TensorFlow 中的體現(xiàn)是 tf.train.AdamOptimizer(learning_rate).minimize(loss) 函數(shù),。這里我們需要傳入 learning_rate 參數(shù)以決定計算梯度時的步進(jìn)長度。 非常方便的一點(diǎn)是,,AdamOptimizer 函數(shù)封裝了兩種功能:一是計算梯度,,二是更新梯度。換句話說,,調(diào)用該函數(shù)不但能計算梯度值,,還能將計算結(jié)果更新到所有 tf.Variables 對象中,這一點(diǎn)大大降低了編程復(fù)雜度,。 具體模型訓(xùn)練部分的代碼如下所示: 數(shù)據(jù)處理 本例中,,我們得到的原始數(shù)據(jù)是許多英文的文本片段,為了將這些數(shù)據(jù)導(dǎo)入模型中,我們需要對原始數(shù)據(jù)進(jìn)行必要的預(yù)處理過程,。這里具體包括兩個部分:
具體實(shí)現(xiàn)代碼如下:
從以上代碼可以看到,當(dāng)輸入文本是“Hi from Brazil”時,,輸出矩陣是 [ 1. 1. 1.],。而當(dāng)輸入文本只有“Hi”時又會怎么樣呢,具體代碼和結(jié)果如下:
可以看到,,這時的輸出是 [ 1. 0. 0.],。 相應(yīng)的,,我們也可以對類別信息進(jìn)行編碼,,只不過這時使用的是獨(dú)熱編碼: 運(yùn)行模型并預(yù)測 至此我們已經(jīng)對 TensorFlow、神經(jīng)網(wǎng)絡(luò)模型,、模型訓(xùn)練和數(shù)據(jù)預(yù)處理等方面有了初步的了解,,下面我們將演示如何將這些知識應(yīng)用于實(shí)際的數(shù)據(jù)。 這里我們的數(shù)據(jù)來源是 20 Newsgroups,,其中包括了 18000 篇新聞稿,,覆蓋率 20 個類別,開源免費(fèi),,下載地址為: http:///zY6ssrE 首先,,為了導(dǎo)入這些數(shù)據(jù)集,我們需要借助 scikit-learn 庫,。它也是個開源的函數(shù)庫,,基于 Python 語言,主要進(jìn)行機(jī)器學(xué)習(xí)相關(guān)的數(shù)據(jù)處理任務(wù),。本例中我們只使用了其中的三個類:comp.graphics,,sci.space 和 rec.sport.baseball。 最終數(shù)據(jù)會被分為兩個子集,,一個是數(shù)據(jù)訓(xùn)練集,,一個是測試集。這里的建議是最好不要提前查看測試數(shù)據(jù)集,。因為提前查看測試數(shù)據(jù)會影響我們對模型參數(shù)的選擇,,從而影響模型對其他未知數(shù)據(jù)的通用性。 具體的數(shù)據(jù)導(dǎo)入代碼如下:
在神經(jīng)網(wǎng)絡(luò)術(shù)語中,,一個 epoch 過程就是對所有訓(xùn)練數(shù)據(jù)的一個前向傳遞(forward pass)加后向傳遞(backward pass)的完整循環(huán),。這里前向是指根據(jù)現(xiàn)有權(quán)重得到實(shí)際輸出值的過程,后向是指根據(jù)誤差結(jié)果反過來調(diào)整權(quán)重的過程。下面我們重點(diǎn)介紹一下 tf.Session.run 函數(shù),,實(shí)際上它的完整調(diào)用形式如下:
在文章開頭介紹該函數(shù)時,,我們只通過 fetches 參數(shù)傳入了加法操作,但其實(shí)它還支持一次傳入多種操作的用法,。在面向?qū)嶋H數(shù)據(jù)的模型訓(xùn)練環(huán)節(jié),,我們就傳入了兩種操作:一個是誤差計算(即隨機(jī)梯度下降),另一個是優(yōu)化函數(shù)(即自適應(yīng)矩估計),。 run 函數(shù)中另一個重要的參數(shù)是 feed_dict,,我們就是通過這個參數(shù)傳入模型每次處理的輸入數(shù)據(jù)。而為了輸入數(shù)據(jù),,我們又必須先定義 tf.placeholders,。 按照官方文檔的解釋,這里 placeholder 僅僅是一個空客,,用于引用即將導(dǎo)入模型的數(shù)據(jù),,既不需要初始化,也不存放真實(shí)的數(shù)據(jù),。本例中定義 tf.placeholders 的代碼如下:
在進(jìn)行實(shí)際的模型訓(xùn)練之前,,還需要將數(shù)據(jù)分成 batch,即一次計算處理數(shù)據(jù)的量,。這時就體現(xiàn)了之前定義 tf.placeholders 的好處,,即可以通過 placeholders 定義中的“None”參數(shù)指定一個維度可變的 batch。也就是說,,batch 的具體大小可以等后面使用時再確定,。這里我們在模型訓(xùn)練階段傳入的 batch 更大,而測試階段可能會做一些改變,,因此需要使用可變 batch,。隨后在訓(xùn)練中,我們通過 get_batches 函數(shù)來獲取每次處理的真實(shí)文本數(shù)據(jù),。具體模型訓(xùn)練部分的代碼如下:
至此我們已經(jīng)針對實(shí)際數(shù)據(jù)完成了模型訓(xùn)練,,下面到了應(yīng)用測試數(shù)據(jù)對模型進(jìn)行測試的時候。在測試過程中,,和之前的訓(xùn)練部分類似,,我們同樣要定義圖元素,包括操作和數(shù)據(jù)兩類,。這里為了計算模型的精度,,同時還因為我們對結(jié)果引入了獨(dú)熱編碼,因此需要同時得到正確輸出的索引,,以及預(yù)測輸出的索引,,并檢查它們是否相等,,如果不等,要計算相應(yīng)的平均誤差,。具體實(shí)現(xiàn)代碼和結(jié)果如下:
最終可以看到,,我們的模型預(yù)測精度達(dá)到了 75%,對于初學(xué)者而言,,這個成績還是不錯的,。至此,我們已經(jīng)通過 TensorFlow 實(shí)現(xiàn)了基于神經(jīng)網(wǎng)絡(luò)模型的文本分類任務(wù),。 來源:medium,,雷鋒網(wǎng)編譯 |
|