tensorflow 基礎學習四:神經網絡優化算法


指數衰減法:

公式代碼如下:

decayed_learning_rate=learning_rate*decay_rate^(global_step/decay_steps)

  變量含義:

  decayed_learning_rate:每一輪優化時使用的學習率

  learning_rate:初始學習率

  decay_rate:衰減系數

  decay_steps:衰減速度,通常表示完整的使用一遍訓練數據所需要的迭代論述(總訓練樣本數除以batch中的訓練樣本數)

tensorflow中的tf.train.exponential_decay函數實現了該功能,具體代碼示例如下:

global_step=tf.Variable(0)
#通過exponential_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)

  上面代碼指定初始學習率為0.1,每訓練100輪后學習率乘以0.96。

過擬合問題:

引入正則化(regularization)項,常用的有L1和L2正則化,具體公式如下:

L1正則化:$R(w)=\left \| w \right \|_{1}=\sum_{i}\left | w_{i} \right |$

L2正則化:$R(w)=\left \| w \right \|_{2}^{2}=\sum_{i}\left | w_{i}^{2} \right |$

  兩種正則化之間的區別,L1正則化會使參數變得更稀疏,而L2正則化不會。所謂參數變得更稀疏是指會有更多的參數變為0,這樣可以達到類似特征提取的功能。L2正則化之所以不會讓參數變稀疏,是因為,當參數變得很小時,比如為0.01時,這個參數的平方基本可以忽略不計,於是模型不會進一步將這個參數調整為0。其次,L1正則化的公式不可導,而L2可導。實踐中,可以同時使用L1和L2正則化:

$R(w)=\sum_{i}\alpha\left | w_{i} \right |+(1-\alpha)w_{i}^{2}$

 以下代碼給出了一個簡單的帶L2正則化的損失函數定義:

w=tf.Variable(tf.random_normal([2,1],stddev=1,seed=1))
y=tf.matmul(x,w)
loss=tf.reduce_mean(tf.square(y_-y))+tf.contrib.layers.l2_regularizer(lambda)(w)

類似的,tf.contrib.layers.l1_regularizer函數可以計算L1正則化項的值,代碼如下:

weights=tf.constant([[1.0,-2.0],[-3.0,4.0]])
with tf.Session() as sess:
    print(sess.run(tf.contrib.layers.l1_regularizer(.5)(weights))) # 輸出5.0 (|1.0|+|-2.0|+|-3.0|+|4.0|)*0.5=5
    print(sess.run(tf.contrib.layers.l2_regularizer(.5)(weights))) # 輸出7.5 (12+(-2)2+(-3)2+42)/2*0.5=7.5

  tensorflow會將L2的正則化損失值除以2使得求導過程更加簡潔。   

  在簡單的神經網絡中,這種方式可以很好的計算帶正則化的損失函數。但當神經網絡參數增多后,這種方式首先會導致損失函數loss的定義很長,可讀性差且容易出錯。更重要的是,當網絡結構變復雜之后定義網絡結構的部分和計算損失函數的部分可能不在同一個函數中,因此通過這種方式計算損失函數就不方便了。為了解決這個問題,可以使用tensorflow中提供的集合(collection)。以下代碼給出了通過集合計算一個5層神經網絡帶L2正則化的損失函數的計算方法。

import tensorflow as tf
# 獲取一層神經網絡邊上的權重,並將這個權重的L2正則化損失加入到名稱為'losses'的集合中
def get_weight(shape,lambda):
    # 生成一個變量
    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(lambda)(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_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'))

 

滑動平均模型

  在采用隨機梯度下降算法訓練神經網絡時,使用滑動平均模型在很多應用中都可以在一定程度提高最終模型在測試數據上的表現。

  在tensorflow中提供了tf.train.ExponentialMovingAverage函數來實現滑動平均模型。在初始化ExponentialMovingAverage時,需要提供一個衰減率(decay)。這個衰減率將用於控制模型更新的速度。ExponentialMovingAverage對每一個變量會維護一個影子變量(shadow variable),這個影子變量的初始值就是相應變量的初始值,而每次運行變量更新時,影子變量的值會更新為:

  shadow_variable=decay*shadow_variable+(1-decay)*variable

  其中shadow_variable為影子變量,variable為待更新的變量,decay為衰減率。從公式中可以看出,decay決定了模型更新的速度,decay越大模型越趨於穩定。在實際應用中,decay一般會設置成非常接近1的數(比如0.999或0.9999)。為了使得模型在訓練前期可以更新得更快,ExponentialMovingAverage還提供了num_updates參數來動態設置decay的大小。如果在ExponentialMovingAverage初始化時提供了num_updates參數,那么每次使用的衰減率將為:

$min\left \{ decay,\frac{1+num\_updates}{10+num\_updates} \right \}$

  以下代碼詳細講解了ExponentialMovingAverage的使用方法:

import tensorflow as tf

# 定義一個變量用於計算滑動平均,這個變量的初始值為0。
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)])) # 輸出[5.0, 4.5]
    
    # 更新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]

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM