当前位置: 首页 > news >正文

从0开始学AI:层归一化,原来是这回事!

归一化原理

归一化按照归一化的维度区分,有层归一化(Layer Norm)和批归一化(Batch Norm)。

特性层归一化批归一化
归一化维度每个样本的所有特征每个特征的所有样本
计算方式对每个样本独立归一化对每个特征独立归一化
适用场景变长序列,小批量固定长度,大批量
Transformer适用不适用
RNN适用不适用
CNN很少使用常用

在层归一化中,对于每个样本:

  1. 计算均值: μ = (1/n) * Σxᵢ
  2. 计算方差: σ² = (1/n) * Σ(xᵢ - μ)²
  3. 归一化: x̂ᵢ = (xᵢ - μ) / √(σ² + ε)
  4. 缩放和平移: yᵢ = γ * x̂ᵢ + β

其中:

  • n: 特征数量
  • ε: 小常数(防止除以0)
  • γ: 可学习的缩放参数
  • β: 可学习的平移参数
""" 逐步演示层归一化的计算过程 """importtorchimporttorch.nnasnndefdemo_layer_norm():""" 逐步演示层归一化的计算过程 """print("\n=== 层归一化计算过程演示 ===\n")# 创建输入数据x=torch.tensor([[1.0,2.0,3.0,4.0,5.0]])print(f"1. 输入数据:")print(f" 形状:{x.shape}")# (1, 5)print(f" 数据:{x[0].tolist()}")print(f" 均值:{x.mean(dim=-1).item():.4f}")print(f" 标准差:{x.std(dim=-1,unbiased=False).item():.4f}\n")# 手动计算层归一化print(f"2. 手动计算层归一化:")# 计算均值mean=x.mean(dim=-1,keepdim=True)print(f" 步骤1: 计算均值")print(f" μ = (1/n) * Σxᵢ")print(f" μ ={mean.item():.4f}")# 计算方差variance=((x-mean)**2).mean(dim=-1,keepdim=True)print(f"\n 步骤2: 计算方差")print(f" σ² = (1/n) * Σ(xᵢ - μ)²")print(f" σ² ={variance.item():.4f}")# 计算标准差std=torch.sqrt(variance+1e-5)# ε = 1e-5print(f"\n 步骤3: 计算标准差")print(f" σ = √(σ² + ε)")print(f" σ ={std.item():.4f}")# 归一化x_normalized=(x-mean)/stdprint(f"\n 步骤4: 归一化")print(f" x̂ᵢ = (xᵢ - μ) / σ")print(f" 归一化后数据:{x_normalized[0].tolist()}")print(f" 归一化后均值:{x_normalized.mean(dim=-1).item():.6f}")print(f" 归一化后标准差:{x_normalized.std(dim=-1,unbiased=False).item():.6f}\n")# 使用PyTorch的LayerNormprint(f"3. 使用PyTorch的LayerNorm:")layer_norm=nn.LayerNorm(5,elementwise_affine=False)# 不使用可学习参数x_pytorch=layer_norm(x)print(f" 归一化后数据:{x_pytorch[0].tolist()}")print(f" 与手动计算一致:{torch.allclose(x_normalized,x_pytorch,atol=1e-5)}\n")# 使用可学习参数print(f"4. 使用可学习参数 (γ, β):")layer_norm_with_params=nn.LayerNorm(5,elementwise_affine=True)x_with_params=layer_norm_with_params(x)print(f" γ (缩放参数):{layer_norm_with_params.weight.data.tolist()}")print(f" β (平移参数):{layer_norm_with_params.bias.data.tolist()}")print(f" 输出数据:{x_with_params[0].tolist()}")print(f" 输出均值:{x_with_params.mean(dim=-1).item():.4f}")print(f" 输出标准差:{x_with_params.std(dim=-1,unbiased=False).item():.4f}\n")defdemo_layer_norm_batch():""" 演示批量的层归一化 """print("\n=== 批量层归一化演示 ===\n")# 创建批量数据x=torch.tensor([[1.0,2.0,3.0,4.0,5.0],[10.0,20.0,30.0,40.0,50.0],[100.0,200.0,300.0,400.0,500.0]])print(f"1. 输入数据:")print(f" 形状:{x.shape}")# (3, 5)print(f" 数据:")foriinrange(x.size(0)):print(f" 样本{i+1}:{x[i].tolist()}")print(f" 均值:{x[i].mean().item():.2f}, 标准差:{x[i].std(unbiased=False).item():.2f}")print()# 层归一化layer_norm=nn.LayerNorm(5)x_normalized=layer_norm(x)print(f"2. 层归一化后:")print(f" 形状:{x_normalized.shape}")# (3, 5)print(f" 数据:")foriinrange(x_normalized.size(0)):print(f" 样本{i+1}:{x_normalized[i].tolist()}")print(f" 均值:{x_normalized[i].mean().item():.6f}, 标准差:{x_normalized[i].std(unbiased=False).item():.6f}")print()print(f"3. 关键观察:")print(f" - 每个样本独立归一化")print(f" - 归一化后每个样本的均值≈0,标准差≈1")print(f" - 不同样本之间的数值范围变得相似")if__name__=="__main__":# 演示计算过程cdemo_layer_norm()# 演示批量归一化demo_layer_norm_batch()

输出如下:

=== 层归一化计算过程演示 === 1. 输入数据: 形状: torch.Size([1, 5]) 数据: [1.0, 2.0, 3.0, 4.0, 5.0] 均值: 3.0000 标准差: 1.4142 2. 手动计算层归一化: 步骤1: 计算均值 μ = (1/n) * Σxᵢ μ = 3.0000步骤2: 计算方差 σ² = (1/n) * Σ(xᵢ - μ)² σ² = 2.0000步骤3: 计算标准差 σ = √(σ² + ε) σ = 1.4142步骤4: 归一化 x̂ᵢ = (xᵢ - μ) / σ 归一化后数据: [-1.4142099618911743, -0.7071049809455872, 0.0, 0.7071049809455872, 1.4142099618911743] 归一化后均值: 0.000000 归一化后标准差: 0.999997 3. 使用PyTorch的LayerNorm: 归一化后数据: [-1.4142099618911743, -0.7071049809455872, 0.0, 0.7071049809455872, 1.4142099618911743] 与手动计算一致: True 4. 使用可学习参数 (γ, β): γ (缩放参数): [1.0, 1.0, 1.0, 1.0, 1.0] β (平移参数): [0.0, 0.0, 0.0, 0.0, 0.0] 输出数据: [-1.4142099618911743, -0.7071049809455872, 0.0, 0.7071049809455872, 1.4142099618911743] 输出均值: 0.0000 输出标准差: 1.0000 === 批量层归一化演示 === 1. 输入数据: 形状: torch.Size([3, 5]) 数据: 样本1: [1.0, 2.0, 3.0, 4.0, 5.0] 均值: 3.00, 标准差: 1.41 样本2: [10.0, 20.0, 30.0, 40.0, 50.0] 均值: 30.00, 标准差: 14.14 样本3: [100.0, 200.0, 300.0, 400.0, 500.0] 均值: 300.00, 标准差: 141.42 2. 层归一化后: 形状: torch.Size([3, 5]) 数据: 样本1: [-1.4142099618911743, -0.7071049809455872, 0.0, 0.7071049809455872, 1.4142099618911743] 均值: 0.000000, 标准差: 0.999997 样本2: [-1.4142134189605713, -0.7071067094802856, 0.0, 0.7071067094802856, 1.4142134189605713] 均值: 0.000000, 标准差: 1.000000 样本3: [-1.4142136573791504, -0.7071068286895752, 0.0, 0.7071068286895752, 1.4142136573791504] 均值: 0.000000, 标准差: 1.000000 3. 关键观察: ○ 每个样本独立归一化 ○ 归一化后每个样本的均值≈0,标准差≈1 ○ 不同样本之间的数值范围变得相似

对比层归一化和批归一化

""" 对比层归一化和批归一化 """importtorchimporttorch.nnasnndefcompare_layer_norm_batch_norm():""" 对比层归一化和批归一化 """print("\n=== 层归一化 vs 批归一化 ===\n")# 创建批量数据x=torch.randn(4,5)# (batch_size=4, features=5)print(f"1. 输入数据:")print(f" 形状:{x.shape}")# (4, 5)print(f" 数据:")print(f"{x}\n")# 层归一化print(f"2. 层归一化 (LayerNorm):")layer_norm=nn.LayerNorm(5)x_ln=layer_norm(x)print(f" 输出形状:{x_ln.shape}")# (4, 5)print(f" 输出:")print(f"{x_ln}")print(f" 每个样本的均值和标准差:")foriinrange(x_ln.size(0)):mean=x_ln[i].mean().item()std=x_ln[i].std(unbiased=False).item()print(f" 样本{i+1}: 均值={mean:.6f}, 标准差={std:.6f}")print()# 批归一化print(f"3. 批归一化 (BatchNorm):")batch_norm=nn.BatchNorm1d(5)batch_norm.eval()# 评估模式x_bn=batch_norm(x)print(f" 输出形状:{x_bn.shape}")# (4, 5)print(f" 输出:")print(f"{x_bn}")print(f" 每个特征的均值和标准差:")forjinrange(x_bn.size(1)):mean=x_bn[:,j].mean().item()std=x_bn[:,j].std(unbiased=False).item()print(f" 特征{j+1}: 均值={mean:.6f}, 标准差={std:.6f}")print()print(f"4. 关键区别:")print(f" LayerNorm: 对每个样本的所有特征归一化")print(f" BatchNorm: 对每个特征的所有样本归一化")print(f" LayerNorm: 适合变长序列(如Transformer)")print(f" BatchNorm: 适合固定长度(如CNN)")if__name__=="__main__":compare_layer_norm_batch_norm()
=== 层归一化 vs 批归一化 === 1. 输入数据: 形状: torch.Size([4, 5]) 数据: tensor([[ 0.4391, -0.5611, -0.0781, 0.6589, -1.0762], [-1.3637, 0.9116, 0.3021, 0.0291, -0.6390], [-0.4089, 0.5864, -1.2565, 0.6122, -0.1534], [ 0.7772, -0.4230, -0.9149, 0.5048, 0.5255]]) 2. 层归一化 (LayerNorm): 输出形状: torch.Size([4, 5]) 输出: tensor([[ 0.8829, -0.6868, 0.0712, 1.2280, -1.4952], [-1.5450, 1.3560, 0.5790, 0.2309, -0.6210], [-0.4102, 1.0231, -1.6307, 1.0601, -0.0423], [ 1.0526, -0.7964, -1.5540, 0.6329, 0.6649]], grad_fn=<NativeLayerNormBackward0>) 每个样本的均值和标准差: 样本1: 均值=0.000000, 标准差=0.999988 样本2: 均值=0.000000, 标准差=0.999992 样本3: 均值=0.000000, 标准差=0.999990 样本4: 均值=0.000000, 标准差=0.999988 3. 批归一化 (BatchNorm): 输出形状: torch.Size([4, 5]) 输出: tensor([[ 0.4391, -0.5611, -0.0781, 0.6589, -1.0762], [-1.3637, 0.9116, 0.3021, 0.0291, -0.6390], [-0.4089, 0.5864, -1.2565, 0.6122, -0.1534], [ 0.7772, -0.4230, -0.9149, 0.5048, 0.5255]], grad_fn=<NativeBatchNormBackward0>) 每个特征的均值和标准差: 特征1: 均值=-0.139086, 标准差=0.828601 特征2: 均值=0.128469, 标准差=0.632970 特征3: 均值=-0.486839, 标准差=0.625527 特征4: 均值=0.451251, 标准差=0.250056 特征5: 均值=-0.335768, 标准差=0.594807 4. 关键区别: LayerNorm: 对每个样本的所有特征归一化 BatchNorm: 对每个特征的所有样本归一化 LayerNorm: 适合变长序列(如Transformer) BatchNorm: 适合固定长度(如CNN)

Transformer 中的层归一化

因为在 Transformer 中,处理的是变成序列,所以,在 Transformer 中,使用的是 LayerNorm,而不是 BatchNorm,LayerNorm 配合残差连接效果更好。

""" 演示LayerNorm在Transformer编码器层中的应用 """importtorchimporttorch.nnasnnclassTransformerEncoderLayer(nn.Module):""" Transformer编码器层 结构: - 多头自注意力 - 残差连接 + LayerNorm - 前馈神经网络 - 残差连接 + LayerNorm """def__init__(self,d_model,n_heads,d_ff,dropout=0.1):super().__init__()# 多头自注意力self.attention=nn.MultiheadAttention(d_model,n_heads,batch_first=True)# 第一个LayerNormself.norm1=nn.LayerNorm(d_model)# 前馈神经网络self.ffn=nn.Sequential(nn.Linear(d_model,d_ff),nn.ReLU(),nn.Dropout(dropout),nn.Linear(d_ff,d_model))# 第二个LayerNormself.norm2=nn.LayerNorm(d_model)# Dropoutself.dropout=nn.Dropout(dropout)defforward(self,x):""" 前向传播 Args: x: 输入张量 (batch_size, seq_len, d_model) Returns: output: 输出张量 (batch_size, seq_len, d_model) """# 多头自注意力attn_output,_=self.attention(x,x,x)# 残差连接 + LayerNormx=self.norm1(x+self.dropout(attn_output))# 前馈神经网络ffn_output=self.ffn(x)# 残差连接 + LayerNormx=self.norm2(x+self.dropout(ffn_output))returnxdefdemo_layer_norm_in_transformer():""" 演示LayerNorm在Transformer中的应用 """print("\n=== LayerNorm在Transformer中的应用 ===\n")# 创建输入batch_size=2seq_len=5d_model=512n_heads=8d_ff=2048x=torch.randn(batch_size,seq_len,d_model)print(f"1. 输入:")print(f" 形状:{x.shape}")# (2, 5, 512)print(f" 第一个样本第一个token的统计:")print(f" 均值:{x[0,0].mean().item():.4f}")print(f" 标准差:{x[0,0].std(unbiased=False).item():.4f}")print(f" 最小值:{x[0,0].min().item():.4f}")print(f" 最大值:{x[0,0].max().item():.4f}\n")# 创建编码器层encoder_layer=TransformerEncoderLayer(d_model,n_heads,d_ff)encoder_layer.eval()# 前向传播print(f"2. 前向传播:")output=encoder_layer(x)print(f" 输出形状:{output.shape}")# (2, 5, 512)print(f" 第一个样本第一个token的统计:")print(f" 均值:{output[0,0].mean().item():.4f}")print(f" 标准差:{output[0,0].std(unbiased=False).item():.4f}")print(f" 最小值:{output[0,0].min().item():.4f}")print(f" 最大值:{output[0,0].max().item():.4f}\n")print(f"3. LayerNorm的作用:")print(f" - 稳定数值范围")print(f"-加速
http://www.jsqmd.com/news/530772/

相关文章:

  • 2026最新windows server2016安装教程,收藏这一篇就够了
  • Sqli-labs靶场通关实战:从字符型注入到HTTP头部注入的完整指南(附Payload大全)
  • 从半加器到BCD码加法器:用Logisim图解计算机运算的基石
  • Video2X视频增强技术全解析:从基础应用到深度优化
  • 导师推荐!断层领先的AI论文工具——千笔写作工具
  • 打个电话,为什么还要“导航”?
  • Fastutil实战:如何用Object2ObjectOpenHashMap替代Java HashMap提升性能(附性能对比测试)
  • 五子棋游戏
  • RK3588 android12修改manifest.xml配置HAL服务
  • Win11Debloat:让Windows系统重获新生的系统优化全攻略
  • ChatGPT电脑版安装包实战指南:从下载到部署的完整解决方案
  • 从HITRAN到HITEMP:用HAPI Python接口处理高温气体光谱的完整实战
  • Parsec VDD虚拟显示技术:重新定义多屏体验的创新方案
  • Android OTA解压终极指南:快速提取payload.bin文件的完整教程
  • Qwen3-ForcedAligner快速入门:3步完成音频与文本精准对齐
  • python校园志愿者服务活动管理系统vue3
  • 造火箭的辞职去放牛,彼得·蒂尔花20亿美元押注一个AI牛项圈
  • Vivado IP核实战:从Accumulator到XADC的10个高频使用技巧
  • 三步精通OpCore-Simplify:零基础搞定黑苹果EFI配置
  • 2026乐山特色餐饮礼盒评测深度解析 - 优质品牌商家
  • 道心网络安全学习笔记系列之好靶场的信息收集
  • Gcode文件处理中的常见错误及解决方案:从缓存不足到刀具补偿配置
  • RWKV7-1.5B-g1a效果展示:三类典型提示词(自我介绍/概念解释/文案压缩)生成质量集锦
  • 保姆级教程:手把手教你用Qwen-Image-Edit实现一句话魔法修图
  • Windows 10下Nacos-Server 2.4.0.1安装配置全攻略(含MySQL数据库连接避坑指南)
  • 如何一键下载番茄小说?终极离线阅读解决方案指南
  • RVC模型开源社区参与:从使用者到贡献者的成长路径
  • 2026中国大模型行业爆发!字节跳动128W年薪抢眼,你的机会来了!
  • 告别复杂配置!Realistic Vision V5.1一键部署,小白也能玩转AI摄影
  • 从内存操作到系统升级:RT-Thread临界区保护的5个典型场景避坑指南