<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 5.10 批量歸一化 本節我們介紹批量歸一化(batch normalization)層,它能讓較深的神經網絡的訓練變得更加容易 [1]。在3.16節(實戰Kaggle比賽:預測房價)里,我們對輸入數據做了標準化處理:處理后的任意一個特征在數據集中所有樣本上的均值為0、標準差為1。標準化處理輸入數據使各個特征的分布相近:這往往更容易訓練出有效的模型。 通常來說,數據標準化預處理對于淺層模型就足夠有效了。隨著模型訓練的進行,當每層中參數更新時,靠近輸出層的輸出較難出現劇烈變化。但對深層神經網絡來說,即使輸入數據已做標準化,訓練中模型參數的更新依然很容易造成靠近輸出層輸出的劇烈變化。這種計算數值的不穩定性通常令我們難以訓練出有效的深度模型。 批量歸一化的提出正是為了應對深度模型訓練的挑戰。在模型訓練時,批量歸一化利用小批量上的均值和標準差,不斷調整神經網絡中間輸出,從而使整個神經網絡在各層的中間輸出的數值更穩定。**批量歸一化和下一節將要介紹的殘差網絡為訓練和設計深度模型提供了兩類重要思路**。 ## 5.10.1 批量歸一化層 對全連接層和卷積層做批量歸一化的方法稍有不同。下面我們將分別介紹這兩種情況下的批量歸一化。 ### 5.10.1.1 對全連接層做批量歸一化 我們先考慮如何對全連接層做批量歸一化。通常,我們將批量歸一化層置于全連接層中的仿射變換和激活函數之間。設全連接層的輸入為`$ \boldsymbol{u} $`,權重參數和偏差參數分別為`$ \boldsymbol{W} $`和`$ \boldsymbol{b} $`,激活函數為 `$ \phi $`。設批量歸一化的運算符為`$ \text{BN} $`。那么,使用批量歸一化的全連接層的輸出為 ```[tex] \phi(\text{BN}(\boldsymbol{x})), ``` 其中批量歸一化輸入`$ \boldsymbol{x} $`由仿射變換 ```[tex] \boldsymbol{x} = \boldsymbol{W\boldsymbol{u} + \boldsymbol{b}} ``` 得到。考慮一個由`$ m $`個樣本組成的小批量,仿射變換的輸出為一個新的小批量`$ \mathcal{B} = \{\boldsymbol{x}^{(1)}, \ldots, \boldsymbol{x}^{(m)} \} $`。它們正是批量歸一化層的輸入。對于小批量`$ \mathcal{B} $`中任意樣本`$ \boldsymbol{x}^{(i)} \in \mathbb{R}^d, 1 \leq i \leq m $`,批量歸一化層的輸出同樣是`$ d $`維向量 ```[tex] \boldsymbol{y}^{(i)} = \text{BN}(\boldsymbol{x}^{(i)}), ``` 并由以下幾步求得。首先,對小批量`$ \mathcal{B} $`求均值和方差: ```[tex] \boldsymbol{\mu}_\mathcal{B} \leftarrow \frac{1}{m}\sum_{i = 1}^{m} \boldsymbol{x}^{(i)}, ``` ```[tex] \boldsymbol{\sigma}_\mathcal{B}^2 \leftarrow \frac{1}{m} \sum_{i=1}^{m}(\boldsymbol{x}^{(i)} - \boldsymbol{\mu}_\mathcal{B})^2, ``` 其中的平方計算是按元素求平方。接下來,使用按元素開方和按元素除法對`$ \boldsymbol{x}^{(i)} $`標準化: ```[tex] \hat{\boldsymbol{x}}^{(i)} \leftarrow \frac{\boldsymbol{x}^{(i)} - \boldsymbol{\mu}_\mathcal{B}}{\sqrt{\boldsymbol{\sigma}_\mathcal{B}^2 + \epsilon}}, ``` 這里`$ \epsilon > 0 $` 是一個很小的常數,保證分母大于0。在上面標準化的基礎上,批量歸一化層引入了兩個可以學習的模型參數,拉伸(scale)參數 `$ \boldsymbol{\gamma} $` 和偏移(shift)參數 `$ \boldsymbol{\beta} $`。這兩個參數和`$ \boldsymbol{x}^{(i)} $`形狀相同,皆為`$ d $`維向量。它們與`$ \boldsymbol{x}^{(i)} $`分別做按元素乘法(符號`$ \odot $`)和加法計算: ```[tex] {\boldsymbol{y}}^{(i)} \leftarrow \boldsymbol{\gamma} \odot \hat{\boldsymbol{x}}^{(i)} + \boldsymbol{\beta}. ``` 至此,我們得到了`$ \boldsymbol{x}^{(i)} $`的批量歸一化的輸出`$ \boldsymbol{y}^{(i)} $`。 值得注意的是,可學習的拉伸和偏移參數保留了不對`$ \hat{\boldsymbol{x}}^{(i)} $`做批量歸一化的可能:此時只需學出`$ \boldsymbol{\gamma} = \sqrt{\boldsymbol{\sigma}_\mathcal{B}^2 + \epsilon} $`和`$ \boldsymbol{\beta} = \boldsymbol{\mu}_\mathcal{B} $`。我們可以對此這樣理解:如果批量歸一化無益,理論上,學出的模型可以不使用批量歸一化。 ### 5.10.1.2 對卷積層做批量歸一化 對卷積層來說,批量歸一化發生在卷積計算之后、應用激活函數之前。如果卷積計算輸出多個通道,我們需要對這些通道的輸出分別做批量歸一化,且**每個通道都擁有獨立的拉伸和偏移參數,并均為標量**。設小批量中有`$ m $`個樣本。在單個通道上,假設卷積計算輸出的高和寬分別為`$ p $`和`$ q $`。我們需要對該通道中`$ m \times p \times q $`個元素同時做批量歸一化。對這些元素做標準化計算時,我們使用相同的均值和方差,即該通道中`$ m \times p \times q $`個元素的均值和方差。 ### 5.10.1.3 預測時的批量歸一化 使用批量歸一化訓練時,我們可以將批量大小設得大一點,從而使批量內樣本的均值和方差的計算都較為準確。將訓練好的模型用于預測時,我們希望模型對于任意輸入都有確定的輸出。因此,單個樣本的輸出不應取決于批量歸一化所需要的隨機小批量中的均值和方差。一種常用的方法是通過移動平均估算整個訓練數據集的樣本均值和方差,并在預測時使用它們得到確定的輸出。可見,和丟棄層一樣,批量歸一化層在訓練模式和預測模式下的計算結果也是不一樣的。 ## 5.10.2 從零開始實現 下面我們自己實現批量歸一化層。 ``` python import time import torch from torch import nn, optim import torch.nn.functional as F import sys sys.path.append("..") import d2lzh_pytorch as d2l device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') def batch_norm(is_training, X, gamma, beta, moving_mean, moving_var, eps, momentum): # 判斷當前模式是訓練模式還是預測模式 if not is_training: # 如果是在預測模式下,直接使用傳入的移動平均所得的均值和方差 X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps) else: assert len(X.shape) in (2, 4) if len(X.shape) == 2: # 使用全連接層的情況,計算特征維上的均值和方差 mean = X.mean(dim=0) var = ((X - mean) ** 2).mean(dim=0) else: # 使用二維卷積層的情況,計算通道維上(axis=1)的均值和方差。這里我們需要保持 # X的形狀以便后面可以做廣播運算 mean = X.mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True) var = ((X - mean) ** 2).mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True) # 訓練模式下用當前的均值和方差做標準化 X_hat = (X - mean) / torch.sqrt(var + eps) # 更新移動平均的均值和方差 moving_mean = momentum * moving_mean + (1.0 - momentum) * mean moving_var = momentum * moving_var + (1.0 - momentum) * var Y = gamma * X_hat + beta # 拉伸和偏移 return Y, moving_mean, moving_var ``` 接下來,我們自定義一個`BatchNorm`層。它保存參與求梯度和迭代的拉伸參數`gamma`和偏移參數`beta`,同時也維護移動平均得到的均值和方差,以便能夠在模型預測時被使用。`BatchNorm`實例所需指定的`num_features`參數對于全連接層來說應為輸出個數,對于卷積層來說則為輸出通道數。該實例所需指定的`num_dims`參數對于全連接層和卷積層來說分別為2和4。 ``` python class BatchNorm(nn.Module): def __init__(self, num_features, num_dims): super(BatchNorm, self).__init__() if num_dims == 2: shape = (1, num_features) else: shape = (1, num_features, 1, 1) # 參與求梯度和迭代的拉伸和偏移參數,分別初始化成0和1 self.gamma = nn.Parameter(torch.ones(shape)) self.beta = nn.Parameter(torch.zeros(shape)) # 不參與求梯度和迭代的變量,全在內存上初始化成0 self.moving_mean = torch.zeros(shape) self.moving_var = torch.zeros(shape) def forward(self, X): # 如果X不在內存上,將moving_mean和moving_var復制到X所在顯存上 if self.moving_mean.device != X.device: self.moving_mean = self.moving_mean.to(X.device) self.moving_var = self.moving_var.to(X.device) # 保存更新過的moving_mean和moving_var, Module實例的traning屬性默認為true, 調用.eval()后設成false Y, self.moving_mean, self.moving_var = batch_norm(self.training, X, self.gamma, self.beta, self.moving_mean, self.moving_var, eps=1e-5, momentum=0.9) return Y ``` ### 5.10.2.1 使用批量歸一化層的LeNet 下面我們修改5.5節(卷積神經網絡(LeNet))介紹的LeNet模型,從而應用批量歸一化層。我們在所有的卷積層或全連接層之后、激活層之前加入批量歸一化層。 ``` python net = nn.Sequential( nn.Conv2d(1, 6, 5), # in_channels, out_channels, kernel_size BatchNorm(6, num_dims=4), nn.Sigmoid(), nn.MaxPool2d(2, 2), # kernel_size, stride nn.Conv2d(6, 16, 5), BatchNorm(16, num_dims=4), nn.Sigmoid(), nn.MaxPool2d(2, 2), d2l.FlattenLayer(), nn.Linear(16*4*4, 120), BatchNorm(120, num_dims=2), nn.Sigmoid(), nn.Linear(120, 84), BatchNorm(84, num_dims=2), nn.Sigmoid(), nn.Linear(84, 10) ) ``` 下面我們訓練修改后的模型。 ``` python batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) lr, num_epochs = 0.001, 5 optimizer = torch.optim.Adam(net.parameters(), lr=lr) d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs) ``` 輸出: ``` training on cuda epoch 1, loss 0.0039, train acc 0.790, test acc 0.835, time 2.9 sec epoch 2, loss 0.0018, train acc 0.866, test acc 0.821, time 3.2 sec epoch 3, loss 0.0014, train acc 0.879, test acc 0.857, time 2.6 sec epoch 4, loss 0.0013, train acc 0.886, test acc 0.820, time 2.7 sec epoch 5, loss 0.0012, train acc 0.891, test acc 0.859, time 2.8 sec ``` 最后我們查看第一個批量歸一化層學習到的拉伸參數`gamma`和偏移參數`beta`。 ``` python net[1].gamma.view((-1,)), net[1].beta.view((-1,)) ``` 輸出: ``` (tensor([ 1.2537, 1.2284, 1.0100, 1.0171, 0.9809, 1.1870], device='cuda:0'), tensor([ 0.0962, 0.3299, -0.5506, 0.1522, -0.1556, 0.2240], device='cuda:0')) ``` ## 5.10.3 簡潔實現 與我們剛剛自己定義的`BatchNorm`類相比,Pytorch中`nn`模塊定義的`BatchNorm1d`和`BatchNorm2d`類使用起來更加簡單,二者分別用于全連接層和卷積層,都需要指定輸入的`num_features`參數值。下面我們用PyTorch實現使用批量歸一化的LeNet。 ``` python net = nn.Sequential( nn.Conv2d(1, 6, 5), # in_channels, out_channels, kernel_size nn.BatchNorm2d(6), nn.Sigmoid(), nn.MaxPool2d(2, 2), # kernel_size, stride nn.Conv2d(6, 16, 5), nn.BatchNorm2d(16), nn.Sigmoid(), nn.MaxPool2d(2, 2), d2l.FlattenLayer(), nn.Linear(16*4*4, 120), nn.BatchNorm1d(120), nn.Sigmoid(), nn.Linear(120, 84), nn.BatchNorm1d(84), nn.Sigmoid(), nn.Linear(84, 10) ) ``` 使用同樣的超參數進行訓練。 ``` python batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) lr, num_epochs = 0.001, 5 optimizer = torch.optim.Adam(net.parameters(), lr=lr) d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs) ``` 輸出: ``` training on cuda epoch 1, loss 0.0054, train acc 0.767, test acc 0.795, time 2.0 sec epoch 2, loss 0.0024, train acc 0.851, test acc 0.748, time 2.0 sec epoch 3, loss 0.0017, train acc 0.872, test acc 0.814, time 2.2 sec epoch 4, loss 0.0014, train acc 0.883, test acc 0.818, time 2.1 sec epoch 5, loss 0.0013, train acc 0.889, test acc 0.734, time 1.8 sec ``` ## 小結 * 在模型訓練時,批量歸一化利用小批量上的均值和標準差,不斷調整神經網絡的中間輸出,從而使整個神經網絡在各層的中間輸出的數值更穩定。 * 對全連接層和卷積層做批量歸一化的方法稍有不同。 * 批量歸一化層和丟棄層一樣,在訓練模式和預測模式的計算結果是不一樣的。 * PyTorch提供了BatchNorm類方便使用。 ## 參考文獻 [1] Ioffe, S., & Szegedy, C. (2015). Batch normalization: Accelerating deep network training by reducing internal covariate shift. arXiv preprint arXiv:1502.03167. ----------- > 注:除代碼外本節與原書此節基本相同,[原書傳送門](https://zh.d2l.ai/chapter_convolutional-neural-networks/batch-norm.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看