1. 程式人生 > 實用技巧 >【學習筆記】Pytorch深度學習—學習率調整策略

【學習筆記】Pytorch深度學習—學習率調整策略

前面的課程學習了優化器的概念,優化器中有很多超引數如學習率lr,momentum動量、weight_decay係數,這些超引數中最重要的就是學習率。學習率可以直接控制模型引數更新的步伐,此外,在整個模型訓練過程中學習率也不是一成不變的,而是可以調整變化的。本節內容就可以分為以下3方面展開,分別是:(1)為什麼要調整學習率?(2)Pytorch的六種學習率調整策略;(3)學習率調整總結。

為什麼要調整學習率?

①僅考慮學習率的梯度下降

\[w_{i+1}=w_i-lr*g(w_i) \]

②加入momentum係數後隨機梯度下降更新公式:

\[v_{i}=m*v_{i-1}+*g(w_i) \]

\[w_{i+1}=w_i+lr*v_{i} \]

從梯度下降法公式可知,引數更新這一項中會有學習率乘以梯度或更新量(\(lr*g(w_i)\) \(or\) \(lr*v_{i}\)),學習率lr直接控制了模型引數更新的步伐。通常,在模型訓練過程中,學習率給的比較大,這樣可以讓模型引數在初期更新比較快;到了後期,學習率有所下降,讓引數更新的步伐減慢。可是,為什麼會在模型訓練時對學習率採用 “前期大、後期小” 的賦值特點呢?


圖1 學習率調整必要性的說明例項

由圖1可知,一開始球距離洞口很遠,將球打進洞口這一過程形如模型訓練中讓Loss逐漸下降至0這一過程。
(1)球距離洞口很遠,利用較大的力度去打球,讓球快速的飛躍到洞口附近,這就是一開始採用大學習率訓練模型的原因;
(2)球來到洞口附近,調整力度,輕輕擊打球,讓球可控、緩慢的朝洞口接近。這就是學習率到了後期,讓引數更新的步伐小一點,使得Loss逐漸下降。
這就學習率前期大、後期小的1個形象比喻。下面以函式的優化過程理解學習率“前期大、後期小”的原因。


圖2 函式說明例項

Pytorch的六種學習率調整策略

Pytorch提供的六種學習率調整策略都是繼承於LRScheduler基類,因此首先學習LRScheduler基本屬性和基本方法。
class_LRScheduler

class_LRScheduler(object)
主要屬性:
optimizer:關聯的優化器
last_epoch:記錄epoch數
base_lrs:記錄初始學習率

主要屬性

(1)optimizer:學習率所關聯的優化器。已知優化器如torch.optim.SGD才是存放學習率lr的,LRScheduler會去修改優化器中的學習率。所以LRScheduler必須要關聯1個優化器才能改動優化器裡面的學習率。

optim.SGD(params,lr=<object object>,
momentum=0,dampening=0,
weight_decay=0,nesterov=False)

(2)last_epoch:記錄epoch數。整個學習率調整是以epoch為週期,不要以iteration。
(3)base_lrs:記錄初始學習率。

LRScheduler的_init_函式

class_LRScheduler(object)
   def_init_(self,optimizer.last_epoch=-1):

只接受2個引數,分別是optimizer和last_epoch=-1。

主要方法
step()——更新下一個epoch的學習率 一定要放在epoch中而不是iteration。

get_lr()——虛擬函式,計算下一個epoch的學習率

Pytorch提供的六種學習率調整策略

1、StepLR
功能:等間隔調整學習率

lr_scheduler.StepLR(optimizer,step_size,gamma,last_epoch=-1)

主要引數:step_size調整間隔數 gamma調整係數
設定step_size=50,每隔50個epoch時調整學習率,具體是用當前學習率乘以gamma即\(lr=lr*gamma\)
調整方式\(lr=lr*gamma\) (gamma通常取0.1縮小10倍、0.5縮小一半)

實驗

LR = 0.1        //設定初始學習率
iteration = 10
max_epoch = 200 
# --------- fake data and optimizer  ---------

weights = torch.randn((1), requires_grad=True)
target = torch.zeros((1))
//構建虛擬優化器,為了 lr_scheduler關聯優化器
optimizer = optim.SGD([weights], lr=LR, momentum=0.9)

# ---------------- 1 Step LR --------
# flag = 0
flag = 1
if flag:
//設定optimizer、step_size等間隔數量:多少個epoch之後就更新學習率lr、gamma
    scheduler_lr = optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1)  # 設定學習率下降策略

    lr_list, epoch_list = list(), list()
    for epoch in range(max_epoch):

        lr_list.append(scheduler_lr.get_lr())
        epoch_list.append(epoch)

        for i in range(iteration):

            loss = torch.pow((weights - target), 2)
            loss.backward()
//優化器引數更新
            optimizer.step()
            optimizer.zero_grad()
//學習率更新
        scheduler_lr.step()

    plt.plot(epoch_list, lr_list, label="Step LR Scheduler")
    plt.xlabel("Epoch")
    plt.ylabel("Learning rate")
    plt.legend()
    plt.show()

結果


圖3 等間隔調整學習率StepLR

2、MultiStepLR
功能:按給定間隔調整學習率

lr_scheduler.MultiStepLR(optimizer,milestones,gamma,last_epoch=-1)

主要引數:milestones設定調整時刻數 gamma調整係數
如構建個list設定milestones=[50,125,180],在第50次、125次、180次時分別調整學習率,具體是用當前學習率乘以gamma即\(lr=lr*gamma\)
調整方式:\(lr=lr*gamma\) (gamma通常取0.1縮小10倍、0.5縮小一半)

# ------------------------------ 2 Multi Step LR ------------------------------
# flag = 0
flag = 1
if flag:

    milestones = [50, 125, 160]
    scheduler_lr = optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.1)

    lr_list, epoch_list = list(), list()
    for epoch in range(max_epoch):

        lr_list.append(scheduler_lr.get_lr())
        epoch_list.append(epoch)

        for i in range(iteration):

            loss = torch.pow((weights - target), 2)
            loss.backward()

            optimizer.step()
            optimizer.zero_grad()

        scheduler_lr.step()

    plt.plot(epoch_list, lr_list, label="Multi Step LR Scheduler\nmilestones:{}".format(milestones))
    plt.xlabel("Epoch")
    plt.ylabel("Learning rate")
    plt.legend()
    plt.show()

3、ExponentialLR
功能:按指數衰減調整學習率

lr_scheduler.ExponentialLR(optimizer,milestones,gamma,last_epoch=-1)

主要引數: gamma指數的底
如構建個list設定milestones=[50,125,180],在第50次、125次、180次時調整學習率,具體是用當前學習率乘以gamma即\(lr=lr*gamma\)
調整方式\(lr=lr*gamma**epoch\) (gamma通常會設定為接近於1的數值,如0.95;\(lr=lr*gamma^{epoch}→0.1*0.95^1→0.1*0.95^2\))

# ------------- 3 Exponential LR -----------
# flag = 0
flag = 1
if flag:

    gamma = 0.95
    scheduler_lr = optim.lr_scheduler.ExponentialLR(optimizer, gamma=gamma)
    ...

結果


圖4 按指數衰減調整學習率ExponentialLR

4、CosineAnnealingLR
功能:餘弦週期調整學習率

lr_scheduler.ExponentialLR(optimizer,T_max,eta_min=0,last_epoch=-1)

主要引數: T_max下降週期,eta_min學習率下限

調整方式

\[ \eta_t=\eta_{min}+\frac{1}{2}(\eta_{max}-\eta_{min})(1+cos(\frac{T_{cur}}{T_{max}}\pi)) \]

結果


圖5 餘弦週期調整學習率CosineAnnealingLR

5、ReduceLRonPlateau
功能:監控指標,當指標不再變化則調整(很實用)
比如監控Loss不再下降、或者分類準確率acc不再上升就進行學習率的調整。

lr_scheduler.ExponentialLR(optimizer,mode='min',
factor=0.1,patience=10,verbose=False,threshold=0.0001,
threshold_mode='rel',cooldown=0,min_lr=0,eps=1e-08)

主要引數
mode:min/max兩種模式;
在min模式下,觀察監控指標是否下降,用於監控Loss;在max模式下觀察監控指標是否上升,用於監控分類準確率acc。
fator調整係數——相當於上面幾種調整策略中的gamma值;
patience:"耐心",接收幾次不變化;一定要連續多少次不發生變化(patience=10,某指標連續10次epoch沒有變化就進行lr調整)
cooldown:“冷卻時間”,停止監控一段時間;(cooldown=10,某指標連續10次epoch沒有變化就進行lr調整)
verbose:是否列印日誌;布林變數;
min_lr:學習率下限;
eps:學習率衰減最小值。

# ------------------- 5 Reduce LR On Plateau -----
# flag = 0
flag = 1
if flag:
    loss_value = 0.5
    accuray = 0.9

    factor = 0.1
    mode = "min"
    patience = 10
    cooldown = 10
    min_lr = 1e-4
    verbose = True

    scheduler_lr = optim.lr_scheduler.ReduceLROnPlateau(optimizer, factor=factor, mode=mode, patience=patience,
                                                        cooldown=cooldown, min_lr=min_lr, verbose=verbose)

    for epoch in range(max_epoch):
        for i in range(iteration):

            # train(...)

            optimizer.step()
            optimizer.zero_grad()

        # if epoch == 5:
        #  loss_value = 0.4

        scheduler_lr.step(loss_value)

6、LambdaLR
功能:自定義調整策略

lr_scheduler.ExponentialLR(optimizer,lr_lambda,last_epoch=-1)

主要引數
lr_lambda:function or list
如果是1個list,每1個元素也必須是1個function。

該方法適用於對不同的引數組設定不同的學習率調整策略。

# ------------------------------ 6 lambda ------------------------------
# flag = 0
flag = 1
if flag:

    lr_init = 0.1

    weights_1 = torch.randn((6, 3, 5, 5))
    weights_2 = torch.ones((5, 5))

    optimizer = optim.SGD([
        {'params': [weights_1]},
        {'params': [weights_2]}], lr=lr_init)

    lambda1 = lambda epoch: 0.1 ** (epoch // 20)
    lambda2 = lambda epoch: 0.95 ** epoch

    scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])

    lr_list, epoch_list = list(), list()
    for epoch in range(max_epoch):
        for i in range(iteration):

            # train(...)

            optimizer.step()
            optimizer.zero_grad()

        scheduler.step()

        lr_list.append(scheduler.get_lr())
        epoch_list.append(epoch)

        print('epoch:{:5d}, lr:{}'.format(epoch, scheduler.get_lr()))

    plt.plot(epoch_list, [i[0] for i in lr_list], label="lambda 1")
    plt.plot(epoch_list, [i[1] for i in lr_list], label="lambda 2")
    plt.xlabel("Epoch")
    plt.ylabel("Learning Rate")
    plt.title("LambdaLR")
    plt.legend()
    plt.show()

學習率調整策略總結

1、有序調整:Step、MultiStep、Exponential 和CosineAnnealing
學習率更新之前,就知道學習率在什麼時候會調整、調整為多少;
2、自適應調整:ReduceLROnPleateau
監控某一個引數,當該引數不再上升或下降就進行學習率調整。
3、自定義調整:lambda
存在多個引數組,且需要對多個引數組設定不同的學習率調整策略可採用。

要調整學習率,至少應當具有初始學習率,那麼該如何設定初始學習率?
(1)設定較小數:10e-2(0.01)、10e-3(0.001)、10e-4(0.0001);
(2)搜尋最大學習率 《Cyclical learning Rates for Training Nerual Networks》