在前面的部落格中我們已經討論過softmax實現分類的例子,該模型最大的特點是簡單易用,但是擬合能力不強。它和傳統意義上的神經網路的最大區別是沒有隱含層。

對於神經網路來說,引入非線性隱含層後,理論上只要隱含節點足夠多,即使只有一個隱含層的神經網路也可以擬合任意函式。同時隱含層越多,越容易擬合複雜函式。理論表明,為了擬合複雜函式需要的隱含節點的數目,基本上隨著隱含層的數量增加呈現指數下降的趨勢。這也是深度學習的特點之一,層數越深,概念越抽象,需要的隱含節點就越少。

對於神經來說,一般有兩種實現方式:
1)設計隱含層少,但節點數多的神經網路(寬而淺的神經網路);
2)設計隱含層多,但每層節點數目少的神經網路(窄而深的神經網路)。
相對來說,第二種的設計方法使用居多。但是使用層數較深的神經網路往往會遇到很多問題,比如過擬合引數難以除錯梯度彌散等等。下面我們簡單介紹一下這三個問題,以及解決的方法。

過擬合,是指該模型在訓練集上的準確率升高,但是在測試集上的準確率反而下降,這意味著泛化能力不好。具體的過擬合現象可以參考前面講述的部落格
過擬合的解決思路:為了解決這個問題,Hinton提出了一個簡單但是非常有效的方法——dropout。它的大致思路為:在訓練時,將神經網路某一層的輸出節點資料隨機丟棄一部分。可以理解為我們隨機把一張圖片50%的點刪掉(即隨機將50%的點變成黑的的點),此時人可以識別出這張影象的類別,同理機器也可以識別出。該方法的實質是等於創造出了很多隨機樣本,通過增加樣本量、減少特徵數量來防止過擬合。 Dropout也可以看做bagging方法,可以理解為每次丟棄的節點資料是對特徵的一種取樣。也就是相當於我們訓練了一個ensemble的神經網路的模型,對每個特徵都做特徵取樣,只不過沒有訓練多個神經網路模型,只有一個融合的神經網路。

引數難以除錯,尤其對於隨機梯度下降(SGD)的引數,對SGD設定不同的學習速率,最後得到的結果可能差異巨大。神經網路通常不是一個凸優化的問題,它處處充滿的區域性最優。同時SGD本身也不是一個穩定的演算法,結果可能會在最優解附近波動,而不同的學習速率可能導致神經網路落入截然不同的區域性最優之中。但是對於神經網路來說,可能有很多個區域性最優解都可以達到比較好的分類效果,而全域性最優反而容易是過擬合的解。
對於學習率設定的解決措施:對於SGD,一開始我們可能希望學習率大一些,可以加速收斂,但是訓練後期又希望學習速率可以小一些,這樣可以比較穩定的落入一個區域性最優解。
解決方法: 不同的機器學習問題所需要的學習速率也不太好設定,需要反覆的除錯,因此就有像Adagrad、Adam、Adadelta等(這些方法在tensorflow中有對應的函式)自適應的方法可以減輕除錯引數的負擔。對於這些優化演算法我們使用它預設的引數設定就可以取得一個比較好的效果。

梯度彌散(Gradient Vanishment),梯度彌散的問題很大程度上是來源於啟用函式的“飽和”。因為在後向傳播的過程中仍然需要計算啟用函式的導數,所以一旦輸出落入函式的飽和區,它的梯度將變得非常小。 使用反向傳播演算法傳播梯度的時候,隨著傳播深度的增加,梯度的幅度會急劇減小,會導致淺層神經元的權重更新非常緩慢,不能有效學習。這樣一來,深層模型也就變成了前幾層相對固定,只能改變最後幾層的淺層模型。
sigmoid函式:在ReLU啟用函數出現之前,神經網路訓練全部使用sigmoid作為啟用函式。非線性的sigmoid函式在訊號的特徵空間對映上,對中央區的訊號增益較大,對兩側區的訊號增益小。在訓練神經網路時,可以將重要特徵置於中央區,將非重要特徵置於兩側區。關於sigmoid的詳細介紹可以參照原來的部落格
sigmoid函式的問題:但是當神經網路層數較多時,sigmoid函式在反向傳播中梯度值會逐漸減小,經過多層的傳遞之後會呈指數級急劇減小,因此梯度值在傳遞到前面幾層時就變得非常小,這時候根據訓練資料的反饋來更新引數將會變得非常緩慢,基本上起不了多大的作用。
解決方法: ReLU的出現解決了梯度彌散的問題。ReLU是一個簡單的非線性函式y=max(0,x). ReLU可以很好的傳遞梯度,經過多層的反向傳播,梯度依舊不會大幅減小,所以非常適合訓練很深的神經網路。ReLU從正面解決了梯度彌散的問題,而不需要通過無監督的逐層訓練初始化權重來繞行。

ReLU與Sigmoid函式相比,主要包括三個變化:
1)單側抑制;
2)相對寬闊的興奮邊界;
3)稀疏啟用性。
值得注意的是,對於神經網路來說,我們習慣於將隱含層的啟用函式從sigmoid函式變為ReLU來提高訓練速度以及模型的準確率。在網路的輸出層一般還是使用sigmoid函式,因為它最接近於概率輸出的分佈。

那麼多層感知機中的隱含層有什麼作用呢?
隱含層一個代表性的功能是解決XOR問題。沒有隱含層的神經網路是線性的,只能解決線性可分的問題,當引入隱含層並使用非線性啟用函式時,神經網路可以解決非線性問題。同時神經網路的隱含層越多,就可以對原有的特徵進行的越抽象,模型的擬合能力就越強。

下面,我們使用tensorflow實現了在使用一個隱含層情況下,使用神經網路對MNIST資料集實現分類。同時針對神經網路上面常見的問題,我們採用了Dropout、Adagrad以及ReLU等方法。

# -*- coding: utf-8 -*-
"""
Created on Tue Mar 20 11:20:23 2018

@author: wangyule
"""

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# 讀取資料
mnist = input_data.read_data_sets('MNIST_data/', one_hot = True)

in_units = 784  #輸入層節點數量
h1_units = 300  #隱含層節點數量

x = tf.placeholder(tf.float32, [None, in_units])    #定義輸入x的placeholder
keep_prob = tf.placeholder(tf.float32)  #定義dropout的比率

#定義權重以及偏置
W1 = tf.Variable(tf.truncated_normal([in_units, h1_units], stddev=0.1))
b1 = tf.Variable(tf.zeros([h1_units]))
W2 = tf.Variable(tf.zeros([h1_units, 10]))
b2 = tf.Variable(tf.zeros([10]))

#定義網路結構
hidden1 = tf.nn.relu(tf.add(tf.matmul(x, W1), b1))  #啟用函式使用ReLU
hidden1_drop = tf.nn.dropout(hidden1, keep_prob)    #在隱含層使用Dropout
y = tf.nn.softmax(tf.add(tf.matmul(hidden1, W2), b2))   #使用softmax分類

#定義損失函式以及確定使用損失函式最小化的優化演算法Adagrad
y_ = tf.placeholder(tf.float32, [None, 10])
cross_enerty = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), \
                              reduction_indices=[1]))
train_step = tf.train.AdagradOptimizer(0.3).minimize(cross_enerty)

#定義會話以及初始化全部變數
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

#共使用3000個batch,每個batch100個樣本進行訓練模型
for i in range(3000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    train_step.run({x: batch_xs, y_: batch_ys, keep_prob: 0.75})

#計算模型的精度   
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

#對測試集進行精度計算
print(accuracy.eval({x: mnist.test.images, y_: mnist.test.labels, \
                     keep_prob: 1.0}))

1. W1 = tf.Variable(tf.truncated_normal([in_units, h1_units], stddev=0.1))
上述權重初始化的方式為正太分佈的一種,稱為截斷的正太分佈。因為模型使用的啟用函式是ReLU,所以需要使用正太分佈給引數加一點噪聲,來打破完全對稱並且避免0梯度(也就是說一般使用ReLU啟用函式時,一般使用正太分佈的權重初始化,而對於sigmoid函式,在0附近最敏感、梯度最大,可以將引數的初始化置為0)。常見的隨機初始化的方法有:
這裡寫圖片描述

2. hidden1_drop = tf.nn.dropout(hidden1, keep_prob)
在隱含層使用Dropout,隨機將一部分節點置為0,keep_prob引數為不置為0的節點比例。在訓練時應該小於1,用以製造隨機性,防止過擬合;在測試時應該等於1,即使用全部的特徵來預測樣本的類別。

與Softmax Regression相比,加入隱含層的MLP效能提高不少。因為沒有隱含層的oftmax Regression只能直接從影象的畫素點推斷是哪個數字,沒有特徵抽象的過程。而MLP依靠隱含層,則可以組合出高階特徵,之後將這些高階特徵或者說元件在組合成數字,就實現了精準的匹配和分類。

但是使用全連線的神經網路(Fully Connected Network, FCN, MLP的另一種說法)也是有侷限性的,即使我們使用很深的網路、很多的隱含層節點、很大的迭代次數,也很難在MNIST資料集上精度達到99%以上,所以下篇部落格將學習一下卷積神經網路CNN,它可以達到99%以上的精度。