一、深度學習與深層神經網絡
深層神經網絡是實現“多層非線性變換”的一種方法。
深層神經網絡有兩個非常重要的特性:深層和非線性。
1.1線性模型的局限性
線性模型:y =wx+b
線性模型的最大特點就是任意線性模型的組合仍然還是線性模型。
如果只通過線性變換,任意層的全連接神經網絡和單層神經網絡模型的表達能力沒有任何的區別,它們都是線性模型。然而線性模型能夠解決的問題是有限的。
如果一個問題是線性不可分的,通過線性模型就無法很好的去分類這些問題。
1.2激活函數實現去線性化
神經元的輸出為所有輸入的加權和,所以整個神經網絡就是一個線性模型。如果將每個神經元的輸出通過一個激活函數,那么整個神經網絡就變成了一個非線性的模型。
幾種常用的非線性激活函數及其圖像。
TF提供了7中不同的非線性激活函數,relu,sigmod和tanh是其中最常用的。TF還支持使用自己定義的激活函數。
這是一個加入了偏置項和激活函數的網絡結構。
那么,怎么用TF實現這個網絡結構的前向傳播算法呢。
import tensorflow as tf #聲明w1,w2 兩個變量,通過seed設定隨機種子 w1 = tf.Variable(tf.random_normal([2,3],stddev=1.0 ,seed =1 )) w2 = tf.Variable(tf.random_normal([3,1],stddev=1.0 ,seed =1 )) bias1 = tf. Variable(tf.random_normal([3,1],stddev=1.0 ,seed =1 ))
bias2 = tf. Variable(tf.random_normal([1,1],stddev=1.0 ,seed =1 ))
#暫時將輸入的謄正向量定義為一個常量,這里的X是一個【1,2】矩陣 x = tf.constant([[0.7,0.9]]) #通過前向傳播算法得到輸出y h = tf.nn.relu( tf.matmul(x,w1)+bias1) y =tf.nn.relu( tf.matmul(h,w2)+bias2)
二 損失函數的定義
神經網絡的模型的效果及優化的目標是通過損失函數來定義的。
2.1經典的損失函數
通過神經網絡解決多分類問題最常用的方法就是設置n個輸出節點,n為類別的個數。神經網絡可以得到一個n維數組作為結果輸出。數組中的每一個維度對應一個類別。理想情況,如果一個樣本屬於類別k,那么這個節點對應的維度為1,其他維度為0.如何判斷一個輸出向量和期望向量有多接近?可以用交叉熵來判斷。
交叉熵的公式:
交叉熵刻畫的是兩個概率分布之間的距離。將前向計算得到的output經過Softmax優化成一個0~1之間的概率分布。
這樣p代表標簽值,q代表輸出的預測值。
根據公式用TF定義交叉熵的計算圖:
cross_entropy = - tf.reduce_mean(y_* tf.log(tf.clip_by_value(y,1e-10,1)))
其中y_表示真實值,y表示模型的預測值。tf.clip_by_value將y的值限制在e^-10 到 1 之間
因為交叉熵損失一般與softmax回歸一起使用,所以TF對這兩個功能進行了統一的封裝,提供了softmax_cross_entropy_with_logits函數
tf.nn.softmax_cross_entropy_with_logits(y,y_)#y代表原始神經網絡的預測輸出,y_代表標准答案
那么,對於回歸問題一般采用什么樣的損失函數?
回歸問題一般只有一個輸出節點,這個節點就是預測值。對於回歸問題,最常用的損失函數就是均方誤差(MSE),公式如下:
假設一個batch包含n個樣本,其中yi是batch中第i個樣本的正確答案,yi ‘ 是神經網絡輸出的預測值。
TF實現MSE:
mse= tf.reduce_mean(tf.square(y_ - y))
均方誤差也是分類中一種常用的損失函數。
2.2自定義損失函數
例如我們定義的損失函數為
用TF實現
loss = tf.reduce_mean(tf.select(tf.greater(v1,v2),(v1-v2)*a,(v1-v2)*b))
tf.greater 比較兩個張量中的每一個元素大小,返回比較的結果為元素為True或False的張量。
tf.select 有三個參數,第一個參數為選擇條件根據。如果第一個參數為true,就選擇第二個參數作為返回值,如果為false就選擇第三個參數作為返回值。
三、神經網絡優化算法
梯度下降算法用於優化參數,反向傳播算法是訓練神經網絡的核心算法。
梯度下降算法存在兩個問題:第一,有可能得到的是局部最優解;第二,每一次迭代的時候,需要計算所有訓練數據的loss,計算時間太長了。
所有一般采用mini_batch SGD。
用TensorFlow實現神經網絡的優化。
一個偽代碼
import tensorflow as tf batch_size = 128 STEPS = 20000 #每次讀取一小部分數據作為當前訓練數據來進行BP x = tf.placeholder(tf.float32,shape=(batch_size,2),name="x_input") y_ = tf.placeholder(tf.float32,shape=(batch_size,1),name="y_input") #定義神經網絡結構和優化算法 loss = tf.nn.sigmoid_cross_entropy_with_logits() train_step = tf.train.AdamOptimizer(0.01).minimize(loss) #訓練神經網絡 with tf.Session() as ss: #參數初始化 #... #迭代的更新參數 for i in range(STEPS): #准備batch_size 個數據 current_x,current_y = (None,None) ss.run(train_step,feed_dict={x:current_x,y_:current_y})
四、進一步優化神經網絡
有了神經網絡的基本算法,在訓練的過程中還會遇到一些問題。例如學習率怎么設置,怎么解決過擬合等
下面針對幾個問題給出解決辦法。
4.1學習率的設置
學習率決定了參數每次更新的幅度。如果學習率過大,那么可能導致參數在極優解的周圍震盪,學習不到極優解。如果學習率過小,會導致收斂速度太慢。
為了解決學習率的問題,TensorFlow提供了提供了一種更加靈活的學習率設置方法——指數衰減法。tf.train_exponential_decay函數實現了指數衰減學習率。
global_step = tf.Variable(0) #通過xponential_decay函數生成學習率 learning_rate = tf.train.exponential_decay(0.1,global_step,100,0.96,staircase=True) #使用指數衰減的學習率,在minimize函數中傳入global_step 將自動更新global_step參數,從而使得學習率也得到相應的更新 learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_step)
staircase=True時,每訓練100輪,學習率乘以0.96
4.2過擬合問題
為了避免過擬合問題,一個非常常用的方法就是正則化。正則化的事項就是在損失函數中加入刻畫模型復雜度的指標。常用L1正則化和L2正則化。
w = tf.Variable(tf.random_normal([2,1],stddev=1.0,seed=1.0)) lamda = 0.3 y = tf.matmul(x,w) #lamda是正則化的懲罰系數 loss = tf.reduce_mean(tf.square(y_-y)+tf.contrib.layers.l2_regularizer(lamda)(w))
通過變量來計算損失函數部署很方便,可以通過TF的集合來保存一組實體。例子如下:
import tensorflow as tf # 獲取一層神經網絡的權重,並將其L2正則化損失加入名稱為“losses”的集合中 def get_weights(shape,lamda): #生成變量 var = tf.Variable(tf.random_normal(shape),dtype=tf.float32) tf.add_to_collection("losses",tf.contrib.layers.regularizar(lamda)(var)) #返回生成的變量 return var x = tf.placeholder(tf.float32,shape=[None,2]) y_ = tf.placeholder(tf.float32,shape=[None,1]) batch_size = 8 #定義每一次中節點的個數 layer_dimension = [2,10,10,10,1] #神經網絡的層數 n_layer = len(layer_dimension) #變量維護前向傳播是最深層的節點,最開始是輸入層 cur_layer = x #當前層的節點個數 in_dimension = layer_dimension[0] #生成5層全連接網絡的結構 for i in range(1,n_layer): out_diminsion = layer_dimension[i] weight = get_weights([in_dimension,out_diminsion],0.001) bias = tf.Variable(tf.constant(0.1,shape=[out_diminsion])) #使用Relu激活函數 cur_layer = tf.nn.relu(tf.matmul(cur_layer,weight)+bias) #進入下一層之間將下一層的節點個數更新為當前層的節點個數 in_dimension = layer_dimension[i] mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer)) #將MSE加入損失集合 tf.add_to_collection("losses",mse_loss) #get_collection 返回一個列表 loss = tf.add(tf.get_collection("losses"))
4.3滑動平均模型
用SGD訓練網絡時,使用滑動平均模型在很多應用中可以在一定程度提高最終模型在測試數據上的表現。
TF提供了tf.train.ExponentialMovingAverage來實現滑動平均模型。