天天看點

pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。

  • 多層感覺機,線性回歸和softmax回歸在内的單層神經網絡。然而深度學習主要關注多層模型。在本節中,我們将以多層感覺機(multilayer perceptron,MLP)為例,介紹多層神經網絡的概念。
  • 隐藏層
    • 多層感覺機在單層神經網絡的基礎上引入了一到多個隐藏層(hidden layer)。隐藏層位于輸入層和輸出層之間。
    • 假設我們設計一個多層感覺機中,輸入和輸出個數分别為4和3,中間的隐藏層中包含了5個隐藏單元(hidden unit)。由于輸入層不涉及計算,多層感覺機的層數設計為2。隐藏層中的神經元和輸入層中各個輸入完全連接配接(可以不用完全,設計簡單點就完全連接配接),輸出層中的神經元和隐藏層中的各個神經元也完全連接配接。是以,多層感覺機中的隐藏層和輸出層都是全連接配接層。
    • 具體來說,給定一個小批量樣本 X ∈ R n × d \boldsymbol{X} \in \mathbb{R}^{n \times d} X∈Rn×d,其批量大小為n,輸入個數為d。假設多層感覺機隻有一個隐藏層,其中隐藏單元個數為h。記隐藏層的輸出(也稱為隐藏層變量或隐藏變量)為 H \boldsymbol{H} H,有 H ∈ R n × h \boldsymbol{H} \in \mathbb{R}^{n \times h} H∈Rn×h。因為隐藏層和輸出層均是全連接配接層,可以設隐藏層的權重參數和偏差參數分别為 W h ∈ R d × h \boldsymbol{W}_h \in \mathbb{R}^{d \times h} Wh​∈Rd×h和 b h ∈ R 1 × h \boldsymbol{b}_h \in \mathbb{R}^{1 \times h} bh​∈R1×h,輸出層的權重和偏差參數分别為 W o ∈ R h × q \boldsymbol{W}_o \in \mathbb{R}^{h \times q} Wo​∈Rh×q和 b o ∈ R 1 × q \boldsymbol{b}_o \in \mathbb{R}^{1 \times q} bo​∈R1×q。
    • 先來看一種含單隐藏層的多層感覺機的設計。其輸出 O ∈ R n × q \boldsymbol{O} \in \mathbb{R}^{n \times q} O∈Rn×q的計算為
      • H = X W h + b h , O = H W o + b o , \begin{aligned} \boldsymbol{H} &= \boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h,\\ \boldsymbol{O} &= \boldsymbol{H} \boldsymbol{W}_o + \boldsymbol{b}_o, \end{aligned} HO​=XWh​+bh​,=HWo​+bo​,​
    • 也就是将隐藏層的輸出直接作為輸出層的輸入。如果将以上兩個式子聯立起來,可以得到
      • O = ( X W h + b h ) W o + b o = X W h W o + b h W o + b o . \boldsymbol{O} = (\boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h)\boldsymbol{W}_o + \boldsymbol{b}_o = \boldsymbol{X} \boldsymbol{W}_h\boldsymbol{W}_o + \boldsymbol{b}_h \boldsymbol{W}_o + \boldsymbol{b}_o. O=(XWh​+bh​)Wo​+bo​=XWh​Wo​+bh​Wo​+bo​.
    • 從聯立後的式子可以看出,雖然神經網絡引入了隐藏層,卻依然等價于一個單層神經網絡:其中輸出層權重參數為 W h W o \boldsymbol{W}_h\boldsymbol{W}_o Wh​Wo​,偏差參數為 b h W o + b o \boldsymbol{b}_h \boldsymbol{W}_o + \boldsymbol{b}_o bh​Wo​+bo​。不難發現,即便再添加更多的隐藏層,以上設計依然隻能與僅含輸出層的單層神經網絡等價。
  • 激活函數
    • 神經網絡模拟了人類神經元的工作機理,激活函數(Activation Function)是一種添加到人工神經網絡中的函數,旨在幫助網絡學習資料中的複雜模式。在神經元中,輸入的input經過一系列權重求和後作用于另一個函數,這個函數就是這裡的激活函數。類似于人類大腦中基于神經元的模型,激活函數最終決定了是否傳遞信号以及要發射給下一個神經元的内容。在人工神經網絡中,一個節點的激活函數定義了該節點在給定的輸入或輸入集合下的輸出。标準的計算機晶片電路可以看作是根據輸入得到開(1)或關(0)輸出的數字電路激活函數。
    • 激活函數可以分為線性激活函數(線性方程控制輸入到輸出的映射,如f(x)=x等)以及非線性激活函數(非線性方程控制輸入到輸出的映射,比如Sigmoid、Tanh、ReLU、LReLU、PReLU、Swish 等)
    • 因為神經網絡中每一層的輸入輸出都是一個線性求和的過程,下一層的輸出隻是承接了上一層輸入函數的線性變換,是以如果沒有激活函數,那麼無論你構造的神經網絡多麼複雜,有多少層,最後的輸出都是輸入的線性組合,純粹的線性組合并不能夠解決更為複雜的問題。而引入激活函數之後,我們會發現常見的激活函數都是非線性的,是以也會給神經元引入非線性元素,使得神經網絡可以逼近其他的任何非線性函數,這樣可以使得神經網絡應用到更多非線性模型中。
    • 一般來說,在神經元中,激活函數是很重要的一部分,為了增強網絡的表示能力和學習能力,神經網絡的激活函數都是非線性的,通常具有以下幾點性質:
      • 連續并可導(允許少數點上不可導),可導的激活函數可以直接利用數值優化的方法來學習網絡參數;
      • 激活函數及其導數要盡可能簡單一些,太複雜不利于提高網絡計算率;
      • 激活函數的導函數值域要在一個合适的區間内,不能太大也不能太小,否則會影響訓練的效率和穩定性。
    • 上述問題的根源在于全連接配接層隻是對資料做仿射變換(affine transformation),而多個仿射變換的疊加仍然是一個仿射變換。解決問題的一個方法是引入非線性變換,例如對隐藏變量使用按元素運算的非線性函數進行變換,然後再作為下一個全連接配接層的輸入。這個非線性函數被稱為激活函數(activation function)。下面我們介紹幾個常用的激活函數。
    • ReLU函數
      • ReLU(rectified linear unit)函數提供了一個很簡單的非線性變換。給定元素x,該函數定義為 ReLU ( x ) = max ⁡ ( x , 0 ) \text{ReLU}(x) = \max(x, 0) ReLU(x)=max(x,0).可以看出,ReLU函數隻保留正數元素,并将負數元素清零。
      • %matplotlib inline
        import torch
        import numpy as np
        import matplotlib.pylab as plt
        from IPython import display
        def use_svg_display():
            """Use svg format to display plot in jupyter"""
            display.display_svg()
        def set_figsize(figsize=(3.5, 2.5)):
            use_svg_display()
            # 設定圖的尺寸
            plt.rcParams['figure.figsize'] = figsize
        def xyplot(x_vals, y_vals, name):
            set_figsize(figsize=(5, 2.5))
            plt.plot(x_vals.detach().numpy(), y_vals.detach().numpy())
            plt.xlabel('x')
            plt.ylabel(name + '(x)')
        x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
        y = x.relu()
        xyplot(x, y, 'relu')
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。
      • 通過

        Tensor

        提供的

        relu

        函數來繪制ReLU函數。可以看到,該激活函數是一個兩段線性函數。顯然,當輸入為負數時,ReLU函數的導數為0;當輸入為正數時,ReLU函數的導數為1。盡管輸入為0時ReLU函數不可導,但是我們可以取此處的導數為0。下面繪制ReLU函數的導數。
      • y.sum().backward()
        xyplot(x, x.grad, 'grad of relu')
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。
      • ReLU 函數是深度學習中較為流行的一種激活函數,相比于 sigmoid 函數和 tanh 函數,它具有如下優點:
        • 當輸入為正時,導數為1,一定程度上改善了梯度消失問題,加速梯度下降的收斂速度;計算速度快得多。
        • ReLU 函數中隻存線上性關系,是以它的計算速度比 sigmoid 和 tanh 更快。
        • 被認為具有生物學合理性(Biological Plausibility),比如單側抑制、寬興奮邊界(即興奮程度可以非常高)
      • ReLU函數的不足:
        • Dead ReLU 問題。當輸入為負時,ReLU 完全失效,在正向傳播過程中,這不是問題。有些區域很敏感,有些則不敏感。但是在反向傳播過程中,如果輸入負數,則梯度将完全為零;
        • 不以零為中心:和 Sigmoid 激活函數類似,ReLU 函數的輸出不以零為中心,ReLU 函數的輸出為 0 或正數,給後一層的神經網絡引入偏置偏移,會影響梯度下降的效率。
      • 為了解決 ReLU 激活函數中的梯度消失問題,當 x < 0 時,我們使用 Leaky ReLU——該函數試圖修複 dead ReLU 問題。
    • sigmoid函數
      • sigmoid函數可以将元素的值變換到0和1之間: sigmoid ( x ) = 1 1 + exp ⁡ ( − x ) \text{sigmoid}(x) = \frac{1}{1 + \exp(-x)} sigmoid(x)=1+exp(−x)1​.sigmoid函數在早期的神經網絡中較為普遍,但它目前逐漸被更簡單的ReLU函數取代。在“循環神經網絡”會利用它值域在0到1之間這一特性來控制資訊在神經網絡中的流動。下面繪制了sigmoid函數。當輸入接近0時,sigmoid函數接近線性變換。
      • y = x.sigmoid()
        xyplot(x, y, 'sigmoid')
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。
      • 依據鍊式法則,sigmoid函數的導數 sigmoid ′ ( x ) = sigmoid ( x ) ( 1 − sigmoid ( x ) ) \text{sigmoid}'(x) = \text{sigmoid}(x)\left(1-\text{sigmoid}(x)\right) sigmoid′(x)=sigmoid(x)(1−sigmoid(x)).下面繪制了sigmoid函數的導數。當輸入為0時,sigmoid函數的導數達到最大值0.25;當輸入越偏離0時,sigmoid函數的導數越接近0。
      • x.grad.zero_()
        y.sum().backward()
        xyplot(x, x.grad, 'grad of sigmoid')
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。
      • Sigmoid 函數的輸出範圍是 0 到 1。由于輸出值限定在 0 到1,是以它對每個神經元的輸出進行了歸一化;用于将預測機率作為輸出的模型。由于機率的取值範圍是 0 到 1,是以 Sigmoid 函數非常合适;梯度平滑,避免「跳躍」的輸出值;函數是可微的。這意味着可以找到任意兩個點的 sigmoid 曲線的斜率;明确的預測,即非常接近 1 或 0。
      • Sigmoid 激活函數存在的不足:
        • 梯度消失:注意:Sigmoid 函數趨近 0 和 1 的時候變化率會變得平坦,也就是說,Sigmoid 的梯度趨近于 0。神經網絡使用 Sigmoid 激活函數進行反向傳播時,輸出接近 0 或 1 的神經元其梯度趨近于 0。這些神經元叫作飽和神經元。是以,這些神經元的權重不會更新。此外,與此類神經元相連的神經元的權重也更新得很慢。該問題叫作梯度消失。是以,想象一下,如果一個大型神經網絡包含 Sigmoid 神經元,而其中很多個都處于飽和狀态,那麼該網絡無法執行反向傳播。
        • 不以零為中心:Sigmoid 輸出不以零為中心的,,輸出恒大于0,非零中心化的輸出會使得其後一層的神經元的輸入發生偏置偏移(Bias Shift),并進一步使得梯度下降的收斂速度變慢。
        • 計算成本高昂:exp() 函數與其他非線性激活函數相比,計算成本高昂,計算機運作起來速度較慢。
    • tanh函數
      • tanh(雙曲正切)函數可以将元素的值變換到-1和1之間: tanh ( x ) = 1 − exp ⁡ ( − 2 x ) 1 + exp ⁡ ( − 2 x ) \text{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)} tanh(x)=1+exp(−2x)1−exp(−2x)​.接着繪制tanh函數。當輸入接近0時,tanh函數接近線性變換。雖然該函數的形狀和sigmoid函數的形狀很像,但tanh函數在坐标系的原點上對稱。
      • y = x.tanh()
        xyplot(x, y, 'tanh')
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。
      • 依據鍊式法則,tanh函數的導數 tanh ′ ( x ) = 1 − tanh 2 ( x ) \text{tanh}'(x) = 1 - \text{tanh}^2(x) tanh′(x)=1−tanh2(x).下面繪制了tanh函數的導數。當輸入為0時,tanh函數的導數達到最大值1;當輸入越偏離0時,tanh函數的導數越接近0。
      • x.grad.zero_()
        y.sum().backward()
        xyplot(x, x.grad, 'grad of tanh')
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。
      • 你可以将 Tanh 函數想象成兩個 Sigmoid 函數放在一起。在實踐中,Tanh 函數的使用優先性高于 Sigmoid 函數。負數輸入被當作負值,零輸入值的映射接近零,正數輸入被當作正值:當輸入較大或較小時,輸出幾乎是平滑的并且梯度較小,這不利于權重更新。二者的差別在于輸出間隔,tanh 的輸出間隔為 1,并且整個函數以 0 為中心,比 sigmoid 函數更好;在 tanh 圖中,負輸入将被強映射為負,而零輸入被映射為接近零。
      • tanh存在的不足:
        • 與sigmoid類似,Tanh 函數也會有梯度消失的問題,是以在飽和時(x很大或很小時)也會「殺死」梯度。
  • 多層感覺機
    • 多層感覺機就是含有至少一個隐藏層的由全連接配接層組成的神經網絡,且每個隐藏層的輸出通過激活函數進行變換。多層感覺機的層數和各隐藏層中隐藏單元個數都是超參數。以單隐藏層為例并沿用本節之前定義的符号,多層感覺機按以下方式計算輸出:
    • H = ϕ ( X W h + b h ) , O = H W o + b o , \begin{aligned} \boldsymbol{H} &= \phi(\boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h),\\ \boldsymbol{O} &= \boldsymbol{H} \boldsymbol{W}_o + \boldsymbol{b}_o, \end{aligned} HO​=ϕ(XWh​+bh​),=HWo​+bo​,​
    • 其中 ϕ \phi ϕ表示激活函數。在分類問題中,可以對輸出 O \boldsymbol{O} O做softmax運算,并使用softmax回歸中的交叉熵損失函數。在回歸問題中,将輸出層的輸出個數設為1,并将輸出 O \boldsymbol{O} O直接提供給線性回歸中使用的平方損失函數。
  • 多層感覺機的從零開始實作
    • 動手實作一個多層感覺機。首先導入實作所需的包或子產品。擷取和讀取資料,繼續使用Fashion-MNIST資料集。我們将使用多層感覺機對圖像進行分類。
    • import torch
      import numpy as np
      import torchvision
      import sys
      def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
          """Download the fashion mnist dataset and then load into memory."""
          trans = []
          if resize:
              trans.append(torchvision.transforms.Resize(size=resize))
          trans.append(torchvision.transforms.ToTensor())
      
          transform = torchvision.transforms.Compose(trans)
          mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
          mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
          if sys.platform.startswith('win'):
              num_workers = 0  # 0表示不用額外的程序來加速讀取資料
          else:
              num_workers = 4
          train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
          test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
          return train_iter, test_iter
      batch_size = 256
      train_iter, test_iter = load_data_fashion_mnist(batch_size)
      print(train_iter, test_iter)
                 
    • 定義模型參數
      • Fashion-MNIST資料集中圖像形狀為 28 × 28 28 \times 28 28×28,類别數為10。本節中依然使用長度為 28 × 28 = 784 28 \times 28 = 784 28×28=784的向量表示每一張圖像。是以,輸入個數為784,輸出個數為10。實驗中,設超參數隐藏單元個數為256。
      • num_inputs, num_outputs, num_hiddens = 784, 10, 256
        W1 = torch.tensor(np.random.normal(0, 0.01, (num_inputs, num_hiddens)), dtype=torch.float)
        b1 = torch.zeros(num_hiddens, dtype=torch.float)
        W2 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens, num_outputs)), dtype=torch.float)
        b2 = torch.zeros(num_outputs, dtype=torch.float)
        params = [W1, b1, W2, b2]
        for param in params:
            param.requires_grad_(requires_grad=True)
        print(W1.shape,b1.shape,W2.shape,b2.shape)
        print(W1.grad,b1.grad,W2.grad,b2.grad)
        print(W1.requires_grad,b1.requires_grad,W2.requires_grad,b2.requires_grad)
                   
      • torch.Size([784, 256]) torch.Size([256]) torch.Size([256, 10]) torch.Size([10])
        None None None None
        True True True True
                   
    • 定義激活函數
      • 這裡使用基礎的

        max

        函數來實作ReLU,而非直接調用

        relu

        函數。
      • def relu(X):
            return torch.max(input=X, other=torch.tensor(0.0))
        myx = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
        myy = myx.relu()
        xyplot(myx, myy, 'new_relu')
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。
    • 定義模型
      • 同softmax回歸一樣,通過

        view

        函數将每張原始圖像改成長度為

        num_inputs

        的向量。然後實作上文中多層感覺機的計算表達式。
      • def net(X):
            X = X.view((-1, num_inputs))
            H = relu(torch.matmul(X, W1) + b1)
            return torch.matmul(H, W2) + b2
                   
    • 定義損失函數:為了得到更好的數值穩定性,我們直接使用PyTorch提供的包括softmax運算和交叉熵損失計算的函數。
      • loss = torch.nn.CrossEntropyLoss()
        def cross_entropy(y_hat, y):
            return - torch.log(y_hat.gather(1, y.view(-1, 1)))
                   
    • 訓練模型
      • 訓練多層感覺機的步驟訓練softmax回歸的步驟沒什麼差別。直接調用``train_ch3`函數。在這裡設超參數疊代周期數為10,學習率為100.0。
      • def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,params=None, lr=None, optimizer=None):
            for epoch in range(num_epochs):
                train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
                for X, y in train_iter:
                    y_hat = net(X)
                    l = loss(y_hat, y).sum()
                    # 梯度清零
                    if optimizer is not None:
                        optimizer.zero_grad()
                    elif params is not None and params[0].grad is not None:
                        for param in params:
                            param.grad.data.zero_()
                    l.backward()
                    if optimizer is None:
                        sgd(params, lr, batch_size)
                    else:
                        optimizer.step()  # “softmax回歸的簡潔實作”一節将用到
                    train_l_sum += l.item()
                    train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
                    n += y.shape[0]
                test_acc = evaluate_accuracy(test_iter, net)
                print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
                      % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
        def sgd(params, lr, batch_size): 
            for param in params:
                param.data -= lr * param.grad/batch_size # 注意這裡更改param時用的param.data
        def evaluate_accuracy(data_iter, net):
            acc_sum, n = 0.0, 0
            for X, y in data_iter:
                acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
                n += y.shape[0]
            return acc_sum / n
        num_epochs, lr = 10, 100.0
        train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params, lr)
                   
      • epoch 1, loss 0.0030, train acc 0.710, test acc 0.780
        epoch 2, loss 0.0019, train acc 0.825, test acc 0.803
        epoch 3, loss 0.0017, train acc 0.843, test acc 0.827
        epoch 4, loss 0.0015, train acc 0.855, test acc 0.826
        epoch 5, loss 0.0014, train acc 0.865, test acc 0.769
        epoch 6, loss 0.0014, train acc 0.870, test acc 0.862
        epoch 7, loss 0.0013, train acc 0.875, test acc 0.777
        epoch 8, loss 0.0013, train acc 0.878, test acc 0.804
        epoch 9, loss 0.0012, train acc 0.882, test acc 0.856
        epoch 10, loss 0.0012, train acc 0.886, test acc 0.831
                   
    • 預測
      • from IPython import display
        from matplotlib import pyplot as plt
        def get_fashion_mnist_labels(labels):
            text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                           'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
            return [text_labels[int(i)] for i in labels]
        def use_svg_display():
            # 用矢量圖顯示
            display.display_svg()
        def show_fashion_mnist(images, labels):
            use_svg_display()
            # 這裡的_表示我們忽略(不使用)的變量
            _, figs = plt.subplots(1, len(images), figsize=(12, 12))
            for f, img, lbl in zip(figs, images, labels):
                f.imshow(img.view((28, 28)).numpy())
                f.set_title(lbl)
                f.axes.get_xaxis().set_visible(False)
                f.axes.get_yaxis().set_visible(False)
            plt.show()
        print(net)
        X, y = next(iter(test_iter))
        true_labels = get_fashion_mnist_labels(y.numpy())
        pred_labels = get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
        titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
        show_fashion_mnist(X[0:9], titles[0:9])
                   
      • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。

多層感覺機的pytorch簡潔實作

  • 使用PyTorch來實作上一節中的多層感覺機。
    • import torch
      from torch import nn
      from torch.nn import init
      import torchvision
      import numpy as np
      import sys
      def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
          """Download the fashion mnist dataset and then load into memory."""
          trans = []
          if resize:
              trans.append(torchvision.transforms.Resize(size=resize))
          trans.append(torchvision.transforms.ToTensor())
          transform = torchvision.transforms.Compose(trans)
          mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
          mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
          if sys.platform.startswith('win'):
              num_workers = 0  # 0表示不用額外的程序來加速讀取資料
          else:
              num_workers = 4
          train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
          test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
          return train_iter, test_iter
      def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,params=None, lr=None, optimizer=None):
          for epoch in range(num_epochs):
              train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
              for X, y in train_iter:
                  y_hat = net(X)
                  l = loss(y_hat, y).sum()
                  # 梯度清零
                  if optimizer is not None:
                      optimizer.zero_grad()
                  elif params is not None and params[0].grad is not None:
                      for param in params:
                          param.grad.data.zero_()
                  l.backward()
                  if optimizer is None:
                      sgd(params, lr, batch_size)
                  else:
                      optimizer.step()  # “softmax回歸的簡潔實作”一節将用到
                  train_l_sum += l.item()
                  train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
                  n += y.shape[0]
              test_acc = evaluate_accuracy(test_iter, net)
              print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
                    % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
      class FlattenLayer(nn.Module):
          def __init__(self):
              super(FlattenLayer, self).__init__()
          def forward(self, x): # x shape: (batch, *, *, ...)
              return x.view(x.shape[0], -1)
      def evaluate_accuracy(data_iter, net):
          acc_sum, n = 0.0, 0
          for X, y in data_iter:
              acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
              n += y.shape[0]
          return acc_sum / n
      batch_size = 256
      train_iter, test_iter = load_data_fashion_mnist(batch_size) # 資料擷取
      num_inputs, num_outputs, num_hiddens = 784, 10, 256   # 多層感覺機超參數設定
      net = nn.Sequential(              # 模型建構
              FlattenLayer(),
              nn.Linear(num_inputs, num_hiddens),
              nn.ReLU(),
              nn.Linear(num_hiddens, num_outputs), 
              )
      for params in net.parameters():            # 模型初始化
          init.normal_(params, mean=0, std=0.01)
      loss = torch.nn.CrossEntropyLoss()  # 損失函數
      optimizer = torch.optim.SGD(net.parameters(), lr=0.5) # 優化器
      num_epochs = 10
      train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)
                 
    • epoch 1, loss 0.0031, train acc 0.706, test acc 0.778
      epoch 2, loss 0.0019, train acc 0.822, test acc 0.830
      epoch 3, loss 0.0016, train acc 0.844, test acc 0.850
      epoch 4, loss 0.0015, train acc 0.855, test acc 0.832
      epoch 5, loss 0.0014, train acc 0.864, test acc 0.860
      epoch 6, loss 0.0013, train acc 0.873, test acc 0.817
      epoch 7, loss 0.0013, train acc 0.878, test acc 0.839
      epoch 8, loss 0.0013, train acc 0.881, test acc 0.853
      epoch 9, loss 0.0012, train acc 0.885, test acc 0.853
      epoch 10, loss 0.0012, train acc 0.888, test acc 0.852
                 
  • 模型預測
    • from IPython import display
      from matplotlib import pyplot as plt
      def get_fashion_mnist_labels(labels):
          text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                         'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
          return [text_labels[int(i)] for i in labels]
      def use_svg_display():
          # 用矢量圖顯示
          display.display_svg()
      def show_fashion_mnist(images, labels):
          use_svg_display()
          # 這裡的_表示我們忽略(不使用)的變量
          _, figs = plt.subplots(1, len(images), figsize=(12, 12))
          for f, img, lbl in zip(figs, images, labels):
              f.imshow(img.view((28, 28)).numpy())
              f.set_title(lbl)
              f.axes.get_xaxis().set_visible(False)
              f.axes.get_yaxis().set_visible(False)
          plt.show()
      print(net)
      X, y = next(iter(test_iter))
      true_labels = get_fashion_mnist_labels(y.numpy())
      pred_labels = get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
      titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
      show_fashion_mnist(X[0:9], titles[0:9])
                 
    • pytorch-多層感覺機,最簡單的深度學習模型,将非線性激活函數引入到模型中。

繼續閱讀