1. 程式人生 > >深度學習——神經網路

深度學習——神經網路

一:神經網路介紹

 傳統神經網路結構比較簡單,訓練時隨機初始化輸入引數,並開啟迴圈計算輸出結果,與實際結果進行比較從而得到損失函式,並更新變數使損失函式結果值極小,當達到誤差閾值時即可停止迴圈。

神經網路的訓練目的是希望能夠學習到一個模型,實現輸出一個期望的目標值。學習的方式是在外界輸入樣本的刺激下不斷改變網路的連線權值。傳統神經網路主要分為以下幾類:前饋神經網路反饋型神經網路和自組織神經網路。這幾類網路具有不同的學習訓練演算法,可以歸結為監督學習演算法和非監督學習演算法。

  • 前饋神經網路

前饋神經網路是一種單向多層的網路結構,即資訊是從輸入層開始,逐層向一個方向傳遞,一直到輸出層結束。所謂的”前饋“是指輸入訊號的傳播方向為前向,在此過程中並不調整各層的權值引數,而反傳播時是將誤差逐層向後傳遞,從而實現使用權值引數對特徵的記憶,即通過反向傳播(BP)演算法

來計算各層網路中神經元之間邊的權重。BP演算法具有非線性對映能力,理論上可逼近任意連續函式,從而實現對模型的學習

  1. 感知器

感知器是一種結構最簡單的前饋神經網路,也稱為感知機,它主要用於求解分類問題。感知器是單層感知器的簡稱,除此之外還有多層感知器,即由多層神經元構成前饋神經網路。如下圖所示:

                                                                                    單層感知器結構

一個感知器可以接收n個輸入x=(x1,x2,...,xn),對應幾個權值w=(w1,w2,...,wn),此外還要一個偏置項閾值,就是圖中的b,神經元將所有輸入引數對應權值進行加權求和,得到的結果經過啟用函式變換後輸出,計算公式如下:

                                                                  y=f(x*w+b)

感知器的啟用函式有很多種,比如Sigmoid函式、雙曲正切函式(tanh)、ReLU函式等,主要作用是非線性對映,從而對現實環境中非線性資料建模。除此之外還有線性函式、斜坡函式、階躍函式等,在啟用函式選擇時,一般選擇光滑、連續和可導的函式,將結果轉換成固定的輸出範圍,例如[0,1],這樣,在分類時通過判斷y值來判定樣本類別。

神經元的作用可以理解為對輸入空間進行直線劃分,單層感知器無法解決最簡單的非線性可分問題——異或問題,如下圖,感知器可以順利求解與(AND)和或(OR)問題,但是對應異或(XOR)問題,單層感知器無法通過一條線進行分割。

2.BP神經網路

BP神經網路也稱為前饋神經網路,只是它的引數權重值是由反向傳播學習演算法進行調整的。BP神經網路模型拓撲結構包括輸入層(input)、隱藏層(hide layer)和輸出層(output layer),如下圖所示,利用啟用函式實現從輸入到輸出的任意非線性對映,從而模擬更曾神經元之間的互動。啟用函式需滿足處處可導的條件。例如,Sigmoid函式連續可微,求導合適,單調遞減,輸出值是0~1的連續量,這些特點使其適合作為神經網路的啟用函式。

BP神經網路訓練過程的基本步驟可以歸納如下:

  • 初始化網路權值和神經元的閾值,一般通過隨機的方式進行初始化。
  • 前向傳播:計算隱層神經元和輸出層神經元的輸出。
  • 反向傳播:根據目標函式公式修正權值wij. 

BP神經網路的核心思想是由後層誤差推導前層誤差,用輸出層的誤差來估計隱層的誤差,再用這個誤差估計輸入層的誤差,如此一層一層地反傳,最終獲得各層的誤差估計,從而得到引數的權重值。由於權值引數的運算量過大,一般採用梯度下降法來實現,所謂梯度下降就是讓引數向著梯度的反方向前進一段距離,不斷重複,直到梯度接近零時停止。此時,所有的引數恰好達到使成本函式取得最低值的狀態,為了避免區域性最優,可以採用隨機化梯度下降。 

  • 神經網路的相關概念

學習神經網路相關概念有助於理解深度學習中網路設計原理,可在模型訓練過程中收放自如的調整引數,並且這些神經網路相關概念是深度學習的基礎,隨著深度學習的不斷演化,深入理解這些常識性理論有助於快速理解層出不窮的深度學習網路模型。

  1. 啟用函式

使用非線性啟用函式的原因是線性模型的表達能力不夠,通過對輸出結果應用啟用函式而引入非線性變換。如果不用啟用函式,每一層輸出都是上層輸入的線性函式,那麼每一次的輸出都是輸入的線性組合,與只有一個隱藏層效果是一樣的。引入非線性函式作為啟用函式之後,深層神經網路才有意義,可以逼近任意函式。啟用函式經常使用Sigmoid函式,其輸出有界。此外,常見的啟用函式還有ReLU函式。啟用函式通常有以下性質。

  • 非線性

神經網路中神經元完成線性變換後,疊加一個非線性啟用函式,轉換輸出為非線性函式結果,神經網路就有可能學習導平滑的曲線來分割平面,而不是通過複雜的線性組合逼近平滑曲線來實現。當啟用函式是非線性的時候,一個兩層的神經網路理論上可以逼近所有的函式。

  • 可微性

當基於梯度的優化方法時,啟用函式需要滿足可微性,因為在反向傳播更新梯度時,需要計算損失函式對權重的偏導數。傳統的啟用函式sigmoid滿足處處可微的特性,而ReLU函式僅在有限個點處不可微。對於隨機梯度下降演算法,幾乎不可能收斂到梯度接近零的位置,所以有限的不可微點對於優化結果影響不大。

1.Sigmoid函式

sigmoid函式的優點在於輸出範圍有限,資料在傳遞的過程中不容易發散,並且其輸出範圍為(0,1),可以在輸出層表示概率值,如下圖所示。Sigmoid函式的導數是非0的,很容易計算。

Sigmoid函式的主要缺點是梯度下降非常明顯,且兩頭過於平坦,容易出現梯度消失的情況,輸出的值域不對稱。

2.ReLU函式

ReLU函式是目前神經網路中常用的啟用函式,這是因為ReLU函式的線性特點使其收斂速度比Sigmoid要快,而且沒有梯度飽和的情況出現。計算更加高效,相比於Sigmoid函式,只需一個閾值就可以得到啟用值,不需要對輸入歸一化來防止達到飽和,如下圖所示

Sigmoid是飽和非線性函式,結果達到一定範圍後不再變換,而ReLU是非飽和非線性函式,輸出值範圍是無限的,梯度下降速度更快,訓練的時間更短。

Sigmoid函式能對細微的特徵進行區分,所以適用於特徵差別較小的情形,對於這種函式,為了避免啟用函式進入飽和區,使隱層的輸出結果趨同,要對輸入值歸一化處理。

ReLU函式的缺點是所有負值均被截斷為結果0,從而導致特徵丟失。ReLU要求學習率不能太高,如果學習率很大,會使網路中很多神經元都失效,即很多神經元的啟用函式結果值為0.所有一般設定一個較小的學習率,如0.005.

2.損失函式

損失函式評價模型對樣本擬合度,預測結果與實際值越接近,說明模型的擬合能力越強,對應損失函式的結果就越小;反之,損失函式的結果越大。損失函式比較大時,對應的梯度下降比較快。為了計算方便,可以採用歐式距離作損失度量標準,通過最小化實際值與估計值之間的均方誤差作為損失函式,即最小平方誤差準則

min C [Y,G(X)]=||G(X)-Y||^{2}=\sum [G(Xi)-y^{i}]^{2}

其中G(X)是模型根據輸入矩陣X輸出一個預測向量,預測值G(X)和真值Y的歐式距離越大,損失就越大,反之就越小。

  • Softmax

使用Softmax函式的好處是可以使分類問題的預測結果更加明顯,不同類別之間的差距更大。在實際應用中,特別是在Tensorflow中推薦採用交叉熵與Softmax結合作為損失函式,可以避免數值不穩定的情況。

  • 交叉熵

目標為二分類問題,分類誤差越小,則損失越小,對正負分類計算各自的損失。但是會產生梯度爆炸問題,例如,如果實際結果yn=1,但是預測結果yn接近於0,則logyn便趨向於負無窮,最終的損失趨向於無窮大,產生梯度爆炸問題。

下面是用Tensorflow實現簡單的神經網路程式碼:

import numpy as np
import  tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets("MNIST_data",one_hot=True)
n_hidden_1=256  #第一層神經元
n_hidden_2=128  #第二層神經元
n_input=784     #輸入畫素點個數
n_classes=10 #最終得出分類的類別 10分類
#引數初始化
x=tf.placeholder("float",[None,n_input])
y=tf.placeholder("float",[None,n_classes])

stddev=0.1
weights={#初始化w1 w2 out 使用高斯初始化
    'w1':tf.Variable(tf.random_normal([n_input,n_hidden_1],stddev=stddev)),#w1=784*256
    'w2':tf.Variable(tf.random_normal([n_hidden_1,n_hidden_2],stddev=stddev)),#w2=256*128
    'out':tf.Variable(tf.random_normal([n_hidden_2,n_classes],stddev=stddev)) #out=128*10
}
biases={
    'b1':tf.Variable(tf.random_normal([n_hidden_1])),
    'b2':tf.Variable(tf.random_normal([n_hidden_2])),
    'out':tf.Variable(tf.random_normal([n_classes]))
}
print("NETWORK READY")
#前向傳播
def multilayer_perceptron(_X,_weights,_biases): # _X表示data weights表示所有的w biases表示所有的b
    layer_1=tf.nn.sigmoid(tf.add(tf.matmul(_X,_weights['w1']),_biases['b1']))#計算layer1這層的輸出值,每層後面都要加sigmoid啟用函式
    layer_2=tf.nn.sigmoid(tf.add(tf.matmul(layer_1,_weights['w2']),_biases['b2'])) #計算layer2這層的輸出值
    return  (tf.matmul(layer_2,_weights['out'])+_biases['out'])#輸出層不需要sigmoid函式啟用
#prediction
pred=multilayer_perceptron(x,weights,biases)
#定義損失函式
cost=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred,labels=y))#交叉熵函式第一個引數是網路的預測值,第二個引數是實際的label值
optm=tf.train.GradientDescentOptimizer(learning_rate=0.001).minimize(cost)#運用梯度下降的優化器求解cost
corr=tf.equal(tf.argmax(pred,1),tf.argmax(y,1)) #模型的準確率
accr=tf.reduce_mean(tf.cast(corr,"float")) #轉化成float型別計算精度
init=tf.global_variables_initializer()#全域性變數初始化
print("FUNCTIONS READY")
training_epochs=20
batch_size=100
display_step=4

sess=tf.Session()#定義計算圖的區域
sess.run(init)
for epoch in range(training_epochs):
    avg_cost=0
    total_batch=int(mnist.train.num_examples/batch_size)
    for i in range(total_batch):
        batch_xs,batch_ys=mnist.train.next_batch(batch_size)
        feeds={x:batch_xs,y:batch_ys}
        sess.run(optm,feed_dict=feeds)
        avg_cost+=sess.run(cost,feed_dict=feeds)
    avg_cost=avg_cost/total_batch
    if(epoch+1) % display_step ==0:
        print("Epoch:%03d/%03d costL%.9f" % (epoch,training_epochs,avg_cost))
        feeds={x:batch_xs,y:batch_ys}
        train_acc=sess.run(accr,feed_dict=feeds)
        print("TRAIN ACCURACY: %.3f" %(train_acc))
        feeds={x:mnist.test.images,y:mnist.test.labels}
        test_accc=sess.run(accr,feed_dict=feeds)
        print("TEST ACCURACY:%.3f" %(test_accc))
print("OPTIMIZATTION FINISHED")