第三天-過擬合欠擬合及其解決方案,梯度消失梯度爆炸,

過擬合葱色、欠擬合及其解決方案

  1. 過擬合廊营、欠擬合的概念
  2. 權(quán)重衰減
  3. 丟棄法

模型選擇衰齐、過擬合和欠擬合

訓(xùn)練誤差和泛化誤差

在解釋上述現(xiàn)象之前任斋,我們需要區(qū)分訓(xùn)練誤差(training error)和泛化誤差(generalization error)。通俗來講耻涛,前者指模型在訓(xùn)練數(shù)據(jù)集上表現(xiàn)出的誤差废酷,后者指模型在任意一個測試數(shù)據(jù)樣本上表現(xiàn)出的誤差的期望,并常常通過測試數(shù)據(jù)集上的誤差來近似抹缕。計算訓(xùn)練誤差和泛化誤差可以使用之前介紹過的損失函數(shù)澈蟆,例如線性回歸用到的平方損失函數(shù)和softmax回歸用到的交叉熵損失函數(shù)。

機器學(xué)習(xí)模型應(yīng)關(guān)注降低泛化誤差卓研。

模型選擇

驗證數(shù)據(jù)集

從嚴格意義上講,測試集只能在所有超參數(shù)和模型參數(shù)選定后使用一次。不可以使用測試數(shù)據(jù)選擇模型牙捉,如調(diào)參幕屹。由于無法從訓(xùn)練誤差估計泛化誤差,因此也不應(yīng)只依賴訓(xùn)練數(shù)據(jù)選擇模型磨淌。鑒于此疲憋,我們可以預(yù)留一部分在訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集以外的數(shù)據(jù)來進行模型選擇。這部分數(shù)據(jù)被稱為驗證數(shù)據(jù)集伦糯,簡稱驗證集(validation set)柜某。例如嗽元,我們可以從給定的訓(xùn)練集中隨機選取一小部分作為驗證集,而將剩余部分作為真正的訓(xùn)練集喂击。

K折交叉驗證

由于驗證數(shù)據(jù)集不參與模型訓(xùn)練剂癌,當訓(xùn)練數(shù)據(jù)不夠用時,預(yù)留大量的驗證數(shù)據(jù)顯得太奢侈翰绊。一種改善的方法是K折交叉驗證(K-fold cross-validation)佩谷。在K折交叉驗證中,我們把原始訓(xùn)練數(shù)據(jù)集分割成K個不重合的子數(shù)據(jù)集监嗜,然后我們做K次模型訓(xùn)練和驗證谐檀。每一次,我們使用一個子數(shù)據(jù)集驗證模型裁奇,并使用其他K-1個子數(shù)據(jù)集來訓(xùn)練模型桐猬。在這K次訓(xùn)練和驗證中,每次用來驗證模型的子數(shù)據(jù)集都不同刽肠。最后溃肪,我們對這K次訓(xùn)練誤差和驗證誤差分別求平均。

過擬合和欠擬合

接下來音五,我們將探究模型訓(xùn)練中經(jīng)常出現(xiàn)的兩類典型問題:

  • 一類是模型無法得到較低的訓(xùn)練誤差惫撰,我們將這一現(xiàn)象稱作欠擬合(underfitting);
  • 另一類是模型的訓(xùn)練誤差遠小于它在測試數(shù)據(jù)集上的誤差躺涝,我們稱該現(xiàn)象為過擬合(overfitting)厨钻。
    在實踐中,我們要盡可能同時應(yīng)對欠擬合和過擬合坚嗜。雖然有很多因素可能導(dǎo)致這兩種擬合問題夯膀,在這里我們重點討論兩個因素:模型復(fù)雜度和訓(xùn)練數(shù)據(jù)集大小。

模型復(fù)雜度

為了解釋模型復(fù)雜度惶傻,我們以多項式函數(shù)擬合為例棍郎。給定一個由標量數(shù)據(jù)特征x和對應(yīng)的標量標簽y組成的訓(xùn)練數(shù)據(jù)集,多項式函數(shù)擬合的目標是找一個K階多項式函數(shù)

\hat{y} = b + \sum_{k=1}^K x^k w_k

來近似 y银室。在上式中涂佃,w_k是模型的權(quán)重參數(shù),b是偏差參數(shù)蜈敢。與線性回歸相同辜荠,多項式函數(shù)擬合也使用平方損失函數(shù)。特別地抓狭,一階多項式函數(shù)擬合又叫線性函數(shù)擬合伯病。

給定訓(xùn)練數(shù)據(jù)集,模型復(fù)雜度和誤差之間的關(guān)系:

Image Name

訓(xùn)練數(shù)據(jù)集大小

影響欠擬合和過擬合的另一個重要因素是訓(xùn)練數(shù)據(jù)集的大小。一般來說午笛,如果訓(xùn)練數(shù)據(jù)集中樣本數(shù)過少惭蟋,特別是比模型參數(shù)數(shù)量(按元素計)更少時,過擬合更容易發(fā)生药磺。此外告组,泛化誤差不會隨訓(xùn)練數(shù)據(jù)集里樣本數(shù)量增加而增大。因此癌佩,在計算資源允許的范圍之內(nèi)木缝,我們通常希望訓(xùn)練數(shù)據(jù)集大一些,特別是在模型復(fù)雜度較高時围辙,例如層數(shù)較多的深度學(xué)習(xí)模型我碟。

多項式函數(shù)擬合實驗

%matplotlib inline
import torch
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.__version__)
1.3.0

初始化模型參數(shù)

n_train, n_test, true_w, true_b = 100, 100, [1.2, -3.4, 5.6], 5
features = torch.randn((n_train + n_test, 1))
poly_features = torch.cat((features, torch.pow(features, 2), torch.pow(features, 3)), 1) 
labels = (true_w[0] * poly_features[:, 0] + true_w[1] * poly_features[:, 1]
          + true_w[2] * poly_features[:, 2] + true_b)
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
features[:2], poly_features[:2], labels[:2]
(tensor([[-0.4231],
         [ 0.1509]]), tensor([[-0.4231,  0.1790, -0.0757],
         [ 0.1509,  0.0228,  0.0034]]), tensor([3.4592, 5.1112]))

定義、訓(xùn)練和測試模型

def semilogy(x_vals, y_vals, x_label, y_label, x2_vals=None, y2_vals=None,
             legend=None, figsize=(3.5, 2.5)):
    # d2l.set_figsize(figsize)
    d2l.plt.xlabel(x_label)
    d2l.plt.ylabel(y_label)
    d2l.plt.semilogy(x_vals, y_vals)
    if x2_vals and y2_vals:
        d2l.plt.semilogy(x2_vals, y2_vals, linestyle=':')
        d2l.plt.legend(legend)
num_epochs, loss = 100, torch.nn.MSELoss()

def fit_and_plot(train_features, test_features, train_labels, test_labels):
    # 初始化網(wǎng)絡(luò)模型
    net = torch.nn.Linear(train_features.shape[-1], 1)
    # 通過Linear文檔可知姚建,pytorch已經(jīng)將參數(shù)初始化了矫俺,所以我們這里就不手動初始化了
    
    # 設(shè)置批量大小
    batch_size = min(10, train_labels.shape[0])    
    dataset = torch.utils.data.TensorDataset(train_features, train_labels)      # 設(shè)置數(shù)據(jù)集
    train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True) # 設(shè)置獲取數(shù)據(jù)方式
    
    optimizer = torch.optim.SGD(net.parameters(), lr=0.01)                      # 設(shè)置優(yōu)化函數(shù),使用的是隨機梯度下降優(yōu)化
    train_ls, test_ls = [], []
    for _ in range(num_epochs):
        for X, y in train_iter:                                                 # 取一個批量的數(shù)據(jù)
            l = loss(net(X), y.view(-1, 1))                                     # 輸入到網(wǎng)絡(luò)中計算輸出桥胞,并和標簽比較求得損失函數(shù)
            optimizer.zero_grad()                                               # 梯度清零恳守,防止梯度累加干擾優(yōu)化
            l.backward()                                                        # 求梯度
            optimizer.step()                                                    # 迭代優(yōu)化函數(shù),進行參數(shù)優(yōu)化
        train_labels = train_labels.view(-1, 1)
        test_labels = test_labels.view(-1, 1)
        train_ls.append(loss(net(train_features), train_labels).item())         # 將訓(xùn)練損失保存到train_ls中
        test_ls.append(loss(net(test_features), test_labels).item())            # 將測試損失保存到test_ls中
    print('final epoch: train loss', train_ls[-1], 'test loss', test_ls[-1])    
    semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
             range(1, num_epochs + 1), test_ls, ['train', 'test'])
    print('weight:', net.weight.data,
          '\nbias:', net.bias.data)

三階多項式函數(shù)擬合(正常)

fit_and_plot(poly_features[:n_train, :], poly_features[n_train:, :], labels[:n_train], labels[n_train:])
final epoch: train loss 0.000102330552181229 test loss 0.00011141761933686212
weight: tensor([[ 1.2026, -3.3995,  5.5994]]) 
bias: tensor([4.9978])

<img src="https://cdn.kesci.com/rt_upload/CD685B472B744329A1CFC47C9F0B5E89/q5vzv0gps1.png">

線性函數(shù)擬合(欠擬合)

fit_and_plot(features[:n_train, :], features[n_train:, :], labels[:n_train], labels[n_train:])
final epoch: train loss 91.04450988769531 test loss 116.70631408691406
weight: tensor([[15.6120]]) 
bias: tensor([2.9952])

<img src="https://cdn.kesci.com/rt_upload/33AD626DA0B94DB7A28D47697312B45D/q5vzv0jgr5.png">

訓(xùn)練樣本不足(過擬合)

fit_and_plot(poly_features[0:2, :], poly_features[n_train:, :], labels[0:2], labels[n_train:])
final epoch: train loss 0.9687690138816833 test loss 310.56109619140625
weight: tensor([[ 0.0137, -0.0228,  0.3522]]) 
bias: tensor([3.7376])

<img src="https://cdn.kesci.com/rt_upload/AB13F65A70A9484788F8004E427EC290/q5vzv1n6lh.png">

權(quán)重衰減

方法

權(quán)重衰減等價于 L_2 范數(shù)正則化(regularization)贩虾。正則化通過為模型損失函數(shù)添加懲罰項使學(xué)出的模型參數(shù)值較小,是應(yīng)對過擬合的常用手段沥阱。

L2 范數(shù)正則化(regularization)

L_2范數(shù)正則化在模型原損失函數(shù)基礎(chǔ)上添加L_2范數(shù)懲罰項缎罢,從而得到訓(xùn)練所需要最小化的函數(shù)。L_2范數(shù)懲罰項指的是模型權(quán)重參數(shù)每個元素的平方和與一個正的常數(shù)的乘積考杉。以線性回歸中的線性回歸損失函數(shù)為例

\ell(w_1, w_2, b) = \frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right)^2

其中w_1, w_2是權(quán)重參數(shù)策精,b是偏差參數(shù),樣本i的輸入為x_1^{(i)}, x_2^{(i)}崇棠,標簽為y^{(i)}咽袜,樣本數(shù)為n。將權(quán)重參數(shù)用向量\boldsymbol{w} = [w_1, w_2]表示枕稀,帶有L_2范數(shù)懲罰項的新?lián)p失函數(shù)為

\ell(w_1, w_2, b) + \frac{\lambda}{2n} |\boldsymbol{w}|^2,

其中超參數(shù)\lambda > 0询刹。當權(quán)重參數(shù)均為0時,懲罰項最小萎坷。當\lambda較大時凹联,懲罰項在損失函數(shù)中的比重較大,這通常會使學(xué)到的權(quán)重參數(shù)的元素較接近0哆档。當\lambda設(shè)為0時蔽挠,懲罰項完全不起作用。上式中L_2范數(shù)平方|\boldsymbol{w}|^2展開后得到w_1^2 + w_2^2瓜浸。
有了L_2范數(shù)懲罰項后澳淑,在小批量隨機梯度下降中比原,我們將線性回歸一節(jié)中權(quán)重w_1w_2的迭代方式更改為

\begin{aligned} w_1 &\leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_1 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_1^{(i)} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right),\\ w_2 &\leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_2 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_2^{(i)} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right). \end{aligned}

可見,L_2范數(shù)正則化令權(quán)重w_1w_2先自乘小于1的數(shù)杠巡,再減去不含懲罰項的梯度量窘。因此,L_2范數(shù)正則化又叫權(quán)重衰減忽孽。權(quán)重衰減通過懲罰絕對值較大的模型參數(shù)為需要學(xué)習(xí)的模型增加了限制绑改,這可能對過擬合有效。

高維線性回歸實驗從零開始的實現(xiàn)

下面兄一,我們以高維線性回歸為例來引入一個過擬合問題厘线,并使用權(quán)重衰減來應(yīng)對過擬合。設(shè)數(shù)據(jù)樣本特征的維度為p出革。對于訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集中特征為x_1, x_2, \ldots, x_p的任一樣本造壮,我們使用如下的線性函數(shù)來生成該樣本的標簽:

y = 0.05 + \sum_{i = 1}^p 0.01x_i + \epsilon

其中噪聲項\epsilon服從均值為0、標準差為0.01的正態(tài)分布骂束。為了較容易地觀察過擬合耳璧,我們考慮高維線性回歸問題,如設(shè)維度p=200展箱;同時旨枯,我們特意把訓(xùn)練數(shù)據(jù)集的樣本數(shù)設(shè)低,如20混驰。

%matplotlib inline
import torch
import torch.nn as nn
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.__version__)
1.3.0

初始化模型參數(shù)

與前面觀察過擬合和欠擬合現(xiàn)象的時候相似攀隔,在這里不再解釋。

n_train, n_test, num_inputs = 20, 100, 200
true_w, true_b = torch.ones(num_inputs, 1) * 0.01, 0.05

features = torch.randn((n_train + n_test, num_inputs))
labels = torch.matmul(features, true_w) + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
train_features, test_features = features[:n_train, :], features[n_train:, :]
train_labels, test_labels = labels[:n_train], labels[n_train:]
# 定義參數(shù)初始化函數(shù)栖榨,初始化模型參數(shù)并且附上梯度
def init_params():
    w = torch.randn((num_inputs, 1), requires_grad=True)
    b = torch.zeros(1, requires_grad=True)
    return [w, b]

定義L2范數(shù)懲罰項

def l2_penalty(w):
    return (w**2).sum() / 2

定義訓(xùn)練和測試

batch_size, num_epochs, lr = 1, 100, 0.003
net, loss = d2l.linreg, d2l.squared_loss

dataset = torch.utils.data.TensorDataset(train_features, train_labels)
train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)

def fit_and_plot(lambd):
    w, b = init_params()
    train_ls, test_ls = [], []
    for _ in range(num_epochs):
        for X, y in train_iter:
            # 添加了L2范數(shù)懲罰項
            l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
            l = l.sum()
            
            if w.grad is not None:
                w.grad.data.zero_()
                b.grad.data.zero_()
            l.backward()
            d2l.sgd([w, b], lr, batch_size)
        train_ls.append(loss(net(train_features, w, b), train_labels).mean().item())
        test_ls.append(loss(net(test_features, w, b), test_labels).mean().item())
    d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
                 range(1, num_epochs + 1), test_ls, ['train', 'test'])
    print('L2 norm of w:', w.norm().item())

觀察過擬合

fit_and_plot(lambd=0)
L2 norm of w: 14.960610389709473

<img src="https://cdn.kesci.com/rt_upload/C27406AAA0FD41C6801D55ED4B25D5EA/q5vzv23kg6.svg">

使用權(quán)重衰減

fit_and_plot(lambd=3)
L2 norm of w: 0.04557040333747864

<img src="https://cdn.kesci.com/rt_upload/0770D8C23B8144C59D13D24390E471F0/q5vzv314zc.svg">

簡潔實現(xiàn)

def fit_and_plot_pytorch(wd):
    # 對權(quán)重參數(shù)衰減昆汹。權(quán)重名稱一般是以weight結(jié)尾
    net = nn.Linear(num_inputs, 1)
    nn.init.normal_(net.weight, mean=0, std=1)
    nn.init.normal_(net.bias, mean=0, std=1)
    optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 對權(quán)重參數(shù)衰減
    optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr)  # 不對偏差參數(shù)衰減
    
    train_ls, test_ls = [], []
    for _ in range(num_epochs):
        for X, y in train_iter:
            l = loss(net(X), y).mean()
            optimizer_w.zero_grad()
            optimizer_b.zero_grad()
            
            l.backward()
            
            # 對兩個optimizer實例分別調(diào)用step函數(shù),從而分別更新權(quán)重和偏差
            optimizer_w.step()
            optimizer_b.step()
        train_ls.append(loss(net(train_features), train_labels).mean().item())
        test_ls.append(loss(net(test_features), test_labels).mean().item())
    d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
                 range(1, num_epochs + 1), test_ls, ['train', 'test'])
    print('L2 norm of w:', net.weight.data.norm().item())
fit_and_plot_pytorch(0)
L2 norm of w: 13.382489204406738

<img src="https://cdn.kesci.com/rt_upload/525D01167F0E40509495588D6B0A0FB9/q5vzv41dsv.svg">

fit_and_plot_pytorch(3)
L2 norm of w: 0.05964863300323486

<img src="https://cdn.kesci.com/rt_upload/3FAACA854B9545A8ADADDEB6EE17A680/q5vzv5m74z.svg">

丟棄法

多層感知機中神經(jīng)網(wǎng)絡(luò)圖描述了一個單隱藏層的多層感知機婴栽。其中輸入個數(shù)為4满粗,隱藏單元個數(shù)為5,且隱藏單元h_ii=1, \ldots, 5)的計算表達式為

h_i = \phi\left(x_1 w_{1i} + x_2 w_{2i} + x_3 w_{3i} + x_4 w_{4i} + b_i\right)

這里\phi是激活函數(shù)愚争,x_1, \ldots, x_4是輸入映皆,隱藏單元i的權(quán)重參數(shù)為w_{1i}, \ldots, w_{4i},偏差參數(shù)為b_i准脂。當對該隱藏層使用丟棄法時劫扒,該層的隱藏單元將有一定概率被丟棄掉。設(shè)丟棄概率為p狸膏,那么有p的概率h_i會被清零沟饥,有1-p的概率h_i會除以1-p做拉伸。丟棄概率是丟棄法的超參數(shù)。具體來說贤旷,設(shè)隨機變量\xi_i為0和1的概率分別為p1-p广料。使用丟棄法時我們計算新的隱藏單元h_i'

h_i' = \frac{\xi_i}{1-p} h_i

由于E(\xi_i) = 1-p,因此

E(h_i') = \frac{E(\xi_i)}{1-p}h_i = h_i

即丟棄法不改變其輸入的期望值幼驶。讓我們對之前多層感知機的神經(jīng)網(wǎng)絡(luò)中的隱藏層使用丟棄法艾杏,一種可能的結(jié)果如圖所示,其中h_2h_5被清零盅藻。這時輸出值的計算不再依賴h_2h_5购桑,在反向傳播時,與這兩個隱藏單元相關(guān)的權(quán)重的梯度均為0氏淑。由于在訓(xùn)練中隱藏層神經(jīng)元的丟棄是隨機的勃蜘,即h_1, \ldots, h_5都有可能被清零,輸出層的計算無法過度依賴h_1, \ldots, h_5中的任一個假残,從而在訓(xùn)練模型時起到正則化的作用缭贡,并可以用來應(yīng)對過擬合。在測試模型時辉懒,我們?yōu)榱四玫礁哟_定性的結(jié)果阳惹,一般不使用丟棄法

Image Name

丟棄法從零開始的實現(xiàn)

%matplotlib inline
import torch
import torch.nn as nn
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.__version__)
1.3.0
def dropout(X, drop_prob):
    X = X.float()
    assert 0 <= drop_prob <= 1
    keep_prob = 1 - drop_prob
    # 這種情況下把全部元素都丟棄
    if keep_prob == 0:
        return torch.zeros_like(X)
    mask = (torch.rand(X.shape) < keep_prob).float()
    
    return mask * X / keep_prob
X = torch.arange(16).view(2, 8)
dropout(X, 0)
tensor([[ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11., 12., 13., 14., 15.]])
dropout(X, 0.5)
tensor([[ 0.,  0.,  4.,  0.,  8., 10.,  0., 14.],
        [16., 18.,  0., 22., 24.,  0.,  0.,  0.]])
dropout(X, 1.0)
tensor([[0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.]])
# 參數(shù)的初始化
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256

W1 = torch.tensor(np.random.normal(0, 0.01, size=(num_inputs, num_hiddens1)), dtype=torch.float, requires_grad=True)
b1 = torch.zeros(num_hiddens1, requires_grad=True)
W2 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens1, num_hiddens2)), dtype=torch.float, requires_grad=True)
b2 = torch.zeros(num_hiddens2, requires_grad=True)
W3 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens2, num_outputs)), dtype=torch.float, requires_grad=True)
b3 = torch.zeros(num_outputs, requires_grad=True)

params = [W1, b1, W2, b2, W3, b3]
drop_prob1, drop_prob2 = 0.2, 0.5

def net(X, is_training=True):
    X = X.view(-1, num_inputs)
    H1 = (torch.matmul(X, W1) + b1).relu()
    if is_training:  # 只在訓(xùn)練模型時使用丟棄法
        H1 = dropout(H1, drop_prob1)  # 在第一層全連接后添加丟棄層
    H2 = (torch.matmul(H1, W2) + b2).relu()
    if is_training:
        H2 = dropout(H2, drop_prob2)  # 在第二層全連接后添加丟棄層
    return torch.matmul(H2, W3) + b3
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for X, y in data_iter:
        if isinstance(net, torch.nn.Module):
            net.eval() # 評估模式, 這會關(guān)閉dropout
            acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            net.train() # 改回訓(xùn)練模式
        else: # 自定義的模型
            if('is_training' in net.__code__.co_varnames): # 如果有is_training這個參數(shù)
                # 將is_training設(shè)置成False
                acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item() 
            else:
                acc_sum += (net(X).argmax(dim=1) == y).float().sum().item() 
        n += y.shape[0]
    return acc_sum / n
num_epochs, lr, batch_size = 5, 100.0, 256  # 這里的學(xué)習(xí)率設(shè)置的很大,原因與之前相同眶俩。
loss = torch.nn.CrossEntropyLoss()
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, root='/home/kesci/input/FashionMNIST2065')
d2l.train_ch3(
    net,
    train_iter,
    test_iter,
    loss,
    num_epochs,
    batch_size,
    params,
    lr)

簡潔實現(xiàn)

net = nn.Sequential(
        d2l.FlattenLayer(),
        nn.Linear(num_inputs, num_hiddens1),
        nn.ReLU(),
        nn.Dropout(drop_prob1),
        nn.Linear(num_hiddens1, num_hiddens2), 
        nn.ReLU(),
        nn.Dropout(drop_prob2),
        nn.Linear(num_hiddens2, 10)
        )

for param in net.parameters():
    nn.init.normal_(param, mean=0, std=0.01)
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)

總結(jié)

  • 欠擬合現(xiàn)象:模型無法達到一個較低的誤差

  • 過擬合現(xiàn)象:訓(xùn)練誤差較低但是泛化誤差依然較高莹汤,二者相差較大


梯度消失、梯度爆炸以及Kaggle房價預(yù)測

  1. 梯度消失和梯度爆炸
  2. 考慮到環(huán)境因素的其他問題
  3. Kaggle房價預(yù)測

梯度消失和梯度爆炸

深度模型有關(guān)數(shù)值穩(wěn)定性的典型問題是消失(vanishing)和爆炸(explosion)颠印。

當神經(jīng)網(wǎng)絡(luò)的層數(shù)較多時体啰,模型的數(shù)值穩(wěn)定性容易變差。

假設(shè)一個層數(shù)為L的多層感知機的第l\boldsymbol{H}^{(l)}的權(quán)重參數(shù)為\boldsymbol{W}^{(l)}嗽仪,輸出層\boldsymbol{H}^{(L)}的權(quán)重參數(shù)為\boldsymbol{W}^{(L)}。為了便于討論柒莉,不考慮偏差參數(shù)闻坚,且設(shè)所有隱藏層的激活函數(shù)為恒等映射(identity mapping)\phi(x) = x。給定輸入\boldsymbol{X}兢孝,多層感知機的第l層的輸出\boldsymbol{H}^{(l)} = \boldsymbol{X} \boldsymbol{W}^{(1)} \boldsymbol{W}^{(2)} \ldots \boldsymbol{W}^{(l)}窿凤。此時,如果層數(shù)l較大跨蟹,\boldsymbol{H}^{(l)}的計算可能會出現(xiàn)衰減或爆炸雳殊。舉個例子,假設(shè)輸入和所有層的權(quán)重參數(shù)都是標量窗轩,如權(quán)重參數(shù)為0.2和5夯秃,多層感知機的第30層輸出為輸入\boldsymbol{X}分別與0.2^{30} \approx 1 \times 10^{-21}(消失)和5^{30} \approx 9 \times 10^{20}(爆炸)的乘積。當層數(shù)較多時,梯度的計算也容易出現(xiàn)消失或爆炸仓洼。

隨機初始化模型參數(shù)

在神經(jīng)網(wǎng)絡(luò)中介陶,通常需要隨機初始化模型參數(shù)。下面我們來解釋這樣做的原因色建。

回顧多層感知機一節(jié)描述的多層感知機哺呜。為了方便解釋,假設(shè)輸出層只保留一個輸出單元o_1(刪去o_2o_3以及指向它們的箭頭)箕戳,且隱藏層使用相同的激活函數(shù)某残。如果將每個隱藏單元的參數(shù)都初始化為相等的值,那么在正向傳播時每個隱藏單元將根據(jù)相同的輸入計算出相同的值陵吸,并傳遞至輸出層玻墅。在反向傳播中,每個隱藏單元的參數(shù)梯度值相等走越。因此饲齐,這些參數(shù)在使用基于梯度的優(yōu)化算法迭代后值依然相等遂铡。之后的迭代也是如此。在這種情況下,無論隱藏單元有多少毅桃,隱藏層本質(zhì)上只有1個隱藏單元在發(fā)揮作用。因此谅河,正如在前面的實驗中所做的那樣铡买,我們通常將神經(jīng)網(wǎng)絡(luò)的模型參數(shù),特別是權(quán)重參數(shù)搬素,進行隨機初始化呵晨。

Image Name

PyTorch的默認隨機初始化

隨機初始化模型參數(shù)的方法有很多。在線性回歸的簡潔實現(xiàn)中熬尺,我們使用torch.nn.init.normal_()使模型net的權(quán)重參數(shù)采用正態(tài)分布的隨機初始化方式摸屠。不過,PyTorch中nn.Module的模塊參數(shù)都采取了較為合理的初始化策略(不同類型的layer具體采樣的哪一種初始化方法的可參考源代碼)粱哼,因此一般不用我們考慮季二。

Xavier隨機初始化

還有一種比較常用的隨機初始化方法叫作Xavier隨機初始化。
假設(shè)某全連接層的輸入個數(shù)為a揭措,輸出個數(shù)為b胯舷,Xavier隨機初始化將使該層中權(quán)重參數(shù)的每個元素都隨機采樣于均勻分布

U\left(-\sqrt{\frac{6}{a+b}}, \sqrt{\frac{6}{a+b}}\right).

它的設(shè)計主要考慮到,模型參數(shù)初始化后绊含,每層輸出的方差不該受該層輸入個數(shù)影響桑嘶,且每層梯度的方差也不該受該層輸出個數(shù)影響。

考慮環(huán)境因素

協(xié)變量偏移

這里我們假設(shè)躬充,雖然輸入的分布可能隨時間而改變逃顶,但是標記函數(shù)讨便,即條件分布P(y∣x)不會改變。雖然這個問題容易理解口蝠,但在實踐中也容易忽視器钟。

想想?yún)^(qū)分貓和狗的一個例子。我們的訓(xùn)練數(shù)據(jù)使用的是貓和狗的真實的照片妙蔗,但是在測試時傲霸,我們被要求對貓和狗的卡通圖片進行分類。

cat cat dog dog
Image Name
Image Name
Image Name
Image Name

測試數(shù)據(jù):

cat cat dog dog
Image Name
Image Name
Image Name
Image Name

顯然眉反,這不太可能奏效昙啄。訓(xùn)練集由照片組成,而測試集只包含卡通寸五。在一個看起來與測試集有著本質(zhì)不同的數(shù)據(jù)集上進行訓(xùn)練梳凛,而不考慮如何適應(yīng)新的情況,這是不是一個好主意梳杏。不幸的是韧拒,這是一個非常常見的陷阱。

統(tǒng)計學(xué)家稱這種協(xié)變量變化是因為問題的根源在于特征分布的變化(即協(xié)變量的變化)十性。數(shù)學(xué)上叛溢,我們可以說P(x)改變了,但P(y∣x)保持不變劲适。盡管它的有用性并不局限于此楷掉,當我們認為x導(dǎo)致y時,協(xié)變量移位通常是正確的假設(shè)霞势。

標簽偏移

當我們認為導(dǎo)致偏移的是標簽P(y)上的邊緣分布的變化烹植,但類條件分布是不變的P(x∣y)時,就會出現(xiàn)相反的問題愕贡。當我們認為y導(dǎo)致x時草雕,標簽偏移是一個合理的假設(shè)。例如固以,通常我們希望根據(jù)其表現(xiàn)來預(yù)測診斷結(jié)果促绵。在這種情況下,我們認為診斷引起的表現(xiàn)嘴纺,即疾病引起的癥狀。有時標簽偏移和協(xié)變量移位假設(shè)可以同時成立浓冒。例如栽渴,當真正的標簽函數(shù)是確定的和不變的,那么協(xié)變量偏移將始終保持稳懒,包括如果標簽偏移也保持闲擦。有趣的是慢味,當我們期望標簽偏移和協(xié)變量偏移保持時,使用來自標簽偏移假設(shè)的方法通常是有利的墅冷。這是因為這些方法傾向于操作看起來像標簽的對象纯路,這(在深度學(xué)習(xí)中)與處理看起來像輸入的對象(在深度學(xué)習(xí)中)相比相對容易一些。

病因(要預(yù)測的診斷結(jié)果)導(dǎo)致 癥狀(觀察到的結(jié)果)寞忿。

訓(xùn)練數(shù)據(jù)集驰唬,數(shù)據(jù)很少只包含流感p(y)的樣本。

而測試數(shù)據(jù)集有流感p(y)和流感q(y)腔彰,其中不變的是流感癥狀p(x|y)叫编。

概念偏移

另一個相關(guān)的問題出現(xiàn)在概念轉(zhuǎn)換中,即標簽本身的定義發(fā)生變化的情況霹抛。這聽起來很奇怪搓逾,畢竟貓就是貓。的確杯拐,貓的定義可能不會改變霞篡,但我們能不能對軟飲料也這么說呢?事實證明端逼,如果我們周游美國朗兵,按地理位置轉(zhuǎn)移數(shù)據(jù)來源,我們會發(fā)現(xiàn)裳食,即使是如圖所示的這個簡單術(shù)語的定義也會發(fā)生相當大的概念轉(zhuǎn)變矛市。

Image Name

美國軟飲料名稱的概念轉(zhuǎn)變
如果我們要建立一個機器翻譯系統(tǒng),分布P(y∣x)可能因我們的位置而異诲祸。這個問題很難發(fā)現(xiàn)浊吏。另一個可取之處是P(y∣x)通常只是逐漸變化。

Kaggle 房價預(yù)測實戰(zhàn)

作為深度學(xué)習(xí)基礎(chǔ)篇章的總結(jié)救氯,我們將對本章內(nèi)容學(xué)以致用找田。下面着憨,讓我們動手實戰(zhàn)一個Kaggle比賽:房價預(yù)測墩衙。本節(jié)將提供未經(jīng)調(diào)優(yōu)的數(shù)據(jù)的預(yù)處理、模型的設(shè)計和超參數(shù)的選擇甲抖。我們希望讀者通過動手操作漆改、仔細觀察實驗現(xiàn)象、認真分析實驗結(jié)果并不斷調(diào)整方法准谚,得到令自己滿意的結(jié)果挫剑。

%matplotlib inline
import torch
import torch.nn as nn
import numpy as np
import pandas as pd
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.__version__)
torch.set_default_tensor_type(torch.FloatTensor)
1.3.0

獲取和讀取數(shù)據(jù)集

比賽數(shù)據(jù)分為訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集。兩個數(shù)據(jù)集都包括每棟房子的特征柱衔,如街道類型樊破、建造年份愉棱、房頂類型、地下室狀況等特征值哲戚。這些特征值有連續(xù)的數(shù)字奔滑、離散的標簽甚至是缺失值“na”。只有訓(xùn)練數(shù)據(jù)集包括了每棟房子的價格顺少,也就是標簽朋其。我們可以訪問比賽網(wǎng)頁,點擊“Data”標簽祈纯,并下載這些數(shù)據(jù)集令宿。

我們將通過pandas庫讀入并處理數(shù)據(jù)。在導(dǎo)入本節(jié)需要的包前請確保已安裝pandas庫腕窥。
假設(shè)解壓后的數(shù)據(jù)位于/home/kesci/input/houseprices2807/目錄粒没,它包括兩個csv文件。下面使用pandas讀取這兩個文件簇爆。

test_data = pd.read_csv("/home/kesci/input/houseprices2807/house-prices-advanced-regression-techniques/test.csv")
train_data = pd.read_csv("/home/kesci/input/houseprices2807/house-prices-advanced-regression-techniques/train.csv")

訓(xùn)練數(shù)據(jù)集包括1460個樣本癞松、80個特征和1個標簽。

train_data.shape
(1460, 81)

測試數(shù)據(jù)集包括1459個樣本和80個特征入蛆。我們需要將測試數(shù)據(jù)集中每個樣本的標簽預(yù)測出來响蓉。

test_data.shape
(1459, 80)

讓我們來查看前4個樣本的前4個特征、后2個特征和標簽(SalePrice):

train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]]

<div>
<style scoped>
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}

.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}

</style>
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>Id</th>
<th>MSSubClass</th>
<th>MSZoning</th>
<th>LotFrontage</th>
<th>SaleType</th>
<th>SaleCondition</th>
<th>SalePrice</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
<td>60</td>
<td>RL</td>
<td>65.0</td>
<td>WD</td>
<td>Normal</td>
<td>208500</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>20</td>
<td>RL</td>
<td>80.0</td>
<td>WD</td>
<td>Normal</td>
<td>181500</td>
</tr>
<tr>
<td>2</td>
<td>3</td>
<td>60</td>
<td>RL</td>
<td>68.0</td>
<td>WD</td>
<td>Normal</td>
<td>223500</td>
</tr>
<tr>
<td>3</td>
<td>4</td>
<td>70</td>
<td>RL</td>
<td>60.0</td>
<td>WD</td>
<td>Abnorml</td>
<td>140000</td>
</tr>
</tbody>
</table>
</div>

可以看到第一個特征是Id哨毁,它能幫助模型記住每個訓(xùn)練樣本枫甲,但難以推廣到測試樣本,所以我們不使用它來訓(xùn)練扼褪。我們將所有的訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)的79個特征按樣本連結(jié)想幻。

all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))

預(yù)處理數(shù)據(jù)

我們對連續(xù)數(shù)值的特征做標準化(standardization):設(shè)該特征在整個數(shù)據(jù)集上的均值為\mu,標準差為\sigma话浇。那么脏毯,我們可以將該特征的每個值先減去\mu再除以\sigma得到標準化后的每個特征值。對于缺失的特征值幔崖,我們將其替換成該特征的均值食店。

numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
all_features[numeric_features] = all_features[numeric_features].apply(
    lambda x: (x - x.mean()) / (x.std()))
# 標準化后,每個數(shù)值特征的均值變?yōu)?赏寇,所以可以直接用0來替換缺失值
all_features[numeric_features] = all_features[numeric_features].fillna(0)

接下來將離散數(shù)值轉(zhuǎn)成指示特征吉嫩。舉個例子,假設(shè)特征MSZoning里面有兩個不同的離散值RL和RM嗅定,那么這一步轉(zhuǎn)換將去掉MSZoning特征率挣,并新加兩個特征MSZoning_RL和MSZoning_RM,其值為0或1露戒。如果一個樣本原來在MSZoning里的值為RL椒功,那么有MSZoning_RL=1且MSZoning_RM=0。

# dummy_na=True將缺失值也當作合法的特征值并為其創(chuàng)建指示特征
all_features = pd.get_dummies(all_features, dummy_na=True)
all_features.shape
(2919, 331)

可以看到這一步轉(zhuǎn)換將特征數(shù)從79增加到了331智什。

最后动漾,通過values屬性得到NumPy格式的數(shù)據(jù),并轉(zhuǎn)成Tensor方便后面的訓(xùn)練荠锭。

n_train = train_data.shape[0]
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float)
train_labels = torch.tensor(train_data.SalePrice.values, dtype=torch.float).view(-1, 1)

訓(xùn)練模型

loss = torch.nn.MSELoss()

def get_net(feature_num):
    net = nn.Linear(feature_num, 1)
    for param in net.parameters():
        nn.init.normal_(param, mean=0, std=0.01)
    return net

下面定義比賽用來評價模型的對數(shù)均方根誤差旱眯。給定預(yù)測值\hat y_1, \ldots, \hat y_n和對應(yīng)的真實標簽y_1,\ldots, y_n,它的定義為

\sqrt{\frac{1}{n}\sum_{i=1}^n\left(\log(y_i)-\log(\hat y_i)\right)^2}.

對數(shù)均方根誤差的實現(xiàn)如下证九。

def log_rmse(net, features, labels):
    with torch.no_grad():
        # 將小于1的值設(shè)成1删豺,使得取對數(shù)時數(shù)值更穩(wěn)定
        clipped_preds = torch.max(net(features), torch.tensor(1.0))
        rmse = torch.sqrt(2 * loss(clipped_preds.log(), labels.log()).mean())
    return rmse.item()

下面的訓(xùn)練函數(shù)跟本章中前幾節(jié)的不同在于使用了Adam優(yōu)化算法。相對之前使用的小批量隨機梯度下降愧怜,它對學(xué)習(xí)率相對不那么敏感呀页。我們將在之后的“優(yōu)化算法”一章里詳細介紹它。

def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    train_ls, test_ls = [], []
    dataset = torch.utils.data.TensorDataset(train_features, train_labels)
    train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)
    # 這里使用了Adam優(yōu)化算法
    optimizer = torch.optim.Adam(params=net.parameters(), lr=learning_rate, weight_decay=weight_decay) 
    net = net.float()
    for epoch in range(num_epochs):
        for X, y in train_iter:
            l = loss(net(X.float()), y.float())
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
        train_ls.append(log_rmse(net, train_features, train_labels))
        if test_labels is not None:
            test_ls.append(log_rmse(net, test_features, test_labels))
    return train_ls, test_ls

K折交叉驗證

我們在模型選擇拥坛、欠擬合和過擬合中介紹了K折交叉驗證蓬蝶。它將被用來選擇模型設(shè)計并調(diào)節(jié)超參數(shù)。下面實現(xiàn)了一個函數(shù)猜惋,它返回第i折交叉驗證時所需要的訓(xùn)練和驗證數(shù)據(jù)丸氛。

def get_k_fold_data(k, i, X, y):
    # 返回第i折交叉驗證時所需要的訓(xùn)練和驗證數(shù)據(jù)
    assert k > 1
    fold_size = X.shape[0] // k
    X_train, y_train = None, None
    for j in range(k):
        idx = slice(j * fold_size, (j + 1) * fold_size)
        X_part, y_part = X[idx, :], y[idx]
        if j == i:
            X_valid, y_valid = X_part, y_part
        elif X_train is None:
            X_train, y_train = X_part, y_part
        else:
            X_train = torch.cat((X_train, X_part), dim=0)
            y_train = torch.cat((y_train, y_part), dim=0)
    return X_train, y_train, X_valid, y_valid

K折交叉驗證中我們訓(xùn)練K次并返回訓(xùn)練和驗證的平均誤差

def k_fold(k, X_train, y_train, num_epochs,
           learning_rate, weight_decay, batch_size):
    train_l_sum, valid_l_sum = 0, 0
    for i in range(k):
        data = get_k_fold_data(k, i, X_train, y_train)
        net = get_net(X_train.shape[1])
        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,
                                   weight_decay, batch_size)
        train_l_sum += train_ls[-1]
        valid_l_sum += valid_ls[-1]
        if i == 0:
            d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'rmse',
                         range(1, num_epochs + 1), valid_ls,
                         ['train', 'valid'])
        print('fold %d, train rmse %f, valid rmse %f' % (i, train_ls[-1], valid_ls[-1]))
    return train_l_sum / k, valid_l_sum / k

模型選擇

我們使用一組未經(jīng)調(diào)優(yōu)的超參數(shù)并計算交叉驗證誤差≈ぃ可以改動這些超參數(shù)來盡可能減小平均測試誤差缓窜。
有時候你會發(fā)現(xiàn)一組參數(shù)的訓(xùn)練誤差可以達到很低,但是在K折交叉驗證上的誤差可能反而較高谍咆。這種現(xiàn)象很可能是由過擬合造成的禾锤。因此,當訓(xùn)練誤差降低時卧波,我們要觀察K折交叉驗證上的誤差是否也相應(yīng)降低时肿。

k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 0, 64
train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, lr, weight_decay, batch_size)
print('%d-fold validation: avg train rmse %f, avg valid rmse %f' % (k, train_l, valid_l))
fold 0, train rmse 0.240368, valid rmse 0.220648
fold 1, train rmse 0.229009, valid rmse 0.267201
fold 2, train rmse 0.231903, valid rmse 0.238630
fold 3, train rmse 0.237220, valid rmse 0.218505
fold 4, train rmse 0.231733, valid rmse 0.259022
5-fold validation: avg train rmse 0.234047, avg valid rmse 0.240801

<img src="https://cdn.kesci.com/rt_upload/989B1E6DC2F046A6899429AC97B29EC0/q5w039prxr.svg">

預(yù)測并在Kaggle中提交結(jié)果

下面定義預(yù)測函數(shù)。在預(yù)測之前港粱,我們會使用完整的訓(xùn)練數(shù)據(jù)集來重新訓(xùn)練模型螃成,并將預(yù)測結(jié)果存成提交所需要的格式。

def train_and_pred(train_features, test_features, train_labels, test_data,
                   num_epochs, lr, weight_decay, batch_size):
    net = get_net(train_features.shape[1])
    train_ls, _ = train(net, train_features, train_labels, None, None,
                        num_epochs, lr, weight_decay, batch_size)
    d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'rmse')
    print('train rmse %f' % train_ls[-1])
    preds = net(test_features).detach().numpy()
    test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])
    submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)
    submission.to_csv('./submission.csv', index=False)
    # sample_submission_data = pd.read_csv("../input/house-prices-advanced-regression-techniques/sample_submission.csv")

設(shè)計好模型并調(diào)好超參數(shù)之后查坪,下一步就是對測試數(shù)據(jù)集上的房屋樣本做價格預(yù)測寸宏。如果我們得到與交叉驗證時差不多的訓(xùn)練誤差,那么這個結(jié)果很可能是理想的偿曙,可以在Kaggle上提交結(jié)果氮凝。

train_and_pred(train_features, test_features, train_labels, test_data, num_epochs, lr, weight_decay, batch_size)
train rmse 0.229939

<!- image uploading -->

希望大家自己動手完成房價預(yù)測的實現(xiàn),多參與討論


GRU

RNN存在的問題:梯度較容易出現(xiàn)衰減或爆炸(BPTT)
?控循環(huán)神經(jīng)?絡(luò):捕捉時間序列中時間步距離較?的依賴關(guān)系
RNN:

Image Name

H_{t} = ?(X_{t}W_{xh} + H_{t-1}W_{hh} + b_{h})
GRU:

Image Name

R_{t} = σ(X_tW_{xr} + H_{t?1}W_{hr} + b_r)\\ Z_{t} = σ(X_tW_{xz} + H_{t?1}W_{hz} + b_z)\\ \widetilde{H}_t = tanh(X_tW_{xh} + (R_t ⊙H_{t?1})W_{hh} + b_h)\\ H_t = Z_t⊙H_{t?1} + (1?Z_t)⊙\widetilde{H}_t
? 重置?有助于捕捉時間序列?短期的依賴關(guān)系望忆;
? 更新?有助于捕捉時間序列??期的依賴關(guān)系罩阵。

載入數(shù)據(jù)集

import os
os.listdir('/home/kesci/input')
['d2lzh1981',
 'houseprices2807',
 'FashionMNIST2065',
 'jaychou_lyrics4703',
 'd2l_jay9460']
import numpy as np
import torch
from torch import nn, optim
import torch.nn.functional as F


import sys
sys.path.append("../input/")
import d2l_jay9460 as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

(corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()

初始化參數(shù)

num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)

def get_params():  
    def _one(shape):
        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32) #正態(tài)分布
        return torch.nn.Parameter(ts, requires_grad=True)
    def _three():
        return (_one((num_inputs, num_hiddens)),
                _one((num_hiddens, num_hiddens)),
                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
     
    W_xz, W_hz, b_z = _three()  # 更新門參數(shù)
    W_xr, W_hr, b_r = _three()  # 重置門參數(shù)
    W_xh, W_hh, b_h = _three()  # 候選隱藏狀態(tài)參數(shù)
    
    # 輸出層參數(shù)
    W_hq = _one((num_hiddens, num_outputs))
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
    return nn.ParameterList([W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q])

def init_gru_state(batch_size, num_hiddens, device):   #隱藏狀態(tài)初始化
    return (torch.zeros((batch_size, num_hiddens), device=device), )
will use cpu

GRU模型

def gru(inputs, state, params):
    W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
    H, = state
    outputs = []
    for X in inputs:
        Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
        R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
        H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)
        H = Z * H + (1 - Z) * H_tilda
        Y = torch.matmul(H, W_hq) + b_q
        outputs.append(Y)
    return outputs, (H,)

訓(xùn)練模型

num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']
d2l.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,
                          vocab_size, device, corpus_indices, idx_to_char,
                          char_to_idx, False, num_epochs, num_steps, lr,
                          clipping_theta, batch_size, pred_period, pred_len,
                          prefixes)

簡潔實現(xiàn)

num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率
gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)

LSTM

** 長短期記憶long short-term memory **:
遺忘門:控制上一時間步的記憶細胞
輸入門:控制當前時間步的輸入
輸出門:控制從記憶細胞到隱藏狀態(tài)
記憶細胞:?種特殊的隱藏狀態(tài)的信息的流動

Image Name

I_t = σ(X_tW_{xi} + H_{t?1}W_{hi} + b_i) \\ F_t = σ(X_tW_{xf} + H_{t?1}W_{hf} + b_f)\\ O_t = σ(X_tW_{xo} + H_{t?1}W_{ho} + b_o)\\ \widetilde{C}_t = tanh(X_tW_{xc} + H_{t?1}W_{hc} + b_c)\\ C_t = F_t ⊙C_{t?1} + I_t ⊙\widetilde{C}_t\\ H_t = O_t⊙tanh(C_t)

初始化參數(shù)

num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)

def get_params():
    def _one(shape):
        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)
        return torch.nn.Parameter(ts, requires_grad=True)
    def _three():
        return (_one((num_inputs, num_hiddens)),
                _one((num_hiddens, num_hiddens)),
                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
    
    W_xi, W_hi, b_i = _three()  # 輸入門參數(shù)
    W_xf, W_hf, b_f = _three()  # 遺忘門參數(shù)
    W_xo, W_ho, b_o = _three()  # 輸出門參數(shù)
    W_xc, W_hc, b_c = _three()  # 候選記憶細胞參數(shù)
    
    # 輸出層參數(shù)
    W_hq = _one((num_hiddens, num_outputs))
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
    return nn.ParameterList([W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q])

def init_lstm_state(batch_size, num_hiddens, device):
    return (torch.zeros((batch_size, num_hiddens), device=device), 
            torch.zeros((batch_size, num_hiddens), device=device))

LSTM模型

def lstm(inputs, state, params):
    [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
    (H, C) = state
    outputs = []
    for X in inputs:
        I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
        F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
        O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
        C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
        C = F * C + I * C_tilda
        H = O * C.tanh()
        Y = torch.matmul(H, W_hq) + b_q
        outputs.append(Y)
    return outputs, (H, C)

訓(xùn)練模型

num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

d2l.train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens,
                          vocab_size, device, corpus_indices, idx_to_char,
                          char_to_idx, False, num_epochs, num_steps, lr,
                          clipping_theta, batch_size, pred_period, pred_len,
                          prefixes)

簡潔實現(xiàn)

num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率
lstm_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens)
model = d2l.RNNModel(lstm_layer, vocab_size)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)

深度循環(huán)神經(jīng)網(wǎng)絡(luò)

Image Name

\boldsymbol{H}_t^{(1)} = \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(1)} + \boldsymbol{H}_{t-1}^{(1)} \boldsymbol{W}_{hh}^{(1)} + \boldsymbol竿秆_h^{(1)})\\ \boldsymbol{H}_t^{(\ell)} = \phi(\boldsymbol{H}_t^{(\ell-1)} \boldsymbol{W}_{xh}^{(\ell)} + \boldsymbol{H}_{t-1}^{(\ell)} \boldsymbol{W}_{hh}^{(\ell)} + \boldsymbol_h^{(\ell)})\\ \boldsymbol{O}_t = \boldsymbol{H}_t^{(L)} \boldsymbol{W}_{hq} + \boldsymbol稿壁_q



num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率

gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=2)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)
gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=6)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)

雙向循環(huán)神經(jīng)網(wǎng)絡(luò)

Image Name

\begin{aligned} \overrightarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(f)} + \overrightarrow{\boldsymbol{H}}_{t-1} \boldsymbol{W}_{hh}^{(f)} + \boldsymbol幽钢_h^{(f)})\\ \overleftarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(b)} + \overleftarrow{\boldsymbol{H}}_{t+1} \boldsymbol{W}_{hh}^{(b)} + \boldsymbol_h^{(b)}) \end{aligned}
\boldsymbol{H}_t=(\overrightarrow{\boldsymbol{H}}_{t}, \overleftarrow{\boldsymbol{H}}_t)
\boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol傅是_q

num_hiddens=128
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e-2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率

gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens,bidirectional=True)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匪燕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子喧笔,更是在濱河造成了極大的恐慌帽驯,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件书闸,死亡現(xiàn)場離奇詭異尼变,居然都是意外死亡,警方通過查閱死者的電腦和手機梗劫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門享甸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梳侨,你說我怎么就攤上這事蛉威。” “怎么了走哺?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵蚯嫌,是天一觀的道長。 經(jīng)常有香客問我丙躏,道長择示,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任晒旅,我火速辦了婚禮栅盲,結(jié)果婚禮上芋类,老公的妹妹穿的比我還像新娘晰赞。我一直安慰自己,他們只是感情好或杠,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布鱼鼓。 她就那樣靜靜地躺著拟烫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迄本。 梳的紋絲不亂的頭發(fā)上硕淑,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機與錄音,去河邊找鬼置媳。 笑死于樟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拇囊。 我是一名探鬼主播隔披,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寂拆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抓韩,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤纠永,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谒拴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尝江,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年英上,在試婚紗的時候發(fā)現(xiàn)自己被綠了炭序。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡苍日,死狀恐怖惭聂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情相恃,我是刑警寧澤辜纲,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站拦耐,受9級特大地震影響耕腾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杀糯,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一扫俺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧固翰,春花似錦狼纬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至方援,卻和暖如春没炒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工送火, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拳话,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓种吸,卻偏偏與公主長得像弃衍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坚俗,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容