Issues with BatchNorm that LayerNorm Addresses

LayerNorm addresses some short-comings from BatchNorm, which are:

  1. RNNs or any language-based tasks might have different sequences/lengths of data \rightarrow We can't simply calculate the statistics across our batches, we also need to consider the time steps.
  2. As BatchNorm calculates statistics across the batch for each feature, the batch sizes need to be relatively large.
  3. Lastly, we can't apply BatchNorm to online learning (batchsize=1) so as a result we need to cache/store some sort of statistic for inference.

Layer Normalization

alt text

Implementation Details

class LayerNorm(nn.Module):
    def __init__(self, eps=1e-5):
        super().__init__()
        self.eps = eps

    def forward(self, x):
        batch_size, length, feature_dim = x.shape
        output = torch.zeros_like(x)

        for i in range(batch_size):
            for t in range(length):
                word_vector = x[i, t, :]

                mu = word_vector.sum() / feature_dim

                var = ((word_vector - mu) ** 2).sum() / feature_dim

                output[i, t, :] = (word_vector - mu) / torch.sqrt(var + self.eps)

        return output
# Assume feature vecs from a batch 32, sentence of length 64, and feature dim. of 512.
# We want to normalize across each word in a batch.
# randn() -> between 0 and 1 so now mean = 10 and var = 5
sample_batch = torch.randn(32, 64, 512) * 5 + 10

print(sample_batch[0, 0, :])

ln_layer = LayerNorm()
out = ln_layer.forward(sample_batch)

print(f"dim(out): {out.shape}") # >>> (32, 64, 512)
print(out[0, 0, :])

alt text

class LayerNorm2(nn.Module):
    def __init__(self, eps=1e-5):
        super().__init__()
        self.eps = eps

    def forward(self, x):
        batch_size, length, feature_dim = x.shape
        output = torch.zeros_like(x)

        # dim=-1 so we squish along last idx(feature_dim)
        # keep_dim=True so we get (batch_size, length, 1)
        mu = x.mean(dim=-1, keep_dim=True)
        var = x.var(dim=-1, keep_dim=True)

        output = (x - mu) / torch.sqrt(var + self.eps)

        return output