Xz's blog Xz's blog
首页
时间序列
多模态
合成生物学
其他方向
生活
工具相关
PyTorch
导航站

Xu Zhen

首页
时间序列
多模态
合成生物学
其他方向
生活
工具相关
PyTorch
导航站
  • 零基础从零实现mini Pytorch

  • PyTorch基础知识

    • 各种Normalization
      • 1 Batch Normalization(BN)
        • 1.1 原理简介
        • 1.2 PyTorch 示例
        • 1.3 适用场景
      • 2 Layer Normalization(LN)
        • 2.1 作用
        • 2.2 举个最简单的例子(2个样本,每个有3个特征)
        • 2.3 输出说明:
        • 2.4 总结
      • 3 Instance Normalization(IN)
        • 3.1 作用
        • 3.2 举个简单例子:图像 \[N, C, H, W] 形状,做每个样本每个通道归一化
        • 3.3 总结一句话:
      • 4 Group Normalization(GN)
        • 4.1 作用与背景
        • 4.2 举个例子:
        • 4.3 示例代码
        • 4.4 手动说明下怎么归一化
        • 4.5 输出说明(近似):
        • 4.6 总结对比
      • 5 RMSNorm(Root Mean Square Normalization)
        • 5.1 作用与背景
        • 5.2 归一化公式
        • 5.3 示例代码
        • 5.4 输出说明(近似):
        • 5.5 总结
      • 6 ScaleNorm(比例缩放归一化)
        • 6.1 作用与背景
        • 6.2 公式定义
        • 6.3 简单实现(PyTorch)
        • 6.4 输出解释
        • 6.5 特点总结
      • 7 深度学习常见 Normalization 方法对比表
        • 7.1 归一化在哪儿:
        • 7.2 使用建议:
      • 8 序列特征如何 Normalization
        • 8.1 LayerNorm 是怎么归一化的?
        • 8.1.1 原则:
        • 8.1.2 举例讲解
        • 8.2 RMSNorm 是怎么归一化的?
        • 8.2.1 举例
        • 8.3 小结对比
    • register_buffer、nn.Parameter与torch.tensor比较
  • Python基础

  • PyTorch学习笔记
  • PyTorch基础知识
xuzhen
2025-07-24
目录

各种Normalization

# 1 Batch Normalization(BN)

# 1.1 原理简介

Batch Normalization 是最早也是最常见的一种归一化方式,它在每一个小批次上对特征进行标准化,使其均值为 0,方差为 1,从而缓解内部协变量偏移。(对每一批数据(batch)中的每一列(特征)进行标准化,让它们变成均值为0、方差为1的分布。)

对一个特征维度 xxx,计算如下:

μB=1m∑i=1mxi,σB2=1m∑i=1m(xi−μB)2\mu_B = \frac{1}{m} \sum_{i=1}^m x_i,\quad \sigma_B^2 = \frac{1}{m} \sum_{i=1}^m (x_i - \mu_B)^2 μB​=m1​i=1∑m​xi​,σB2​=m1​i=1∑m​(xi​−μB​)2

x^i=xi−μBσB2+ϵ,yi=γx^i+β\hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}},\quad y_i = \gamma \hat{x}_i + \beta x^i​=σB2​+ϵ​xi​−μB​​,yi​=γx^i​+β

其中:

  • mmm 是 batch size
  • γ\gammaγ 和 β\betaβ 是可学习参数(用于恢复模型表达能力)

# 1.2 PyTorch 示例

import torch
import torch.nn as nn

# 一个 batch 有 2 个样本,每个样本有 3 个特征
x = torch.tensor([[1.0, 2.0, 3.0],
                  [2.0, 4.0, 6.0]])

# 转成 float32 类型
x = x.float()

# 创建 BatchNorm1d 对象,对每个“列”做归一化(因为是3个特征)
bn = nn.BatchNorm1d(num_features=3)

# 在训练模式下进行前向传播
bn.train()
out = bn(x)

print("输入:\n", x)
print("归一化输出:\n", out)

# 计算均值和方差
mean = x.mean(dim=0, keepdim=True)
std = x.std(dim=0, keepdim=True, unbiased=False)
print("均值:\n", mean)
print("方差:\n", std)
# 计算归一化输出
normalized_output = (x - mean) / (std + 1e-5)  # 加上一个小常数以避免除零错误
print("手动归一化输出:\n", normalized_output)
'''
输入:
 tensor([[1., 2., 3.],
        [2., 4., 6.]])
归一化输出:
 tensor([[-1.0000, -1.0000, -1.0000],
        [ 1.0000,  1.0000,  1.0000]], grad_fn=<NativeBatchNormBackward0>)
均值:
 tensor([[1.5000, 3.0000, 4.5000]])
方差:
 tensor([[0.5000, 1.0000, 1.5000]])
手动归一化输出:
 tensor([[-1.0000, -1.0000, -1.0000],
        [ 1.0000,  1.0000,  1.0000]])
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

输出说明(假设 gamma=1,beta=0):

输入:
[[1.0, 2.0, 3.0],
 [2.0, 4.0, 6.0]]
1
2
3
  • 第1列均值是 1.5,方差是 0.25
  • 第2列均值是 3.0,方差是 1.0
  • 第3列均值是 4.5,方差是 2.25

归一化后:

[[-1, -1, -1],
 [ 1,  1,  1]]
1
2

(实际输出会稍微不同,因为加了一个小常数 epsilon 来避免除以 0)

# 1.3 适用场景

  • CNN 中用于特征图的每个通道(使用 BatchNorm2d)
  • MLP 中用于全连接层(使用 BatchNorm1d)

# 2 Layer Normalization(LN)

# 2.1 作用

LayerNorm 和 BatchNorm 最大的区别是:

  • BatchNorm:对同一特征维度、跨多个样本归一化(对列)
  • LayerNorm:对每个样本自身的所有特征归一化(对行)

这使得 LayerNorm 更适用于 RNN、Transformer、小 batch 情况

# 2.2 举个最简单的例子(2个样本,每个有3个特征)

import torch
import torch.nn as nn

# 输入:2个样本,每个样本有3个特征
x = torch.tensor([[1.0, 2.0, 3.0],
                  [2.0, 4.0, 6.0]])

x = x.float()

# LayerNorm 对每个样本的全部3个特征归一化
ln = nn.LayerNorm(normalized_shape=3)

out = ln(x)

print("输入:\n", x)
print("归一化输出:\n", out)

# 计算均值和方差
mean = x.mean(dim=1, keepdim=True)
std = x.std(dim=1, keepdim=True, unbiased=False)
print("均值:\n", mean)
print("方差:\n", std)
# 计算归一化输出
normalized_output = (x - mean) / (std + 1e-5)  # 加上一个小常数以避免除零错误
print("手动归一化输出:\n", normalized_output)

'''
输入:
 tensor([[1., 2., 3.],
        [2., 4., 6.]])
归一化输出:
 tensor([[-1.2247,  0.0000,  1.2247],
        [-1.2247,  0.0000,  1.2247]], grad_fn=<NativeLayerNormBackward0>)
均值:
 tensor([[2.],
        [4.]])
方差:
 tensor([[0.8165],
        [1.6330]])
手动归一化输出:
 tensor([[-1.2247,  0.0000,  1.2247],
        [-1.2247,  0.0000,  1.2247]])
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 2.3 输出说明:

对于第一行 [1.0, 2.0, 3.0]:

  • 均值 = 2.0,方差 = 1.0
  • 归一化结果 = [-1.2247, 0.0, 1.2247](近似)

对于第二行 [2.0, 4.0, 6.0]:

  • 均值 = 4.0,方差 = 2
  • 归一化结果 = [-1.2247, 0.0, 1.2247](近似)

# 2.4 总结

项目 BatchNorm LayerNorm
归一化维度 跨样本、每个特征(列) 每个样本内部所有特征(行)
应用场景 CNN、图像 NLP、RNN、Transformer、小batch
输入形状 [B, C] / [B, C, H, W] 通常是 [B, L]、[B, L, D] 等

# 3 Instance Normalization(IN)

# 3.1 作用

Instance Normalization 是对每个样本的每个通道分别归一化。和前两种的区别:

名称 归一化维度
BatchNorm 每个通道、跨整个 batch
LayerNorm 每个样本、所有特征(向量层)
InstanceNorm 每个样本的每个通道(图像每张图的每个通道)

InstanceNorm 常用于图像风格迁移(style transfer),因为它去除了样本间的统计特征,只保留个体结构。

# 3.2 举个简单例子:图像 [N, C, H, W] 形状,做每个样本每个通道归一化

我们用一个更真实的张量,形状为 [batch_size=1, channels=2, height=2, width=2],表示 1 张图像,2 个通道,每个通道是 2×2 的像素块。

import torch
import torch.nn as nn

# 创建一个样本,2个通道,每个通道是 2x2 像素
x = torch.tensor([[
    [[1.0, 2.0],        # 第一个通道
     [3.0, 4.0]],

    [[10.0, 20.0],      # 第二个通道
     [30.0, 40.0]]
]])

x = x.float()  # 形状:[1, 2, 2, 2]

# InstanceNorm2d: 对每个样本的每个通道归一化(独立处理)
inorm = nn.InstanceNorm2d(num_features=2, affine=False)

# 归一化输出
out = inorm(x)

print("输入:\n", x)
print("归一化输出:\n", out)

# 计算均值和方差
mean = x.mean(dim=(2, 3), keepdim=True)
std = x.std(dim=(2, 3), keepdim=True, unbiased=False)
print("均值:\n", mean)
print("方差:\n", std)
print("手动归一化输出:\n", (x - mean) / (std + 1e-5))  # 加上一个小常数以避免除零错误
'''
输入:
 tensor([[[[ 1.,  2.],
          [ 3.,  4.]],

         [[10., 20.],
          [30., 40.]]]])
归一化输出:
 tensor([[[[-1.3416, -0.4472],
          [ 0.4472,  1.3416]],

         [[-1.3416, -0.4472],
          [ 0.4472,  1.3416]]]])
均值:
 tensor([[[[ 2.5000]],

         [[25.0000]]]])
方差:
 tensor([[[[ 1.1180]],

         [[11.1803]]]])
手动归一化输出:
 tensor([[[[-1.3416, -0.4472],
          [ 0.4472,  1.3416]],

         [[-1.3416, -0.4472],
          [ 0.4472,  1.3416]]]])
'''

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# 3.3 总结一句话:

InstanceNorm:适合图像风格迁移,归一化的是每个样本的每个通道的所有像素。

# 4 Group Normalization(GN)

# 4.1 作用与背景

Group Normalization(组归一化)是为了解决 BatchNorm 在小 batch size 下效果不佳 的问题。它的归一化策略:

把通道(Channel)分成几组,每组内部进行归一化。

适用于图像、3D、NLP 等多种任务,尤其是:

  • Batch 很小(比如只有1或2)时
  • 模型很深、需要稳定训练时

# 4.2 举个例子:

我们还是用一个张量 [1, 4, 2, 2]:

  • batch_size = 1
  • channel = 4
  • 每个通道是 2×2 像素

我们将通道分成 2组,每组2个通道。

# 4.3 示例代码

import torch
import torch.nn as nn

# 输入张量:[B=1, C=4, H=2, W=2]
x = torch.tensor([[
    [[1.0, 2.0], [3.0, 4.0]],     # 通道1
    [[5.0, 6.0], [7.0, 8.0]],     # 通道2
    [[10.0, 20.0], [30.0, 40.0]], # 通道3
    [[50.0, 60.0], [70.0, 80.0]]  # 通道4
]])

x = x.float()  # [1, 4, 2, 2]

# 将4个通道分成2组(每组2个通道)
gn = nn.GroupNorm(num_groups=2, num_channels=4, affine=False)

out = gn(x)

print("输入:\n", x)
print("归一化输出:\n", out)

# 分组归一化的均值和方差计算
# 手动计算
group1 = x[0, 0:2].reshape(-1)  # 通道0和1
group2 = x[0, 2:4].reshape(-1)  # 通道2和3

mean1 = group1.mean()
std1 = group1.std(unbiased=False)
print("组1均值:\n", mean1)
print("组1方差:\n", std1)
group1_norm = (x[0, 0:2] - mean1) / std1

mean2 = group2.mean()
std2 = group2.std(unbiased=False)
print("组2均值:\n", mean2)
print("组2方差:\n", std2)
group2_norm = (x[0, 2:4] - mean2) / std2

manual = torch.cat([group1_norm, group2_norm], dim=0).unsqueeze(0)
print("手动归一化结果:\n", manual)

'''
输入:
 tensor([[[[ 1.,  2.],
          [ 3.,  4.]],

         [[ 5.,  6.],
          [ 7.,  8.]],

         [[10., 20.],
          [30., 40.]],

         [[50., 60.],
          [70., 80.]]]])
归一化输出:
 tensor([[[[-1.5275, -1.0911],
          [-0.6547, -0.2182]],

         [[ 0.2182,  0.6547],
          [ 1.0911,  1.5275]],

         [[-1.5275, -1.0911],
          [-0.6547, -0.2182]],

         [[ 0.2182,  0.6547],
          [ 1.0911,  1.5275]]]])
组1均值:
 tensor(4.5000)
组1方差:
 tensor(2.2913)
组2均值:
 tensor(45.)
组2方差:
 tensor(22.9129)
手动归一化结果:
 tensor([[[[-1.5275, -1.0911],
          [-0.6547, -0.2182]],

         [[ 0.2182,  0.6547],
          [ 1.0911,  1.5275]],

         [[-1.5275, -1.0911],
          [-0.6547, -0.2182]],

         [[ 0.2182,  0.6547],
          [ 1.0911,  1.5275]]]])
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

# 4.4 手动说明下怎么归一化

  • Group 1 = 通道 1 和 2,共 8 个值
  • Group 2 = 通道 3 和 4,共 8 个值

对每组内所有像素值做均值方差归一化。

🚨 关键点:跨通道归一化,但不跨 batch,不跨组。

# 4.5 输出说明(近似):

输出中:

  • Group1(前2通道)会被归一化成均值0,标准差1
  • Group2(后2通道)也独立归一化

# 4.6 总结对比

名称 归一化范围 是否依赖 batch size
BatchNorm 每个通道(跨样本) ✅ 是
LayerNorm 每个样本自身所有特征 ❌ 否
InstanceNorm 每个样本的每个通道 ❌ 否
GroupNorm 每个样本的每组通道 ❌ 否

# 5 RMSNorm(Root Mean Square Normalization)

# 5.1 作用与背景

RMSNorm 是一种更简洁、更稳定的归一化方法,常见于 Transformer 变种中,比如 GPT 系列和一些轻量模型。它与 LayerNorm 非常相似,但有两点区别:

对比项 LayerNorm RMSNorm
是否减均值 ✅ 是 ❌ 否
是否只用方差 ❌ 均值和方差都用 ✅ 只用 RMS(均方根)
公式是否更简单 ❌ ✅ 更简单计算、更稳定、更高效

# 5.2 归一化公式

RMSNorm 公式如下:

RMS(x)=1d∑i=1dxi2\text{RMS}(x) = \sqrt{\frac{1}{d} \sum_{i=1}^d x_i^2} RMS(x)=d1​i=1∑d​xi2​​

RMSNorm(x)=xRMS(x)+ϵ⋅γ\text{RMSNorm}(x) = \frac{x}{\text{RMS}(x) + \epsilon} \cdot \gamma RMSNorm(x)=RMS(x)+ϵx​⋅γ

其中:

  • xxx 是一个样本向量
  • ddd 是维度长度
  • γ\gammaγ 是可学习缩放因子(没有偏移量 β\betaβ)
  • 没有减去均值

# 5.3 示例代码

import torch
import torch.nn as nn

class RMSNorm(nn.Module):
    def __init__(self, dim, eps=1e-8):
        super().__init__()
        self.eps = eps
        self.gamma = nn.Parameter(torch.ones(dim))  # 可学习缩放

    def forward(self, x):
        rms = torch.sqrt(torch.mean(x ** 2, dim=-1, keepdim=True))
        return self.gamma * x / (rms + self.eps)

# 输入是2个样本,每个有4个特征
x = torch.tensor([[1.0, 2.0, 3.0, 4.0],
                  [2.0, 4.0, 6.0, 8.0]])

x = x.float()

rmsnorm = RMSNorm(dim=4)
out = rmsnorm(x)

print("输入:\n", x)
print("归一化输出:\n", out)
'''
输入:
 tensor([[1., 2., 3., 4.],
        [2., 4., 6., 8.]])
归一化输出:
 tensor([[0.3651, 0.7303, 1.0954, 1.4606],
        [0.3651, 0.7303, 1.0954, 1.4606]], grad_fn=<DivBackward0>)
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 5.4 输出说明(近似):

对第一行:

  • 均方根 = √[(1² + 2² + 3² + 4²)/4] = √7.5 ≈ 2.7386
  • 每个数除以 2.7386 → 归一化成相对比例结构

优点:

  • 更高效(少一步减均值)
  • 更稳定(避免减均值带来的波动)
  • 不依赖 batch

# 5.5 总结

名称 是否减均值 是否标准差 是否依赖 batch 常用于
LayerNorm ✅ ✅ ❌ Transformer/LLM
RMSNorm ❌ ✅ (RMS) ❌ GPT-Neo, LLaMA

# 6 ScaleNorm(比例缩放归一化)

# 6.1 作用与背景

ScaleNorm 是一种极简的归一化方式,用于替代 LayerNorm,在某些 Transformer 模型中被使用(如 T5 的某些变体)。它的思路很简单:

不去计算均值或方差,仅通过一个固定比例来控制输出大小。

# 6.2 公式定义

ScaleNorm(x)=γ∥x∥⋅x\text{ScaleNorm}(x) = \frac{\gamma}{\|x\|} \cdot x ScaleNorm(x)=∥x∥γ​⋅x

其中:

  • xxx 是一个向量
  • ∥x∥\|x\|∥x∥ 是其 L2 范数(向量长度)
  • γ\gammaγ 是一个可学习的标量参数

它不会做标准化,只是缩放向量到固定长度(类似单位向量再乘一个比例)

# 6.3 简单实现(PyTorch)

import torch
import torch.nn as nn

class ScaleNorm(nn.Module):
    def __init__(self, scale, eps=1e-5):
        super().__init__()
        self.scale = nn.Parameter(torch.tensor(scale))
        self.eps = eps

    def forward(self, x):
        norm = x.norm(dim=-1, keepdim=True)
        return self.scale * x / (norm + self.eps)

# 示例:2个样本,每个4维特征
x = torch.tensor([[1.0, 2.0, 3.0, 4.0],
                  [2.0, 4.0, 6.0, 8.0]])

x = x.float()

scalenorm = ScaleNorm(scale=1.0)
out = scalenorm(x)

print("输入:\n", x)
print("归一化输出:\n", out)
'''
输入:
 tensor([[1., 2., 3., 4.],
        [2., 4., 6., 8.]])
归一化输出:
 tensor([[0.1826, 0.3651, 0.5477, 0.7303],
        [0.1826, 0.3651, 0.5477, 0.7303]], grad_fn=<DivBackward0>)
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 6.4 输出解释

对第一行:

  • L2 范数 = √(1²+2²+3²+4²) = √30 ≈ 5.477
  • 归一化为单位向量再乘上可学习的 γ

最终输出就变成“长度为 γ”的向量,方向不变。

# 6.5 特点总结

特点 是否有均值/方差 是否可学习 复杂度 是否依赖 batch
LayerNorm ✅ 有 ✅ 中 ❌
RMSNorm ❌ 无均值 ✅ 中 ❌
ScaleNorm ❌ 无均值/方差 ✅(仅缩放) ✅ 最快 ❌

适用场景:

  • 大模型训练中追求极致简洁与速度
  • 对规范化要求不高但要控制数值范围

# 7 深度学习常见 Normalization 方法对比表

名称 是否减均值 是否标准差 / RMS 是否使用偏度 归一化维度 依赖 batch? 常见用法 / 模型
BatchNorm ✅ 是 ✅ 是(标准差) ❌ 否 每个通道(跨 batch) ✅ 是 CNN, ResNet, MLP
LayerNorm ✅ 是 ✅ 是(标准差) ❌ 否 每个样本自身所有特征 ❌ 否 NLP, Transformer, LLM
InstanceNorm ✅ 是 ✅ 是(标准差) ❌ 否 每个样本的每个通道 ❌ 否 风格迁移,图像生成
GroupNorm ✅ 是 ✅ 是(标准差) ❌ 否 每个样本每组通道(按组划分) ❌ 否 小 batch 图像模型
RMSNorm ❌ 否 ✅ 是(RMS) ❌ 否 每个样本所有特征(类似 LayerNorm) ❌ 否 LLaMA, GPT-Neo, Transformer
ScaleNorm ❌ 否 ❌ 否(只用 L2 范数) ❌ 否 每个样本整体缩放 ❌ 否 T5 变体,轻量 Transformer

# 7.1 归一化在哪儿:

输入 shape BatchNorm LayerNorm InstanceNorm GroupNorm
[B, C, H, W] 对每个通道 跨 batch 对每个样本的全特征 每张图每个通道独立 每张图通道分组归一化

# 7.2 使用建议:

情况 推荐归一化方法
CNN 图像 + batch 较大 BatchNorm
CNN 图像 + batch 很小 GroupNorm / InstanceNorm
NLP / Transformer LayerNorm / RMSNorm / PowerNorm
图像风格迁移、图像生成模型 InstanceNorm
追求极简、快速推理 ScaleNorm

# 8 序列特征如何 Normalization

(batchsize,seq,feature_dim),这种序列特征layernorm和RMSNorm是怎么归一化的?

现在用具体维度为 [batch_size, seq_len, feature_dim] 的张量来说明 LayerNorm 和 RMSNorm 是如何归一化的。

x.shape = [B, L, D]  
# B: batch_size,例如 2
# L: 序列长度,例如 5
# D: 特征维度,例如 4
1
2
3
4

# 8.1 LayerNorm 是怎么归一化的?

# 8.1.1 原则:

对每个样本的每个时间步(token)单独做归一化:

沿着最后一个维度 D(feature_dim)做归一化

也就是说,对于 x[b, l, :] 这一个 token,它是一个 [D] 的向量,LayerNorm 会:

  • 减去它自己的均值
  • 除以它自己的标准差
  • 再乘上 γ,加上 β(可学习)

# 8.1.2 举例讲解

import torch
import torch.nn as nn

# 手动创建一个固定值的张量,方便观察
x = torch.tensor([
    [  # 第一句话,共3个token
        [1.0, 2.0, 3.0, 4.0],    # token 1
        [2.0, 4.0, 6.0, 8.0],    # token 2
        [0.5, 1.0, 1.5, 2.0]     # token 3
    ],
    [  # 第二句话
        [10.0, 20.0, 30.0, 40.0],
        [5.0, 5.0, 5.0, 5.0],
        [-1.0, 0.0, 1.0, 2.0]
    ]
])  # shape = [2, 3, 4]

x = x.float()
print("原始输入 shape:", x.shape)

ln = nn.LayerNorm(normalized_shape=4)
out_ln = ln(x)
print("\nLayerNorm 输出:\n", out_ln)

'''
原始输入 shape: torch.Size([2, 3, 4])

LayerNorm 输出:
 tensor([[[-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416],
         [-1.3416, -0.4472,  0.4472,  1.3416]],

        [[-1.3416, -0.4472,  0.4472,  1.3416],
         [ 0.0000,  0.0000,  0.0000,  0.0000],
         [-1.3416, -0.4472,  0.4472,  1.3416]]],
       grad_fn=<NativeLayerNormBackward0>)
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

每个 token 如 [1, 2, 3, 4] 会被:

  • 减去自身均值 2.5
  • 除以标准差 ~1.118
  • 得到输出 [-1.3416, -0.4472, 0.4472, 1.3416]

# 8.2 RMSNorm 是怎么归一化的?

RMSNorm 的原理一样,只不过:

  • 不减均值
  • 只除以 RMS(均方根)

也就是对每个 x[b, l, :]:

RMSNorm(x)=x1D∑xi2+ϵ⋅γ\text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{D} \sum x_i^2} + \epsilon} \cdot \gamma RMSNorm(x)=D1​∑xi2​​+ϵx​⋅γ

# 8.2.1 举例

class RMSNorm(nn.Module):
    def __init__(self, dim, eps=1e-8):
        super().__init__()
        self.eps = eps
        self.gamma = nn.Parameter(torch.ones(dim))  # 可学习缩放

    def forward(self, x):
        rms = torch.sqrt(torch.mean(x ** 2, dim=-1, keepdim=True))
        return self.gamma * x / (rms + self.eps)

import torch
import torch.nn as nn

# 手动创建一个固定值的张量,方便观察
x = torch.tensor([
    [  # 第一句话,共3个token
        [1.0, 2.0, 3.0, 4.0],    # token 1
        [2.0, 4.0, 6.0, 8.0],    # token 2
        [0.5, 1.0, 1.5, 2.0]     # token 3
    ],
    [  # 第二句话
        [10.0, 20.0, 30.0, 40.0],
        [5.0, 5.0, 5.0, 5.0],
        [-1.0, 0.0, 1.0, 2.0]
    ]
])  # shape = [2, 3, 4]

rmsnorm = RMSNorm(dim=4)
out_rms = rmsnorm(x)
print("\nRMSNorm 输出:\n", out_rms)
'''
RMSNorm 输出:
 tensor([[[ 0.3651,  0.7303,  1.0954,  1.4606],
         [ 0.3651,  0.7303,  1.0954,  1.4606],
         [ 0.3651,  0.7303,  1.0954,  1.4606]],

        [[ 0.3651,  0.7303,  1.0954,  1.4606],
         [ 1.0000,  1.0000,  1.0000,  1.0000],
         [-0.8165,  0.0000,  0.8165,  1.6330]]], grad_fn=<DivBackward0>)
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 8.3 小结对比

项目 LayerNorm RMSNorm
归一化维度 每个 token 的特征维度 D 同上
是否减均值 ✅ 是 ❌ 否
是否除 RMS ✅ 用标准差(含均值) ✅ 用 RMS
参数维度 γ, β 是 [D] 向量(每个特征一组) γ 是 [D] 向量(β 可无)
输入形状支持 [B, L, D],自动按 D 归一化 同上
常见场景 BERT, GPT, Transformer GPT-Neo, LLaMA, Falcon, Mamba
#PyTorch
上次更新: 2025/07/25, 09:32:17

← 自动求导 register_buffer、nn.Parameter与torch.tensor比较→

最近更新
01
Slice切片
07-26
02
引用与借用
07-26
03
所有权
07-26
更多文章>
Theme by Vdoing | Copyright © 2025-2025 Xu Zhen | 鲁ICP备2025169719号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式