Skip to content

图解LayerNorm & BatchNorm

约 897 个字 119 行代码 30 张图片 预计阅读时间 6 分钟

image-20241125223117360

image-20241126091859388

image-20241127202336411

BatchNorm1D、NLP

b=2,n=3,d=4

  • 理解 3维张量、4维张量:

\(\begin{cases} 矩阵法:2个3×4的矩阵 \\ \\ 抽屉法:一直划分同一个方向,直到最后一个维度,画另一个方向 \end{cases}\)

都是横排和竖排的数字排列,不同维度的张量表示不同的分组

怎么理解nlp中的三维张量?

  • 矩阵法:

2个3×4的矩阵,图解:

image-20241127204153619

  • 抽屉法

(第一次真正开始理解多维张量是好朋友拿抽屉给我举得例子,四维张量就是抽屉里面还有抽屉)

要点:一直沿同一个方向划分,直到最后一维换方向

2×3×4

image-20241127205452248

:逻辑理顺了,感觉自己做这些东西蠢蠢的......但是,我记性不好,总忘......笨就笨吧,(凌妙妙口吻:师傅天天说人家笨,可人家本来就是笨)

  • 实际意义

对于nlp来说,bnd=234表示b个句子,每个句子n个词,每个词有d个维度

\(batch\_size × max\_sequence\_length × model\_dim\)

就是说 现在有 2个句子,每个句子3个单词,每个单词用4维向量表示

image-20241127214746841

这里的问题是:我们这个元素的个数都是对的,但是在计算机中存储中,并不是这么存的

  • 实际意义 & 矩阵法 & 计算机存储逻辑

image-20241127215739102

Python
batch_size = 2
times_steps = 3
embedding_dim = 4

inputx = torch.randn(batch_size,times_steps,embedding_dim) # N*L*C
print(inputx)

输出:

Python
tensor([[[ 0.8475, -0.3956, -0.5602,  1.4907],
         [-0.0746, -0.0021,  0.1291, -0.0343],
         [-0.3636,  1.8378,  0.1954, -1.0180]],

        [[-1.0256,  1.0202,  0.7321,  0.3294],
         [-0.6416,  0.5399,  0.8733,  1.7110],
         [-1.1292,  0.2056,  0.6884,  0.2267]]])
  • torch.randn & torch.rand

现在开始nlp&BN,图形结合,例子,数学例子,不要脱离实际意义

也就是说

输入:三维张量,也可以理解为二维数表,用括号分组,所以有了不同的意义

怎么计算BN1D?

image-20241127220937242

Python
bn_mean = inputx.mean(dim=(0,1),keepdim=True)
print(bn_mean)

输出:

Python
tensor([[[ 0.0695, -0.6811, -0.1232, -0.5339]]])

好嘟,这串代码是没有任何问题了

代码实现

库函数要的输入格式:bnd \(\rightarrow\) bdn

  • 说一下这边的转置 .transpose(-1,-2)

image-20241127221904313

官网api给了,输入bdn,输出bdn,所以输出以后再transpose,变成bnd

还有一点,不管是BN还是LN都是不改变形状的

Python
batch_size = 2
times_steps = 3
embedding_dim = 4

inputx = torch.randn(batch_size,times_steps,embedding_dim) # N*L*C
# print(inputx)
# 1. 实现batch_norm并验证API

## 调用 batch_norm API
batch_norm_op = torch.nn.BatchNorm1d(embedding_dim,affine=False)
bn_y = batch_norm_op(inputx.transpose(-1,-2)).transpose(-1,-2)

## 手写batch_norm
bn_mean = inputx.mean(dim=(0,1),keepdim=True)
# print(bn_mean)
bn_std = inputx.std(dim=(0,1),unbiased=False,keepdim=True)
verify_bn_y = (inputx - bn_mean)/(bn_std+1e-5)
print(bn_y)
print(verify_bn_y)
print(torch.allclose(bn_y,verify_bn_y))

输出:

Text Only
tensor([[[ 1.2288e+00,  2.1594e-01, -8.7470e-01,  1.3089e+00],
         [ 1.2093e+00,  5.8475e-01, -4.2434e-01, -4.7364e-01],
         [-1.0128e+00, -1.9468e+00, -1.2768e+00,  2.7564e-01]],

        [[-1.1732e+00,  3.5125e-01,  1.5184e+00, -1.3330e+00],
         [ 4.1865e-01, -4.3382e-01, -4.6577e-04,  1.1563e+00],
         [-6.7079e-01,  1.2287e+00,  1.0579e+00, -9.3418e-01]]])
tensor([[[ 1.2288e+00,  2.1594e-01, -8.7470e-01,  1.3089e+00],
         [ 1.2093e+00,  5.8474e-01, -4.2434e-01, -4.7363e-01],
         [-1.0128e+00, -1.9468e+00, -1.2768e+00,  2.7564e-01]],

        [[-1.1732e+00,  3.5125e-01,  1.5183e+00, -1.3330e+00],
         [ 4.1865e-01, -4.3382e-01, -4.6576e-04,  1.1563e+00],
         [-6.7079e-01,  1.2287e+00,  1.0579e+00, -9.3418e-01]]])
True

BN2D

图片的存储格式:bchw

计算BN

自己脑袋里想的:

image-20241127222814036

计算机认识的:

image-20241127223215122

加括号,就变成了张量

计算机与实际意义联系起来:优先竖排,最后一维横排

如图,4×3×2×2

image-20241128152225891

BN与CV

bchw \(\rightarrow\) 1c11

对 bhw个数求和 计算 均值和方差

image-20241128154208220

image-20241128195945646

step1

image-20241128154246886

step2

image-20241128154300164

step3

第三个通道的均值和⽅差:

image-20241128154343080

image-20241128154417113

代码

Python
import torch
import torch.nn as nn
# 模拟⼀个输⼊张量
b, c, h, w = 4, 3, 2, 2 # 例如,4个样本,每个样本有3个通道,每个通道的⼤⼩为2x2
# 定义⼀个批量归⼀化层
batch_norm = nn.BatchNorm2d(num_features=c) # c 是输⼊的通道数
input_tensor = torch.arange(48).reshape((4,3,2,2)).float()
print(input_tensor)
# 应⽤批量归⼀化
output_tensor = batch_norm(input_tensor)
print(output_tensor)
print(output_tensor.shape) # 输出的形状仍然是 [b, c, h, w]

均值 & 方差代码:

Python
# 计算每个通道的均值和⽅差
mean = input_tensor.mean(dim=(0, 2, 3))
var = input_tensor.var(dim=(0, 2, 3), unbiased=False)
print(f"均值: {mean}")
print(f"⽅差: {var}")

输出:

Python
均值: tensor([19.5000, 23.5000, 27.5000])
: tensor([181.2500, 181.2500, 181.2500])

image-20241128155225803

LN1D

文字描述、实际意义

  • LN是对每个词的所有特征进行归一化

类比到二维数表是对 横行样本行进行归一化

  • BN是对同一个特征的所有样本进行归一化

类比到二维数表 就是对列进行归一化

  • 我觉得理解LN就一句话牢牢记住:per sample、per layer!尤其是per sample

有几个样本、就有几个均值&方差、类似的

有几个词,就有均值 bnd → 1n1

有几张图片,就有几个均值 bchw→b111

来,在折腾一下,for BN,特征多少维,就是几个均值

bnd→11d

bchw→1c11

  • 二维数表的格式:

image-20241128160542155

图示

image-20241128160016777

对比:BN1D & LN1D

image-20241128160106582

数学例子

见:5种归一化方法

代码实现

见:5种归一化方法

LN2D

文字描述

per sample

数学例子

Python
import torch
import torch.nn as nn
b, c, h, w = 4, 3, 2, 2 # 例如,10个样本,每个样本有3个通道,每个通道的⼤⼩为32x32
# 定义⼀个层归⼀化层
layer_norm = nn.LayerNorm(normalized_shape=[c, h, w]) # c, h, w 分别是通道数、⾼度和宽度
# 模拟⼀个输⼊张量
input_tensor = torch.arange(48).reshape((4,3,2,2)).float()
# 应⽤层归⼀化
output_tensor = layer_norm(input_tensor)
# print(input_tensor)
print(output_tensor) # 输出的形状仍然是 [b, c, h, w]

image-20241128193622308

image-20241128193635259

image-20241128193710058

image-20241128193725526

图示

image-20241128194356421

对比:

image-20241128195114249

代码实现

Python
import torch

batch_size = 4
channels = 3
h,w = 2,2

inputx = torch.randn(batch_size,channels,h,w) # BCHW 只要维度是正确的,数字可以随便生成

# 1. 实现batch_norm并验证API

## 调用 batch_norm API
batch_norm_op = torch.nn.BatchNorm2d(channels,affine=False)
bn_y = batch_norm_op(inputx) # torch.Size([4, 3, 2, 2])

## 手写batch_norm
bn_mean = inputx.mean(dim=(0,2,3),keepdim=True) # torch.Size([1, 3, 1, 1])
bn_std = inputx.std(dim=(0,2,3),unbiased=False,keepdim=True) # torch.Size([1, 3, 1, 1])
verify_bn_y = (inputx - bn_mean)/(bn_std+1e-5) # torch.Size([4, 3, 2, 2])

# print(bn_mean.shape) 
# print(bn_std.shape) 
# print(bn_y.shape)   
# print(verify_bn_y.shape)    
# print(torch.allclose(bn_y,verify_bn_y))
'''
    torch.Size([1, 3, 1, 1])
    torch.Size([1, 3, 1, 1])
    torch.Size([4, 3, 2, 2])
    torch.Size([4, 3, 2, 2])
    True
'''
# 2. 实现layer_norm 并验证api

## 调用 layer_norm API
layer_norm_op = torch.nn.LayerNorm((channels,h,w),elementwise_affine=False)
ln_y = layer_norm_op(inputx)  # torch.Size([4, 3, 2, 2])

## 手写layer_norm
ln_mean = inputx.mean(dim=(1,2,3),keepdim=True)  # torch.Size([4, 1, 1, 1])
ln_std = inputx.std(dim=(1,2,3),keepdim=True,unbiased=False)  # torch.Size([4, 1, 1, 1])
verify_bn_y = (inputx - ln_mean)/(ln_std + 1e-05)   # torch.Size([4, 3, 2, 2])
print(ln_mean.shape)
print(ln_std.shape)
print(ln_y.shape)
print(verify_bn_y.shape)
print(torch.allclose(ln_y,verify_bn_y))
'''
    torch.Size([4, 1, 1, 1])
    torch.Size([4, 1, 1, 1])
    torch.Size([4, 3, 2, 2])
    torch.Size([4, 3, 2, 2])
    True
'''

总结

所有的图示 再看一遍

BN常用于CV

LN常用于NLP

image-20241128200239432

image-20241128203318040

add_circle2024-11-25 22:25:07update2024-12-04 14:56:27