三、深度學習與深層神經網絡
1、介紹
-
深度學習特性:
多層和非線性
-
線性模型侷限性:
只通過線性變換,任意層的全連接神經網絡和單層神經網絡模型的表達能力沒有任何區別。
線性模型能夠解決的問題是有限的——這是它的最大侷限性
2、激活函數實現去線性化
-
如果將每一個神經元(也就是神經網絡中的節點)的輸出通過一個非線性函數,那麼整個神經網絡的模型也就不再是線性的了。這個非線性函數就是激活函數
-
偏置項可以被表達爲一個輸出永遠爲1的節點
-
tf.nn.relu、tf.sigmoid和tf.tanh是TensorFlow中比較常見的幾個非線性激活函數
3、經典損失函數交叉熵
https://blog.csdn.net/weixin_37567451/article/details/80895309
import tensorflow as tf
from numpy.random import RandomState
# 定義訓練數據batch的大小
batch_size = 8
# 兩個輸入節點
x = tf.placeholder(tf.float32,shape=(None,2),name= 'x_input')
# 迴歸問題一般只有一個輸出節點
y_ = tf.placeholder(tf.float32,shape=(None,1),name= 'y_input')
# 定義了一個單層的神經網絡前向傳播的過程,這裏是簡單加權和
w1 = tf.Variable(tf.random_normal([2,1],stddev=1,seed=1))
y = tf.matmul(x,w1)
# 定義預測多了和預測少了的成本
loss_less = 10
loss_more = 1
"""
w1=[[ 1.01934695]
[ 1.04280889]]
"""
# loss_less = 1
# loss_more = 10
"""
w1=[[ 0.95525807]
[ 0.9813394 ]]
"""
loss = tf.reduce_sum(tf.where(tf.greater(y,y_),
(y-y_)*loss_more,
(y_-y)*loss_less))
# 定義學習率
learning_rate = 0.001
# 定義反向傳播算法來優化神經網絡中的參數
train_step = \
tf.train.AdamOptimizer(learning_rate).minimize(loss)
# 通過隨機數生成一個模擬數據集
""""
1爲隨機種子,只要隨機種子seed相同,產生的隨機數序列就相同
設置迴歸的正確值爲兩個輸入的和加上一個隨機量。
之所以要加入一個隨機量是爲了加入不可預測的噪音,否則不同損失函數的意義就不大了,
因爲不同損失函數都會在能完全預測正確的時候最低。
一般來說噪音爲一個均值爲0的小量,所以這裏的噪音設置爲-0.05 ~ 0.05的隨機數
"""
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size,2)
Y = [[x1+x2+rdm.rand()/10.0-0.05]for (x1,x2)in X]
# 訓練神經網絡
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
STEPS = 5000
for i in range(STEPS):
start = (i*batch_size) % dataset_size
end = min(start+batch_size, dataset_size)
sess.run(train_step,
feed_dict={x:X[start:end],y_:Y[start:end]})
print("w1=")
print(sess.run(w1))
4、神經網絡優化算法
1、方法與問題
問題1:梯度下降算法可能得到的數據是局部最優而不是全局最優
方法1:
爲了加速訓練過程,可以使用隨機梯度下降的算法。這個算法優化的不是在全部訓練數據上的損失函數,而是在每一輪迭代中,隨機優化某一條訓練數據上的損失函數。這樣每一輪參數更新的速度就大大加快了。
問題2:
在某一條數據上損失函數更小並不代表在全部數據上損失函數更小。使用隨機梯度下降優化得到的神經網絡甚至無法達到局部最優
方法2:
每次計算一小部分訓練數據的損失函數。這一小部分數據被稱之爲一個batch。每次使用一個batch可以大大減小收斂所需要的迭代次數。
2、學習率的設置——指數衰減法
tf.train.exponential_decay函數實現了指數衰減學習率。通過這個函數,可以先使用較大的學習率來快速得到一個比較優的解,然後隨着迭代的繼續逐步減小學習率,使得模型在訓練後期更加穩定。
"""
decayed_learning_rate:每一輪優化時使用的學習率
learning_rate:事先設定的初始學習率
decay_rate:衰減係數
decay_steps:衰減速度
"""
decayed_learning_rate = \
learning_rate * decay_rate ^ (global_step / decay_steps)
3、過擬合問題
爲了避免過擬合問題,一個非常常用的方法是正則化(regularization)。
正則化的思想就是在損失函數中加入刻畫模型複雜度的指標。假設用於刻畫模型在訓練數據上表現的損失函數爲J(θ),那麼在優化時不是直接優化J(θ),而是優化J(θ)+λR(w)。其中R(w)刻畫的是模型的複雜程度,而λ表示模型複雜損失在總損失中的比例。
注意這裏的θ表示的是一個神經網絡中的所有參數,它包括邊上的權重w和偏置b。一般來說模型複雜度只由權重w決定
w越小,對應的θ值就越小,當θ→0時可視爲對應參數不存在,所以模型複雜度只由權重w決定
常用的刻畫模型複雜度的函數R(w)有兩種,一種是L1正則化,計算公式是:
另一種是L2正則化,計算公式是:
無論哪一種正則化方式,基本的思想都是希望通過限制權重的大小,使得模型不能任意擬合訓練數據中的隨機噪音
import tensorflow as tf
# 獲取一層神經網絡邊上的權重,並將這個權重的L2正則化損失加入名稱爲‘losses’的集合中
def get_weight(shape,langda):
# 生成一個變量
var = tf.Variable(tf.random_normal(shape),dtype=tf.float32)
# add_to_collection 函數將這個新生成的變量的L2正則化損失項加入集合
# 這個函數的第一個參數‘losses’是集合的名字,第二個參數是要加入這個集合的內容
tf.add_to_collection(
'losses',tf.contrib.layers.l2_regularizer(langda)(var)
)
return var
x = tf.placeholder(tf.float32,shape=(None,2))
y_ = tf.placeholder(tf.float32,shape=(None,1))
batch_size = 2
# 定義了每一層網絡中節點的個數
layer_dimension = [2,10,10,10,1]
# 神經網絡的層數
n_layers = len(layer_dimension)
# 這個變量維護前向傳播時最深層的節點,開始的時候就是輸入層
cur_layer = x
# 當前節點的個數
in_dimension = layer_dimension[0]
# 通過一個循環來生成5層全連接的神經網絡結構
for i in range(1,n_layers):
# layer_dimension[i]爲下一層的節點個數
out_dimension = layer_dimension[i]
# 生成當前層中權重的變量,並將這個變量的L2正則化損失加入計算圖上的集合
weight = get_weight([in_dimension,out_dimension],0.001)
bias =tf.Variable(tf.constant(0.1,shape=[out_dimension]))
# 使用ReLU激活函數
cur_layer = tf.nn.relu(tf.matmul(cur_layer,weight)+bias)
# 進入下一層之前將下一層的節點個數更新爲當前層節點個數
in_dimension = layer_dimension[i]
# 在定義神經網絡前向傳播的同時已經將所有的L2正則化損失加上了圖上的集合
# 這裏只需要計算刻畫模型在訓練數據上表現的損失函數
mse_loss = tf.reduce_mean(tf.square(y_-cur_layer))
# 將均方誤差損失函數加入損失集合
tf.add_to_collection('losses',mse_loss)
"""
get_collection返回一個列表,這個列表是所有這個集合中的元素。
在這個樣例中,這些元素就是損失函數的不同部分,將它們加起來就可以得到最終的損失函數
"""
loss = tf.add_n(tf.get_collection('losses'))
4、滑動平均模型
tf.train.ExponentialMovingAverage是用來讓模型不被更新的太快的一種方法,在實際應用中會有兩個變量:模型變量variable和影子變量shadow variable,
,在上面這個公式中可以看到,decay使得shadow variable具有一定的緩衝能力,不會變化太快,爲了讓前期訓練加快,可以計入num_update參數,使得
滑動平均模型概述:
主要是控制衰減率來控制參數更新前後之間的差距,從而達到減緩參數的變化值(比如:參數更新前是5,更新後是4,通過滑動平均模型之後,參數的值會在4到5之間)
ExponentialMovingAverage函數:
tensorflow中提供了tf.train.ExponentialMovingAverage來實現滑動平均模型:
tf.train.ExponentialMovingAverage(decay,num_updates=None,zero_debias=False,name="ExponentialMovingAverage")
在初始化ExponentialMovingAverage時,需要提供一個衰減率(decay)用於控制模型更新的速度.
ExponentialMovingAverage對每一個變量會維護一個影子變量,(shadow_variable)
這個影子變量的初始值就是相應變量的初始值,每次運行變量更新時,影子變量的值會更新爲:
這個算法的原理:平滑、濾波,即使數據平滑變化,通過調整參數來調整變化的穩定性
shadow_variable爲影子變量,variable爲更新的變量,decay爲衰減率,decay決定了模型更新的速度,decay越大模型越趨於穩定.在實際應用中,decay一般會設置爲非常接近1 的數.爲了使得模型在訓練前期可以更新地更快.
ExponentialMovingAverage提供了num_updates參數來動態設置decay的大小.如果在ExponentialMovingAverage初始化時提供了num_updates參數,那麼每次使用的衰減概率爲
由此得:(應該求導的,求導證明是單增的)
num_updates的值越大,整個值就越大,那麼decay在原decay與該值中就更傾向於選擇原decay.
反之亦反.
返回值:ExponentialMovingAverage對象,通過對象調用apply方法可以通過滑動平均模型來更新參數。
import tensorflow as tf
# 定義一個變量用於計算滑動平均,這個變量的初始值爲0.
# 類型爲tf.float32,因爲所有需要計算滑動平均的變量必須是實數型
v1 = tf.Variable(0, dtype=tf.float32)
# 這裏step變量模擬神經網絡中迭代的輪數,可以用於動態控制衰減率
step = tf.Variable(0, trainable=False)
# 定義一個滑動平均的類(class)。初始化時給定了衰減率(0.99)和控制衰減率的變量step
ema = tf.train.ExponentialMovingAverage(0.99, step)
# 定義一個更新變量滑動平均的操作。這裏需要給定一個列表,每次執行這個操作時
# 這個列表中的變量都會被更新
maintain_averages_op = ema.apply([v1])
with tf.Session() as sess:
# 初始化所有變量
init_op = tf.global_variables_initializer()
sess.run(init_op)
# 通過ema.average(v1)獲取滑動平均之後變量的取值。在初始化之後變量v1的值和v1的滑動平均都爲0
print(sess.run([v1, ema.average(v1)])) # 輸出[0.0,0.0]
# 更新變量v1的值到5
sess.run(tf.assign(v1, 5))
# 更新v1的滑動平均值。衰減率爲min{0.99,(1+step)/(10+step)=0.1}=0.1
# 所以v1的滑動平均會被更新爲0.1*0+0.9*5=4.5
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
# 更新step的值爲10000
sess.run(tf.assign(step, 10000))
# 更新v1的值爲10
sess.run(tf.assign(v1, 10))
# 更新v1的滑動平均值。衰減率爲min{0.99,(1+step)/(10+step)=0.999}}=0.99
# 所以v1的滑動平均會被更新爲0.99*4.5+0.01*10=4.555
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
# 輸出[10.0,4.5549998]
# 再次更新滑動平均值,得到的新滑動平均值爲0.99*4.555+0.01*10=4.60945
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
# 輸出[10.0,4.6094499]