1. 程式人生 > >機器學習——神經網路關於學習率的調優

機器學習——神經網路關於學習率的調優

學習率控制每次更新引數的幅度,是比較重要的模型超參,過高和過低的學習率都可能對模型結果帶來不良影響,合適的學習率可以加快模型的訓練速度。

學習率太大會導致權重更新的幅度太大,有可能會跨過損失函式的最小值,導致引數值在極優值兩邊徘徊,即在極值點兩端不斷髮散,或者劇烈震盪,隨著迭代次數增大損失沒有減小的趨勢。如果學習率設定太小,引數更新速度太慢,導致無法快速地找到好的下降的方向,隨著迭代次數增大模型損失基本不變,需要消耗更多的訓練資源來保證獲取到引數的最優值。對於學習率的設定,剛開始更新的時候,學習率儘可能的大,當引數快接近最優值的時候,學習率逐漸減小,保證引數最後能夠達到最優值。而且希望跌打的次數足夠少,這樣不僅可以加快訓練的速度,還可以減少資源的消耗。

  • 基於經驗的手動調整

通過嘗試不同的固定學習率,如0.1,0.01,0.001等,觀察迭代次數和損失的變化關係,找到損失函式下降最快對應的學習率。

  • 固定學習率

對於新手,建議將學習率設為一個固定值,可以根據訓練情況(包括損失函式值和準確率)隨時調節學習率。例如,開始時將學習率設定為一個較大的值,設在0.01~0.1,然後通過觀察損失函式和準確率的值。如果出現下降緩慢、不再下降或者出現震盪的情況,就將學習率調小,也就是逐次減小的策略。

  • 均勻分佈降低策略

這一策略與步數步長(Stepsize)相關,每當迴圈次數達到Stepsize整數倍時,其學習率為learning_rate=base_lr*gamma^(floor(iter/stepsize)),其中iter是當前迭代到第幾步,stepsize是每stepsize步時將學習率進行gamma函式調整,原始學習率為base_kr,例如原始學習率base_lr為0.01,每10000步(stepsize)時對學習率進行調整,每一次調整時學習率由0.01變成0.0058(0.01*gamma^(1)=0.01*0.57721)。這一策略的好處是隨著迭代次數的變化可以自動降低學習率,不需要人工操作。

  • 指數級衰減

指數級衰減與均勻步長原理類似,它先用指數級減少的速度快速減少學習率,使模型接近最優解,然後再逐步減少學習率,不斷迭代達到最優解,這樣使模型在訓練後期更加穩定。在Tensorflow中可以按照下面的公式調整學習率。

learning_rate*decay_rate^(global_step/decay_steps)

其中learningrate是事先設定的初始學習率,globalstep是當前迭代輪數,decaysteps是指衰減步長,即多少輪後對學習率進行調整,decayrate是指衰減係數,取值範圍為(0,1),例如設為0.8.這實質上就是一個常數的指數得到新一輪的逐漸減少的學習率。例如初始學習率為0.01,衰減係數為0.8,衰減步長為每10000步進行一次更新,則第一次更新時學習率為0.01*0.8^(10000/10000)=0.008,第二次更新時,學習率變成0.00512.

程式碼如下:

# coding:utf-8
import matplotlib.pyplot as plt
import tensorflow as tf

num_epoch = tf.Variable(0, name='global_step', trainable=False)

y = []
z = []
N = 200

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for num_epoch in range(N):
        # 階梯型衰減
        learing_rate1 = tf.train.exponential_decay(
            learning_rate=0.5, global_step=num_epoch, decay_steps=10, decay_rate=0.9, staircase=True)
        # 標準指數型衰減
        learing_rate2 = tf.train.exponential_decay(
            learning_rate=0.5, global_step=num_epoch, decay_steps=10, decay_rate=0.9, staircase=False)
        lr1 = sess.run([learing_rate1])
        lr2 = sess.run([learing_rate2])
        y.append(lr1)
        z.append(lr2)

x = range(N)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_ylim([0, 0.55])

plt.plot(x, y, 'r-', linewidth=2)
plt.plot(x, z, 'g-', linewidth=2)
plt.title('exponential_decay')
ax.set_xlabel('step')
ax.set_ylabel('learing rate')
plt.show()

視覺化效果如下所示:

  • AdaGrad動態調整

AdaGrad策略是按照引數更新頻率動態調整引數的更新步長,引數更新越低頻,更新越大;反之,引數更新越快,引數更新時變化越小,防止引數跳躍最優值。不同特徵具有不同學習率,稀疏特徵的更新速率高,而其他特徵更新速率低,提高了梯度下降的穩健性。

  • AdaDeIta自動調整 

AdaDelta是對AdaGrad策略的一種改進,優化其學習率急劇下降問題。與AdaGrad相比,AdaDelta不再累加所有的梯度平方和,而是將歷史梯度視窗控制在某一固定範圍內,基本思想是用一階的方法近似模擬二階牛頓法。

  • 動量法(Momentum)動態調整

動量法就是模擬物理上加速模型的學習過程,例如小球從高處滾下,其速度會越來越快,直到最低處。梯度下降方法中更加不穩定,每次迭代計算的梯度含有比較大的噪聲。引入動量法就是為了解決這一問題。其在更新時保留部分之前更新的方向,同時利用當前這一批資料的梯度微調最終的更新方向。這樣可以在一定程式上增加穩定性,從而學習得更快,並且還能部分擺脫區域性最優的能力,所以其優點是當前後的梯度方向一致時,能夠加速學習;當前後的梯度方向不一致時,能夠抑制震盪,更加穩定。

  • RMSProp動態調整

RMSProp與Momentum方法類似,每次迭代都按照一定比例對學習率進行衰減,這個比例稱為衰減係數。與AdaGrad演算法相比,RMSProp用移動平均代替梯度的累積,從而解決學習率過早趨向於0而結束訓練,並通過衰減係數控制歷史學習率的保留。

  • 隨機梯度下降

隨機梯度下降是對批量梯度下降的改進,與批量梯度下降法相比,SGD每次更新時只隨機取一個樣本計算梯度,所以速度較快。但是,在頻繁更新的情況下,會導致結果不穩定,波動較大,所以現在的SGD一般都指隨機小批量梯度下降(MGD),即隨機抽取一批樣本,以此更新引數。通常一小批資料含有的樣本數量為50~256。

  • Adam自動調整

Adam首先計算梯度的一階矩E(x)和二階矩E(X2)估計,並據此為不同引數設定獨立的自適應學習率。傳統的隨機梯度下降中是按照固定學習率更新所有引數權重,而在Adam策略中,它為每一個引數保留一個學習率以提升在稀疏梯度上的效能。同時,與RMSProp類似,它基於移動平均的思想為每一個引數保留近期學習率,所以Adam演算法在非穩態和線上問題上有很有優秀的效能。

下面用Tensorflow對MNIST資料集進行分類,使用不同的優化器查看準確度的變化,程式碼如下:

import tensorflow as tf
import  ssl
ssl._create_default_https_context=ssl._create_unverified_context
from tensorflow.examples.tutorials.mnist import  input_data
mnist=input_data.read_data_sets("./mnist_data",one_hot=True)
#定義學習率、迭代次數、批大小、批數量(總樣本數除以批大小)等引數,設定輸入層大小為784,即將28*28的畫素展開為一維行向量(一個輸入圖片784個值)。第一層和第二層隱層神經元數量均為256輸出層的分類類別為0~9的數字,即10個類別
learning_rate=0.005 #學習率
training_epochs=20 #迭代次數
batch_size=100  #批大小
batch_count=int(mnist.train.num_examples/batch_size) #批數量
n_hidden_1=256 #第一層和第二層隱層神經元數量均為256 #根據訓練集的特徵個數
n_hidden_2=256
n_input=784#設定輸入層的大小為784
n_classes=10 #(0-9數字)
X=tf.placeholder("float",[None,n_input]) #第二個引數為維度 None為無窮大
Y=tf.placeholder("float",[None,n_classes])
#使用tf.random_normal()生成模型權重值引數矩陣和偏置引數,並將其分別儲存於weights和biases變數中,並定義多層感知機的神經網路模型
weights={
    'weight1':tf.Variable(tf.random_normal([n_input,n_hidden_1])),
    'weight2':tf.Variable(tf.random_normal([n_hidden_1,n_hidden_2])),
    'out':tf.Variable(tf.random_normal([n_hidden_2,n_classes]))
}
biases={
    'bias1':tf.Variable(tf.random_normal([n_hidden_1])),
    'bias2':tf.Variable(tf.random_normal([n_hidden_2])),
    'out':tf.Variable(tf.random_normal([n_classes]))
}
def multilayer_perceptron_model(x):#wx+b
    layer_1=tf.add(tf.matmul(x,weights['weight1']),biases['bias1'])
    layer_2=tf.add(tf.matmul(layer_1,weights['weight2']),biases['bias2'])
    out_layer=tf.matmul(layer_2,weights['out'])+biases['out']
    return out_layer
#使用輸入變數X初始化模型,定義損失函式為交叉熵,採用梯度下降法作為優化器(除此之外  還可選MomentumOptimizer、AdagradOptimizer、AdamOptimizer等,見註釋部分
#並對模型中tf.placeholder定義的各引數初始化
logits=multilayer_perceptron_model(X) #多層感知機
#
loss_op=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=Y)) #定義損失函式為交叉熵
#用來更新和計算影響模型訓練和模型輸出的網路引數,使其逼近或達到最優值,從而最小化(或最大化)損失函式E(x)

# 這種演算法使用各引數的梯度值來最小化或最大化損失函式E(x)。最常用的一階優化演算法是梯度下降。
optimizer=tf.train.GradientDescentOptimizer(learning_rate)
# optimizer=tf.train.MomentumOptimizer(learning_rate,0.2)
# optimizer=tf.train.AdamOptimizer(learning_rate)
# optimizer=tf.train.AdagradOptimizer(learning_rate)
# optimizer=tf.train.AdadeltaOptimizer(learning_rate)
# optimizer=tf.train.RMSPropOptimizer(learning_rate,0.2)
train_op=optimizer.minimize(loss_op)
init=tf.global_variables_initializer()#引數初始化
#將訓練集樣本輸入模型進行訓練,並計算每個批次的平均損失,在每次迭代時輸出模型的平均損失
with tf.Session() as sess:
    sess.run(init)
    for epochs in range(training_epochs):
        avg_cost=0
        for i in range(batch_count):
          train_x,train_y=mnist.train.next_batch(batch_size)
          _,c=sess.run([train_op,loss_op],feed_dict={X:train_x,Y:train_y})
          avg_cost+=c/batch_count #計算每個批次的平均損失
        print("Epoch:",'%02d' % (epochs+1),"avg cost={:.6f}".format(avg_cost))
#模型訓練完成,使用測試集樣本對其評估,並計算其正確率
    pred=tf.nn.softmax(logits)  #Apply softmax to logits
    correct_pediction=tf.equal(tf.argmax(pred,1),tf.argmax(Y,1))
    accuracy=tf.reduce_mean(tf.cast(correct_pediction,"float"))
    print("Accuracy:",accuracy.eval({X:mnist.test.images,Y:mnist.test.labels}))

使用隨機梯度下降優化器的準確率為:

使用動量法(Momentum)動態調整優化器的準確率為:

使用Adam自動調整優化器的準確率為:

 使用AdaGrad動態調整的優化器準確率為:

使用AdaDeIta自動調整的優化器的準確率為: 

使用RMSProp動態調整優化器的準確率為: