nn.LayerNorm的具体实现方法(通过公式复现)

以下通过LayerNorm的公式复现了Layer Norm的计算结果,以此来具体了解Layer Norm的工作方式
公 式 : y = x − E [ x ] V a r [ x ] + ϵ ∗ γ + β 公式:y = \frac{x - \mathrm{E}[x]}{ \sqrt{\mathrm{Var}[x] + \epsilon}} * \gamma + \beta y=Var[x]+ϵ xE[x]γ+β

1.只考虑最低维:每个维各自按公式计算即可,不和其他维度掺和

# LayerNorm
a = torch.tensor([[1,2,4,1],[6,3,2,4],[2,4,6,1]]).float()
nn.LayerNorm([4])(a)

结果:
tensor([[-0.8165, 0.0000, 1.6330, -0.8165],
[ 1.5213, -0.5071, -1.1832, 0.1690],
[-0.6509, 0.3906, 1.4321, -1.1717]])

# 使用公式实现,注意方差是有偏样本方差,系数是1/n
a = torch.tensor([[1,2,4,1],[6,3,2,4],[2,4,6,1]]).float()
b0 = (a[0] - torch.mean(a[0]))/((torch.var(a[0], unbiased=False)+1e-5)**0.5)#有偏样本方差
b1 = (a[1] - torch.mean(a[1]))/((torch.var(a[1], unbiased=False)+1e-5)**0.5)
b2 = (a[2] - torch.mean(a[2]))/((torch.var(a[2], unbiased=False)+1e-5)**0.5)
[b0, b1, b2]

结果:
[tensor([-0.8165, 0.0000, 1.6330, -0.8165]),
tensor([ 1.5213, -0.5071, -1.1832, 0.1690]),
tensor([-0.6509, 0.3906, 1.4321, -1.1717])]

2.考虑最低的2个维度:计算最低两维的12个元素的方差均值,然后按照公式算

a = torch.tensor([[1,2,4,1],[6,3,2,4],[2,4,6,1]]).float()
nn.LayerNorm([3,4])(a)

结果:
tensor([[-1.1547e+00, -5.7735e-01, 5.7735e-01, -1.1547e+00],
[ 1.7320e+00, 1.1921e-07, -5.7735e-01, 5.7735e-01],
[-5.7735e-01, 5.7735e-01, 1.7320e+00, -1.1547e+00]],
grad_fn=)

a = torch.tensor([[1,2,4,1],[6,3,2,4],[2,4,6,1]]).float()
b = (a - torch.mean(a))/((torch.var(a, unbiased=False)+1e-5)**0.5)

结果:
tensor([[-1.1547, -0.5773, 0.5773, -1.1547],
[ 1.7320, 0.0000, -0.5773, 0.5773],
[-0.5773, 0.5773, 1.7320, -1.1547]])

3. 多维的例子复现

import torch
from torch import nn
# 机算
x = torch.rand(2,3,4,5)
layer = nn.LayerNorm(5)
out1 = layer(x)
# 手算
mean = x.mean(axis=3).reshape(-1,x.shape[1],x.shape[2],1)
var = x.var(axis=3,unbiased=False).reshape(-1,x.shape[1],x.shape[2],1)
out2 = (x-mean)/((var+1e-5)**0.5)

# 机算
x = torch.rand(2,3,4,5)
layer = nn.LayerNorm([4,5])
out1 = layer(x)
# 手算:
mean = x.reshape(x.shape[0],x.shape[1],-1).mean(axis=2).reshape(-1,x.shape[1],1,1)
var = x.reshape(x.shape[0],x.shape[1],-1).var(axis=2,unbiased=False).reshape(-1,x.shape[1],1,1)
out2 = (x-mean)/((var+1e-5)**0.5)

4.其他参数细节见此博客:

https://blog.csdn.net/weixin_39228381/article/details/107939602

<think>嗯,用户让我解释这段PyTorch代码,看起来是Bottleneck类的__init__方法。首先,我需要确认这段代码属于哪个模型结构,因为Bottleneck通常出现在ResNet或类似的残差网络中。比如ResNet-50及更深的模型使用Bottleneck结构来减少计算量。 首先,用户提供的代码中有几个关键部分需要分析:构造函数参数、变量计算、各层定义。我得逐一拆解每个参数的作用,比如inplanes是输入通道数,planes是输出通道数,stride可能用于下采样。downsample可能是一个下采样模块,groups和base_width可能与分组卷积或宽度调整有关,比如在ResNeXt中。dilation是空洞卷积的参数,norm_layer是归一化层,默认是BatchNorm2d。 接下来,代码中的width计算部分需要解释。这里的公式可能与ResNeXt有关,因为groups参数被用来调整宽度,base_width默认为64,所以当groups改变时,width会调整,从而改变中间层的通道数。 然后,三个卷积层的结构:conv1是1x1卷积,负责降维;conv2是3x3卷积,处理空间特征,可能带有stride和dilation;conv3是1x1卷积,恢复通道数,并乘以expansion系数,这个expansion应该是Bottleneck类的属性,比如在ResNet中通常为4,这样最终输出的通道数是planes * 4。 各层的BN和ReLU激活函数的位置也需要说明,比如每个卷积层后接BN和ReLU,但最后一个ReLU是否在forward中应用要看是否有下采样。downsample的作用是在输入和输出维度不匹配时调整维度,通常在stride不为1或通道数变化时使用。 需要注意用户可能对Bottleneck结构的作用不太清楚,需要解释其设计目的,比如减少参数量,提高计算效率,同时保持模型深度。此外,可能涉及到的ResNet变体如ResNeXt中的分组卷积,通过groups和base_width参数调整模型的基数(cardinality)。 最后,确保解释清晰,结构分明,分步骤说明每个部分的功能,并联系到整体网络结构中的作用。用户可能是学习者或开发者,想了解代码细节以便修改或复现模型,因此需要准确且易于理解。</think>以下是对代码的逐步解释: 1. **类定义与继承** 这是`Bottleneck`类的构造函数,继承自PyTorch的`nn.Module`,是ResNet中用于构建深度残差网络的瓶颈结构模块。 2. **参数解析** ```python def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, base_width=64, dilation=1, norm_layer=None): ``` - `inplanes`: 输入通道数 (对应论文中的$C_{in}$) - `planes`: 基础输出通道数 (对应论文中的$C_{base}$) - `stride`: 卷积步长 (控制特征图下采样) - `downsample`: 下采样函数 (用于维度匹配) - `groups`: 分组卷积组数 (用于ResNeXt变体) - `base_width`: 基准宽度 (配合groups调整通道数) - `dilation`: 空洞卷积率 - `norm_layer`: 归一化层类型 (默认为BatchNorm) 3. **宽度计算** $$ \text{width} = \left\lfloor \frac{\text{planes} \times \text{base\_width}}{64} \right\rfloor \times \text{groups} $$ 这是ResNeXt的核心公式,通过`groups`和`base_width`控制网络基数(cardinality)。 4. **网络层定义** ```python self.conv1 = conv1x1(inplanes, width) # 1x1卷积降维 self.bn1 = norm_layer(width) self.conv2 = conv3x3(width, width, stride, groups, dilation) # 3x3主卷积 self.bn2 = norm_layer(width) self.conv3 = conv1x1(width, planes * self.expansion) # 1x1卷积升维 self.bn3 = norm_layer(planes * self.expansion) ``` - 采用经典的 $1\times1 \rightarrow 3\times3 \rightarrow 1\times1$ 结构 - `self.expansion`是类属性,通常为4 (使最终输出通道数为planes*4) 5. **辅助组件** ```python self.relu = nn.ReLU(inplace=True) # 原地激活节省内存 self.downsample = downsample # 残差连接时的维度匹配 self.stride = stride # 记录步长参数 ``` 6. **设计特点** - 通过1x1卷积实现通道维度的先压缩后扩展(如输入256维→压缩64维→处理→扩展256维) - 当`stride>1`时,`conv2`和`downsample`会同时进行下采样 - 支持空洞卷积(dilation)和分组卷积(groups),可用于实现Deeplab/ResNeXt等变体 该模块实现了标准的Bottleneck残差结构: $$ \text{Output} = \mathcal{F}(x) + \text{downsample}(x) $$ 其中$\mathcal{F}(x)$表示三个卷积层的复合操作,是ResNet在深层网络保持梯度流动的核心设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值