[動手學深度學習-PyTorch版]-7.3優(yōu)化算法-小批量隨機梯度下降

7.3 小批量隨機梯度下降

在每一次迭代中护昧,梯度下降使用整個訓練數(shù)據(jù)集來計算梯度贺归,因此它有時也被稱為批量梯度下降(batch gradient descent)杰扫。而隨機梯度下降在每次迭代中只隨機采樣一個樣本來計算梯度。正如我們在前幾章中所看到的拦宣,我們還可以在每輪迭代中隨機均勻采樣多個樣本來組成一個小批量菊霜,然后使用這個小批量來計算梯度屯曹。下面就來描述小批量隨機梯度下降抄谐。


image.png

7.3.1 讀取數(shù)據(jù)

本章里我們將使用一個來自NASA的測試不同飛機機翼噪音的數(shù)據(jù)集來比較各個優(yōu)化算法 [1]。我們使用該數(shù)據(jù)集的前1,500個樣本和5個特征筹燕,并使用標準化對數(shù)據(jù)進行預處理轧飞。

%matplotlib inline
import numpy as np
import time
import torch
from torch import nn, optim
import sys
sys.path.append("..") 
import d2lzh_pytorch as d2l

def get_data_ch7():  # 本函數(shù)已保存在d2lzh_pytorch包中方便以后使用
    data = np.genfromtxt('../../data/airfoil_self_noise.dat', delimiter='\t')
    data = (data - data.mean(axis=0)) / data.std(axis=0)
    return torch.tensor(data[:1500, :-1], dtype=torch.float32), \
    torch.tensor(data[:1500, -1], dtype=torch.float32) # 前1500個樣本(每個樣本5個特征)

features, labels = get_data_ch7()
features.shape # torch.Size([1500, 5])

7.3.2 從零開始實現(xiàn)

3.2節(jié)(線性回歸的從零開始實現(xiàn))中已經(jīng)實現(xiàn)過小批量隨機梯度下降算法。我們在這里將它的輸入?yún)?shù)變得更加通用撒踪,主要是為了方便本章后面介紹的其他優(yōu)化算法也可以使用同樣的輸入过咬。具體來說,我們添加了一個狀態(tài)輸入states并將超參數(shù)放在字典hyperparams里糠涛。此外援奢,我們將在訓練函數(shù)里對各個小批量樣本的損失求平均兼犯,因此優(yōu)化算法里的梯度不需要除以批量大小忍捡。

def sgd(params, states, hyperparams):
    for p in params:
        p.data -= hyperparams['lr'] * p.grad.data

下面實現(xiàn)一個通用的訓練函數(shù),以方便本章后面介紹的其他優(yōu)化算法使用切黔。它初始化一個線性回歸模型砸脊,然后可以使用小批量隨機梯度下降以及后續(xù)小節(jié)介紹的其他算法來訓練模型。

# 本函數(shù)已保存在d2lzh_pytorch包中方便以后使用
def train_ch7(optimizer_fn, states, hyperparams, features, labels,
              batch_size=10, num_epochs=2):
    # 初始化模型
    net, loss = d2l.linreg, d2l.squared_loss

    w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01, size=(features.shape[1], 1)), dtype=torch.float32),
                           requires_grad=True)
    b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32), requires_grad=True)

    def eval_loss():
        return loss(net(features, w, b), labels).mean().item()

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)

    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            l = loss(net(X, w, b), y).mean()  # 使用平均損失

            # 梯度清零
            if w.grad is not None:
                w.grad.data.zero_()
                b.grad.data.zero_()

            l.backward()
            optimizer_fn([w, b], states, hyperparams)  # 迭代模型參數(shù)
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())  # 每100個樣本記錄下當前訓練誤差
    # 打印結果和作圖
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    d2l.set_figsize()
    d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    d2l.plt.xlabel('epoch')
    d2l.plt.ylabel('loss')

當批量大小為樣本總數(shù)1,500時纬霞,優(yōu)化使用的是梯度下降凌埂。梯度下降的1個迭代周期對模型參數(shù)只迭代1次∈撸可以看到6次迭代后目標函數(shù)值(訓練損失)的下降趨向了平穩(wěn)瞳抓。

def train_sgd(lr, batch_size, num_epochs=2):
    train_ch7(sgd, None, {'lr': lr}, features, labels, batch_size, num_epochs)

train_sgd(1, 1500, 6)

輸出:

loss: 0.243605, 0.014335 sec per epoch
image.png

當批量大小為1時,優(yōu)化使用的是隨機梯度下降伏恐。為了簡化實現(xiàn)孩哑,有關(小批量)隨機梯度下降的實驗中,我們未對學習率進行自我衰減翠桦,而是直接采用較小的常數(shù)學習率横蜒。隨機梯度下降中,每處理一個樣本會更新一次自變量(模型參數(shù)),一個迭代周期里會對自變量進行1,500次更新丛晌〗龃叮可以看到,目標函數(shù)值的下降在1個迭代周期后就變得較為平緩澎蛛。

train_sgd(0.005, 1)

輸出:

loss: 0.243433, 0.270011 sec per epoch
image.png

雖然隨機梯度下降和梯度下降在一個迭代周期里都處理了1,500個樣本抚垄,但實驗中隨機梯度下降的一個迭代周期耗時更多。這是因為隨機梯度下降在一個迭代周期里做了更多次的自變量迭代谋逻,而且單樣本的梯度計算難以有效利用矢量計算督勺。

當批量大小為10時,優(yōu)化使用的是小批量隨機梯度下降斤贰。它在每個迭代周期的耗時介于梯度下降和隨機梯度下降的耗時之間智哀。

train_sgd(0.05, 10)

輸出:

loss: 0.242805, 0.078792 sec per epoch
image.png

7.3.3 簡潔實現(xiàn)

在PyTorch里可以通過創(chuàng)建optimizer實例來調用優(yōu)化算法。這能讓實現(xiàn)更簡潔荧恍。下面實現(xiàn)一個通用的訓練函數(shù)瓷叫,它通過優(yōu)化算法的函數(shù)optimizer_fn和超參數(shù)optimizer_hyperparams來創(chuàng)建optimizer實例。

# 本函數(shù)與原書不同的是這里第一個參數(shù)優(yōu)化器函數(shù)而不是優(yōu)化器的名字
# 例如: optimizer_fn=torch.optim.SGD, optimizer_hyperparams={"lr": 0.05}
def train_pytorch_ch7(optimizer_fn, optimizer_hyperparams, features, labels,
                    batch_size=10, num_epochs=2):
    # 初始化模型
    net = nn.Sequential(
        nn.Linear(features.shape[-1], 1)
    )
    loss = nn.MSELoss()
    optimizer = optimizer_fn(net.parameters(), **optimizer_hyperparams)

    def eval_loss():
        return loss(net(features).view(-1), labels).item() / 2

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)

    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            # 除以2是為了和train_ch7保持一致, 因為squared_loss中除了2
            l = loss(net(X).view(-1), y) / 2 

            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())
    # 打印結果和作圖
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    d2l.set_figsize()
    d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    d2l.plt.xlabel('epoch')
    d2l.plt.ylabel('loss')

使用PyTorch重復上一個實驗送巡。

train_pytorch_ch7(optim.SGD, {"lr": 0.05}, features, labels, 10)

輸出:

loss: 0.245491, 0.044150 sec per epoch
image.png

小結

  • 小批量隨機梯度每次隨機均勻采樣一個小批量的訓練樣本來計算梯度摹菠。
  • 在實際中,(小批量)隨機梯度下降的學習率可以在迭代過程中自我衰減骗爆。
  • 通常次氨,小批量隨機梯度在每個迭代周期的耗時介于梯度下降和隨機梯度下降的耗時之間。

參考文獻

[1] 飛機機翼噪音數(shù)據(jù)集摘投。https://archive.ics.uci.edu/ml/datasets/Airfoil+Self-Noise


注:除代碼外本節(jié)與原書此節(jié)基本相同煮寡,原書傳送門

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市犀呼,隨后出現(xiàn)的幾起案子幸撕,更是在濱河造成了極大的恐慌,老刑警劉巖外臂,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坐儿,死亡現(xiàn)場離奇詭異,居然都是意外死亡宋光,警方通過查閱死者的電腦和手機貌矿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罪佳,“玉大人逛漫,你說我怎么就攤上這事」矫瘢” “怎么了尽楔?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵投储,是天一觀的道長。 經(jīng)常有香客問我阔馋,道長玛荞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任呕寝,我火速辦了婚禮勋眯,結果婚禮上,老公的妹妹穿的比我還像新娘下梢。我一直安慰自己客蹋,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布孽江。 她就那樣靜靜地躺著讶坯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岗屏。 梳的紋絲不亂的頭發(fā)上辆琅,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音这刷,去河邊找鬼婉烟。 笑死,一個胖子當著我的面吹牛暇屋,可吹牛的內容都是我干的似袁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咐刨,長吁一口氣:“原來是場噩夢啊……” “哼昙衅!你這毒婦竟也來了?” 一聲冷哼從身側響起所宰,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绒尊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后仔粥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蟹但,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年躯泰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片华糖。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡麦向,死狀恐怖,靈堂內的尸體忽然破棺而出客叉,到底是詐尸還是另有隱情诵竭,我是刑警寧澤话告,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站卵慰,受9級特大地震影響沙郭,放射性物質發(fā)生泄漏。R本人自食惡果不足惜裳朋,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一病线、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鲤嫡,春花似錦送挑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诫肠,卻和暖如春赡突,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背区赵。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工惭缰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笼才。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓漱受,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骡送。 傳聞我的和親對象是個殘疾皇子昂羡,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內容