图解LayerNorm & BatchNorm¶
约 897 个字 119 行代码 30 张图片 预计阅读时间 6 分钟
BatchNorm1D、NLP¶
b=2,n=3,d=4
- 理解 3维张量、4维张量:
\(\begin{cases} 矩阵法:2个3×4的矩阵 \\ \\ 抽屉法:一直划分同一个方向,直到最后一个维度,画另一个方向 \end{cases}\)
都是横排和竖排的数字排列,不同维度的张量表示不同的分组
怎么理解nlp中的三维张量?¶
- 矩阵法:
2个3×4的矩阵,图解:
- 抽屉法
(第一次真正开始理解多维张量是好朋友拿抽屉给我举得例子,四维张量就是抽屉里面还有抽屉)
要点:一直沿同一个方向划分,直到最后一维换方向
2×3×4
:逻辑理顺了,感觉自己做这些东西蠢蠢的......但是,我记性不好,总忘......笨就笨吧,(凌妙妙口吻:师傅天天说人家笨,可人家本来就是笨)
- 实际意义
对于nlp来说,bnd=234表示b个句子,每个句子n个词,每个词有d个维度
\(batch\_size × max\_sequence\_length × model\_dim\)
就是说 现在有 2个句子,每个句子3个单词,每个单词用4维向量表示
这里的问题是:我们这个元素的个数都是对的,但是在计算机中存储中,并不是这么存的
- 实际意义 & 矩阵法 & 计算机存储逻辑
batch_size = 2
times_steps = 3
embedding_dim = 4
inputx = torch.randn(batch_size,times_steps,embedding_dim) # N*L*C
print(inputx)
输出:
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?¶
输出:
好嘟,这串代码是没有任何问题了
代码实现¶
库函数要的输入格式:bnd \(\rightarrow\) bdn
- 说一下这边的转置 .transpose(-1,-2)
官网api给了,输入bdn,输出bdn,所以输出以后再transpose,变成bnd
还有一点,不管是BN还是LN都是不改变形状的
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))
输出:
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
自己脑袋里想的:
计算机认识的:
加括号,就变成了张量
计算机与实际意义联系起来:优先竖排,最后一维横排
如图,4×3×2×2
BN与CV¶
bchw \(\rightarrow\) 1c11
对 bhw个数求和 计算 均值和方差
step1
step2
step3
第三个通道的均值和⽅差:
代码¶
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]
均值 & 方差代码:
# 计算每个通道的均值和⽅差
mean = input_tensor.mean(dim=(0, 2, 3))
var = input_tensor.var(dim=(0, 2, 3), unbiased=False)
print(f"均值: {mean}")
print(f"⽅差: {var}")
输出:
LN1D¶
文字描述、实际意义¶
- LN是对每个词的所有特征进行归一化
类比到二维数表是对 横行样本行进行归一化
- BN是对同一个特征的所有样本进行归一化
类比到二维数表 就是对列进行归一化
- 我觉得理解LN就一句话牢牢记住:per sample、per layer!尤其是per sample
有几个样本、就有几个均值&方差、类似的
有几个词,就有均值 bnd → 1n1
有几张图片,就有几个均值 bchw→b111
来,在折腾一下,for BN,特征多少维,就是几个均值
bnd→11d
bchw→1c11
- 二维数表的格式:
图示¶
对比:BN1D & LN1D
数学例子¶
见:5种归一化方法
代码实现¶
见:5种归一化方法
LN2D¶
文字描述¶
per sample
数学例子¶
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]
图示¶
对比:
代码实现¶
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