正規化(normalization)是一種來自統計學的資料轉換技術,用來調整資料的平均值與變異數,使其更穩定、可預測。在 deep learning 中,normalization 被廣泛應用於提升模型訓練的穩定性與效率。本文將說明 normalization 的原始概念,介紹 Batch Normalization 的設計與限制,並深入探討 Layer Normalization 如何解決這些限制,成為現代 language models 中的標準做法。
Table of Contents
正規化(Normalization)
Normalization 是一種來自統計與資料前處理的基本方法,其目的是重新調整資料的分佈,使其在數值尺度上更加一致、可預期。也就是說,它就是在不改變資料相對關係的前提下,調整其中心與尺度,使得資料更易於後續的建模或計算處理。在實務應用中,normalization 可以幫助模型更容易學習,也讓不同來源或尺度的特徵能夠比較與整合。
我們將介紹最常見的兩種 normalization。
標準化(Z-score Normalization)
Z-score Normalization 將資料轉換為平均值(mean )為 0、標準差(standard deviation
)為 1 的分佈。
這種方式讓模型在處理不同特徵時不會因為數值尺度不同而偏頗,有助於梯度下降等數值優化方法的穩定性。
最小最大正規化(Min-max Normalization)
Min-max Normalization 將資料縮放至某個區間(如 [0, 1])內。
常見於影像處理等需要將特徵壓縮在特定範圍內的應用中。
Deep Learning 與 Normalization
在 deep neural networks 中,資料會經過多層 non-linear 轉換,每一層都可能改變資料的分佈。而這種分佈的不斷改變(稱為 Internal Covariate Shift)會使得模型訓練變得困難,特別是當輸入資料的分佈在訓練過程中不斷漂移時,模型每一層都需要不停調整。
Normalization 的引入,正是為了降低這樣的問題。它可以穩定訊號的分佈,避免過大或過小的值干擾梯度。它減少對 weights 初始化與 learning rate 設定的敏感性。因此,normalization 可以加速收斂,甚至改善泛化能力。
批次正規化(Batch Normalization)
2015 年,Sergey Ioffe et al. 提出 Batch Normalization。它的核心想法是,對每一層的輸出,以 mini-batch 為單位,在特徵維度上進行標準化,使每個通道的輸出的 mean 為 0 且 standard deviation 為 1。
對一個 neuron 的輸出 ,其公式為:
其中 與
是可訓練參數,用來恢復表達能力。Batch Normalization 可大幅改善訓練穩定性、加速收斂,也讓更深的網路變得可行。
但 Batch Normalization 也有其侷限性。它依賴 batch size,因為小 batch 可能導致統計量不穩,且不適用於 RNNs。例外,訓練與推論行為不一致,需要額外處理 moving average。
以下是 Batch Normalization 的實作。
import torch import torch.nn as nn class BatchNorm(nn.Module): def __init__(self, num_features, eps=1e-5, momentum=0.1): super().__init__() self.eps = eps self.momentum = momentum self.gamma = nn.Parameter(torch.ones(num_features)) self.beta = nn.Parameter(torch.zeros(num_features)) self.register_buffer('running_mean', torch.zeros(num_features)) self.register_buffer('running_var', torch.ones(num_features)) def forward(self, x): if self.training: mean = x.mean(dim=0) var = x.var(dim=0, unbiased=False) self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * mean.detach() self.running_var = (1 - self.momentum) * self.running_var + self.momentum * var.detach() x_hat = (x - mean[None, :]) / torch.sqrt(var[None, :] + self.eps) else: x_hat = (x - self.running_mean[None, :]) / torch.sqrt(self.running_var[None, :] + self.eps) return self.gamma[None, :] * x_hat + self.beta[None, :] if __name__ == "__main__": torch.manual_seed(42) batch_size = 6 feature_dim = 8 x = torch.randn(batch_size, feature_dim) bn = BatchNorm(feature_dim) print("=== Training Mode ===") bn.train() y_bn_train = bn(x) print("BatchNorm mean/std:", y_bn_train.mean().item(), y_bn_train.std().item()) print("\n=== Eval Mode ===") bn.eval() x_new = torch.randn(batch_size, feature_dim) y_bn_eval = bn(x_new) print("BatchNorm mean/std:", y_bn_eval.mean().item(), y_bn_eval.std().item())
層正規化(Layer Normalization)
2016 年,Jimmy Lei Ba et al. 提出 Layer Normalization,解決了 Batch Normalization 無法處理的場景,特別是在語言模型與 recurrent network 中的應用。它們之間的差異在於,Layer Normalization 是針對單一樣本內的所有 hidden units 做 normalization,而非跨樣本統計。其公式為:
由於每個樣本各自計算 mean 與 variance,因此 Layer Normalization 不依賴 batch,所以它的推論與訓練可以一致,並適用於 RNNs。
以下是 Layer Normalization 的實作。
import torch import torch.nn as nn class LayerNorm(nn.Module): def __init__(self, normalized_shape, eps=1e-5): super().__init__() if isinstance(normalized_shape, int): normalized_shape = (normalized_shape,) self.normalized_shape = normalized_shape self.eps = eps self.gamma = nn.Parameter(torch.ones(*normalized_shape)) self.beta = nn.Parameter(torch.zeros(*normalized_shape)) def forward(self, x): dims = [-i for i in range(1, len(self.normalized_shape) + 1)] mean = x.mean(dim=dims, keepdim=True) var = x.var(dim=dims, keepdim=True, unbiased=False) x_hat = (x - mean) / torch.sqrt(var + self.eps) return self.gamma * x_hat + self.beta if __name__ == "__main__": torch.manual_seed(42) batch_size = 6 feature_dim = 8 x = torch.randn(batch_size, feature_dim) ln = LayerNorm(feature_dim) print("=== Training Mode ===") ln.train() y_ln_train = ln(x) print("LayerNorm mean/std:", y_ln_train.mean().item(), y_ln_train.std().item()) print("\n=== Eval Mode ===") ln.eval() x_new = torch.randn(batch_size, feature_dim) y_ln_eval = ln(x_new) print("LayerNorm mean/std:", y_ln_eval.mean().item(), y_ln_eval.std().item())
結語
現今的 NLP 模型,如 Transformer、BERT、GPT 系列、T5、LLaMA 等,幾乎都將 Layer Normalization 作為標準元件。它常常被放在注意力(attention)機制與 feed-forward layer 之前或之後,與 residual connection 搭配使用。在這些模型中,Layer Normalization 有助於穩定長序列的學習,尤其在自注意力(self-attention)結構中,能有效維持每一層輸出訊號的穩定性。
參考
- Jimmy Lei Ba, Jamie Ryan Kiros, and Geoffrey E. Hinton. 2016. Layer Normalization. In NIPS 2016 Deep Learning Symposium.
- Sergey Ioffe and Christian Szegedy. 2015. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. In Proceedings of the 32nd International Conference on Machine Learning, ICML 2015.