神經(jīng)網(wǎng)絡(luò)(四):應(yīng)用示例之分類

一膏蚓、 傳統(tǒng)分類模型的局限

在之前的文章中(《神經(jīng)網(wǎng)絡(luò)(一)》瓢谢、《神經(jīng)網(wǎng)絡(luò)(二)》《神經(jīng)網(wǎng)絡(luò)(三)》),我們討論的重點是神經(jīng)網(wǎng)絡(luò)的理論知識⊥郧疲現(xiàn)在來看一個實際的例子,如何利用神經(jīng)網(wǎng)絡(luò)解決分類問題论笔。(為了更好地展示神經(jīng)網(wǎng)絡(luò)的特點翅楼,我們在這個示例中并不劃分訓(xùn)練集和測試集)。

分類是機(jī)器學(xué)習(xí)最常見的應(yīng)用之一管嬉,之前的章節(jié)也討論過很多解決分類問題的機(jī)器學(xué)習(xí)模型朗鸠,比如邏輯回歸和支持向量學(xué)習(xí)機(jī)等烛占。但這些模型最大的局限性是它們都有比較明確的適用范圍胎挎,如果訓(xùn)練數(shù)據(jù)符合模型的假設(shè)沟启,則分類效果很好。否則犹菇,分類的效果就會很差德迹。

比如圖1[1]中展示了4種不同分布類型的數(shù)據(jù)。具體來說揭芍,數(shù)據(jù)里有兩個自變量胳搞,分別對應(yīng)著坐標(biāo)系的橫縱軸;數(shù)據(jù)分為兩類称杨,在圖中用三角形表示類別0肌毅,用圓點表示類別1。如果使用邏輯回歸對數(shù)據(jù)進(jìn)行分類姑原,只有圖中標(biāo)記1中的模型效果較好(圖中的灰色區(qū)域里悬而,模型的預(yù)測結(jié)果是類別0;白色區(qū)域里锭汛,模型的預(yù)測結(jié)果是類別1)摊滔,因為在已知類別的情況下,數(shù)據(jù)服從正態(tài)分布(不同類別店乐,分布的中心不同)艰躺,符合邏輯回歸的模型假設(shè)。對于標(biāo)記2眨八、3腺兴、4中的數(shù)據(jù),由于類別與自變量之間的關(guān)系是非線性的廉侧,如果想取得比較好的分類效果页响,則需要其他的建模技巧。比如先使用核函數(shù)對數(shù)據(jù)進(jìn)行升維段誊,再使用支持向量學(xué)習(xí)機(jī)進(jìn)行分類闰蚕。

圖1

二、 神經(jīng)網(wǎng)絡(luò)的優(yōu)勢

這樣的建模方法是比較辛苦的连舍,要求搭建模型的數(shù)據(jù)科學(xué)家對不同模型的假設(shè)以及優(yōu)缺點有比較深刻的理解没陡。但如果使用神經(jīng)網(wǎng)絡(luò)對數(shù)據(jù)進(jìn)行分類,則整個建模過程就比較輕松了索赏,只需設(shè)計神經(jīng)網(wǎng)絡(luò)的形狀(包括神經(jīng)網(wǎng)絡(luò)的層數(shù)以及每一層里的神經(jīng)元個數(shù))盼玄,然后將數(shù)據(jù)輸入給模型即可。
在這個例子中潜腻,使用的神經(jīng)網(wǎng)絡(luò)如圖2所示埃儿,是一個3-層的全連接神經(jīng)網(wǎng)絡(luò)。

圖2

使用這個神經(jīng)網(wǎng)絡(luò)對數(shù)據(jù)進(jìn)行分類融涣,得到的結(jié)果如圖3所示童番,可以看到同一個神經(jīng)網(wǎng)絡(luò)(結(jié)構(gòu)相同精钮,但具體的模型參數(shù)是不同的)對4種不同分布類型的數(shù)據(jù)都能較好地進(jìn)行分類。

圖3

三剃斧、 代碼實現(xiàn)(完整的代碼請見)

這一節(jié)節(jié)將討論如何借助第三方庫TensorFlow來實現(xiàn)神經(jīng)網(wǎng)絡(luò)杂拨,。

第一步是定義神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)悯衬,如程序清單1所示弹沽。

  1. 我們使用類(class)來實現(xiàn)神經(jīng)網(wǎng)絡(luò),如第4行代碼所示筋粗。在Python的類中可以定義相應(yīng)的函數(shù)策橘,但在類中,函數(shù)的定義與普通函數(shù)的定義有所不同娜亿,它的參數(shù)個數(shù)必須大于1丽已,且第一個參數(shù)表示類本身,如第7行代碼里的“self”變量买决。但在調(diào)用這個函數(shù)時沛婴,卻不需要“手動”地傳入這個參數(shù),Python會自動地進(jìn)行參數(shù)傳遞督赤,比如defineANN函數(shù)的調(diào)用方式是“defineANN()”嘁灯。
  2. 在ANN類中,“self.input”對應(yīng)著訓(xùn)練數(shù)據(jù)里的自變量(它的類型是tf.placeholder)躲舌,如第12行代碼所示丑婿,“self.input.shape[1].value”表示輸入層的神經(jīng)元個數(shù)(針對如圖2的神經(jīng)網(wǎng)絡(luò),這個值等于2)没卸。而“self.size”是表示神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的數(shù)組(針對如圖2的神經(jīng)網(wǎng)絡(luò)羹奉,這個值等于[4, 4, 2])。在ANN類中约计,“self.input”對應(yīng)著訓(xùn)練數(shù)據(jù)里的自變量(它的類型是tf.placeholder)诀拭,如第12行代碼所示,“self.input.shape[1].value”表示輸入層的神經(jīng)元個數(shù)(針對如圖12-8的神經(jīng)網(wǎng)絡(luò)煤蚌,這個值等于2)耕挨。而“self.size”是表示神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的數(shù)組(針對如圖2的神經(jīng)網(wǎng)絡(luò),這個值等于[4, 4, 2])铺然。
  3. 接下來是定義網(wǎng)絡(luò)的隱藏層俗孝。首先是神經(jīng)元里的線性模型部分,如第18~21行代碼所示魄健,定義權(quán)重項“weights”和截距項“biases”。因此插勤,權(quán)重項是一個的矩陣沽瘦,而截距項是一個維度等于的行向量革骨。值得注意的是,在定義權(quán)重項時析恋,使用tf.truncated_normal函數(shù)(近似地對應(yīng)著正態(tài)分布)來生成初始值良哲,在生成初始值的過程中,我們用如下的命令來規(guī)定分布的標(biāo)準(zhǔn)差“stddev=1.0 / np.sqrt(float(prevSize))”助隧,這樣操作的原因是為了使神經(jīng)網(wǎng)絡(luò)更快收斂筑凫。定義好線性模型后,就需要定義神經(jīng)元的激活函數(shù)并村,如第22行代碼所示巍实,使用的激活函數(shù)是tf.nn.sigmoid,它對應(yīng)著sigmoid函數(shù)哩牍。
  4. 最后是定義神經(jīng)網(wǎng)絡(luò)的輸出層棚潦,如第25~29行代碼所示。具體的過程和隱藏層類似膝昆,唯一不同的是丸边,輸出層并沒有激活函數(shù),因此只需定義線性模型部分“tf.matmul(prevOut, weights) + biases”荚孵。

程序清單1 定義神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)

 1  |  import numpy as np
 2  |  import tensorflow as tf
 3  |  
 4  |  class ANN(object):
 5  |      # 省略掉其他部分
 6  |  
 7  |      def defineANN(self):
 8  |          """
 9  |          定義神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)
10  |          """
11  |          # self.input是訓(xùn)練數(shù)據(jù)里自變量
12  |          prevSize = self.input.shape[1].value
13  |          prevOut = self.input
14  |          # self.size是神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)妹窖,也就是每一層的神經(jīng)元個數(shù)
15  |          size = self.size
16  |          # 定義隱藏層
17  |          for currentSize in size[:-1]:
18  |              weights = tf.Variable(
19  |                  tf.truncated_normal([prevSize, currentSize],
20  |                      stddev=1.0 / np.sqrt(float(prevSize))))
21  |              biases = tf.Variable(tf.zeros([currentSize]))
22  |              prevOut = tf.nn.sigmoid(tf.matmul(prevOut, weights) + biases)
23  |              prevSize = currentSize
24  |          # 定義輸出層
25  |          weights = tf.Variable(
26  |              tf.truncated_normal([prevSize, size[-1]],
27  |                  stddev=1.0 / np.sqrt(float(prevSize))))
28  |          biases = tf.Variable(tf.zeros([size[-1]]))
29  |          self.out = tf.matmul(prevOut, weights) + biases
30  |          return self

第二步是定義神經(jīng)網(wǎng)絡(luò)的損失函數(shù),如程序清單2所示收叶。

  1. 在ANN類中嘱吗,“self.label”對應(yīng)著訓(xùn)練數(shù)據(jù)里的標(biāo)簽變量(它的類型是tf.placeholder)。值得注意的是滔驾,這里用到的標(biāo)簽變量是使用One-Hot Encoding(獨熱編碼)處理過的谒麦。比如針對圖1中的數(shù)據(jù),每個數(shù)據(jù)的標(biāo)簽變量是二維的行向量哆致,用表示類別0绕德,用表示類別1。
  2. 在ANN類中摊阀,“self.out”對應(yīng)著神經(jīng)網(wǎng)絡(luò)的輸出層耻蛇,具體的定義如程序清單2中的第29行代碼所示。
  3. 根據(jù)《神經(jīng)網(wǎng)絡(luò)(一)》胞此、《神經(jīng)網(wǎng)絡(luò)(二)》和《神經(jīng)網(wǎng)絡(luò)(三)》中的討論結(jié)果臣咖,神經(jīng)網(wǎng)絡(luò)的單點損失的實現(xiàn)如第9、10行代碼所示漱牵,其中夺蛇,“self.out”對應(yīng)著公式里的變量。
  4. 模型的整體損失等于所有單點損失之和酣胀,相應(yīng)的實現(xiàn)如第12行代碼所示刁赦。

程序清單2 定義神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)

 1  |  class ANN(object):
 2  |      # 省略掉其他部分
 3  |  
 4  |      def defineLoss(self):
 5  |          """
 6  |          定義神經(jīng)網(wǎng)絡(luò)的損失函數(shù)
 7  |          """
 8  |          # 定義單點損失娶聘,self.label是訓(xùn)練數(shù)據(jù)里的標(biāo)簽變量
 9  |          loss = tf.nn.softmax_cross_entropy_with_logits(
10  |              labels=self.label, logits=self.out, name="loss")
11  |          # 定義整體損失
12  |          self.loss = tf.reduce_mean(loss, name="average_loss")
13  |          return self

第三步是訓(xùn)練神經(jīng)網(wǎng)絡(luò),如程序清單3所示甚脉。

  1. 從理論上來講丸升,訓(xùn)練神經(jīng)網(wǎng)絡(luò)的算法是之后將討論的反向傳播算法,這個算法的基礎(chǔ)是隨機(jī)梯度下降法(stochastic gradient descent)牺氨。由于TensorFlow已經(jīng)將整個算法包裝好了狡耻,如第8~23行代碼所示。限于篇幅猴凹,實現(xiàn)的具體細(xì)節(jié)在此就不再重復(fù)了夷狰。

  2. 如果將訓(xùn)練過程的模型損失(隨訓(xùn)練輪次的變化曲線)記錄下來,可以得到如圖4所示的圖像精堕,其中曲線的標(biāo)記對應(yīng)著訓(xùn)練數(shù)據(jù)的標(biāo)記孵淘。從圖中的結(jié)果可以看到,對于不同類型的數(shù)據(jù)歹篓,模型損失函數(shù)的變化曲線是不一樣的瘫证。對于比較難訓(xùn)練的數(shù)據(jù)(標(biāo)記3),模型的損失經(jīng)歷了一個很漫長的訓(xùn)練瓶頸期庄撮。也就是說背捌,雖然模型并沒有達(dá)到收斂狀態(tài),但在較長的訓(xùn)練周期里洞斯,模型效果幾乎沒有提升毡庆。這種現(xiàn)象其實是神經(jīng)網(wǎng)絡(luò)研究領(lǐng)域里最大的難點,它使得神經(jīng)網(wǎng)絡(luò)的訓(xùn)練(特別是層數(shù)較多深度神經(jīng)網(wǎng)絡(luò))變得極其困難烙如,一方面瓶頸期會使模型的訓(xùn)練變得非常漫長么抗;另一方面,在實際應(yīng)用中亚铁,當(dāng)模型損失不再大幅變動時蝇刀,我們很難判斷這是因為模型到達(dá)了收斂狀態(tài)還是因為模型進(jìn)入了瓶頸期[2]。引起瓶頸期這種現(xiàn)象的原因有很多徘溢,我們將在后面的文章中重點討論這部分內(nèi)容吞琐。

圖4

程序清單3 訓(xùn)練模型

1  |  class ANN(object):
2  |      # 省略掉其他部分
3  |  
4  |      def SGD(self, X, Y, learningRate, miniBatchFraction, epoch):
5  |          """
6  |          使用隨機(jī)梯度下降法訓(xùn)練模型
7  |          """
8  |          method = tf.train.GradientDescentOptimizer(learningRate)
9  |          optimizer= method.minimize(self.loss)
10  |          batchSize = int(X.shape[0] * miniBatchFraction)
11  |          batchNum = int(np.ceil(1 / miniBatchFraction))
12  |          sess = tf.Session()
13  |          init = tf.global_variables_initializer()
14  |          sess.run(init)
15  |          step = 0
16  |          while (step < epoch):
17  |              for i in range(batchNum):
18  |                  batchX = X[i * batchSize: (i + 1) * batchSize]
19  |                  batchY = Y[i * batchSize: (i + 1) * batchSize]
20  |                  sess.run([optimizer],
21  |                      feed_dict={self.input: batchX, self.label: batchY})
22  |              step += 1
23  |          self.sess = sess
24  |          return self

神經(jīng)網(wǎng)絡(luò)訓(xùn)練好之后,就可以使用它對未知數(shù)據(jù)做預(yù)測然爆,如程序清單4所示站粟。根據(jù)前面的討論,對神經(jīng)網(wǎng)絡(luò)的輸出層使用softmax函數(shù)曾雕,就可以得到每個類別的預(yù)測概率奴烙,具體的實現(xiàn)如第9、10行代碼所示。

程序清單4 對未知數(shù)據(jù)做預(yù)測

 1  |  class ANN(object):
 2  |      # 省略掉其他部分
 3  |  
 4  |      def predict_proba(self, X):
 5  |          """
 6  |          使用神經(jīng)網(wǎng)絡(luò)對未知數(shù)據(jù)進(jìn)行預(yù)測
 7  |          """
 8  |          sess = self.sess
 9  |          pred = tf.nn.softmax(logits=self.out, name="pred")
10  |          prob = sess.run(pred, feed_dict={self.input: X})
11  |          return prob

四缸沃、廣告時間

這篇文章的大部分內(nèi)容參考自我的新書《精通數(shù)據(jù)科學(xué):從線性回歸到深度學(xué)習(xí)》恰起。

李國杰院士和韓家煒教授在讀過此書后修械,親自為其作序趾牧,歡迎大家購買。

另外肯污,與之相關(guān)的免費視頻課程請關(guān)注這個鏈接


  1. 例子參考自GitHub上的開源項目tensorflow/playground翘单。完整的實現(xiàn)請請參考隨書配套的代碼/ch12-ann/ classification_example.py ?

  2. 雖然對于特定的應(yīng)用場景,我們在數(shù)學(xué)上可以找到一些判斷瓶頸期的依據(jù)蹦渣,但從整體上來說并沒有特別通用的辦法哄芜,這一點也顯示了人類對神經(jīng)網(wǎng)絡(luò)的理解是十分薄弱的 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市柬唯,隨后出現(xiàn)的幾起案子认臊,更是在濱河造成了極大的恐慌,老刑警劉巖锄奢,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件失晴,死亡現(xiàn)場離奇詭異,居然都是意外死亡拘央,警方通過查閱死者的電腦和手機(jī)涂屁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灰伟,“玉大人拆又,你說我怎么就攤上這事±刚耍” “怎么了帖族?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挡爵。 經(jīng)常有香客問我竖般,道長,這世上最難降的妖魔是什么了讨? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任捻激,我火速辦了婚禮,結(jié)果婚禮上前计,老公的妹妹穿的比我還像新娘胞谭。我一直安慰自己,他們只是感情好男杈,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布丈屹。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旺垒。 梳的紋絲不亂的頭發(fā)上彩库,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音先蒋,去河邊找鬼骇钦。 笑死,一個胖子當(dāng)著我的面吹牛竞漾,可吹牛的內(nèi)容都是我干的眯搭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼业岁,長吁一口氣:“原來是場噩夢啊……” “哼鳞仙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笔时,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤棍好,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后允耿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體借笙,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年右犹,在試婚紗的時候發(fā)現(xiàn)自己被綠了提澎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡念链,死狀恐怖盼忌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掂墓,我是刑警寧澤谦纱,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站君编,受9級特大地震影響跨嘉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吃嘿,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一祠乃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兑燥,春花似錦亮瓷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚓胸。三九已至,卻和暖如春除师,著一層夾襖步出監(jiān)牢的瞬間沛膳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工汛聚, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留锹安,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓贞岭,卻偏偏與公主長得像八毯,于是被迫代替她去往敵國和親搓侄。 傳聞我的和親對象是個殘疾皇子瞄桨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容