动手学深度学习7.5 批量规范化-笔记&练习(PyTorch)

本节课程地址:28 批量归一化【动手学深度学习v2】_哔哩哔哩_bilibili

本节教材地址:7.5. 批量规范化 — 动手学深度学习 2.0.0 documentation (d2l.ai)

本节开源代码:...>d2l-zh>pytorch>chapter_multilayer-perceptrons>batch-norm.ipynb


批量规范化

训练深层神经网络是十分困难的,特别是在较短的时间内使他们收敛更加棘手。 本节将介绍批量规范化(batch normalization) 1502.03167 (arxiv.org) ,这是一种流行且有效的技术,可持续加速深层网络的收敛速度。 再结合在 7.6节 中将介绍的残差块,批量规范化使得研究人员能够训练100层以上的网络。

训练深层网络

为什么需要批量规范化层呢?让我们来回顾一下训练神经网络时出现的一些实际挑战。

首先,数据预处理的方式通常会对最终结果产生巨大影响。 回想一下我们应用多层感知机来预测房价的例子( 4.10节)。 使用真实数据时,我们的第一步是标准化输入特征,使其平均值为0,方差为1。 直观地说,这种标准化可以很好地与我们的优化器配合使用,因为它可以将参数的量级进行统一。

第二,对于典型的多层感知机或卷积神经网络。当我们训练时,中间层中的变量(例如,多层感知机中的仿射变换输出)可能具有更广的变化范围:不论是沿着从输入到输出的层,跨同一层中的单元,或是随着时间的推移,模型参数的随着训练更新变幻莫测。 批量规范化的发明者非正式地假设,这些变量分布中的这种偏移可能会阻碍网络的收敛。 直观地说,我们可能会猜想,如果一个层的可变值是另一层的100倍,这可能需要对学习率进行补偿调整。

第三,更深层的网络很复杂,容易过拟合。 这意味着正则化变得更加重要。

批量规范化应用于单个可选层(也可以应用到所有层),其原理如下:在每次训练迭代中,我们首先规范化输入,即通过减去其均值并除以其标准差,其中两者均基于当前小批量处理。 接下来,我们应用比例系数和比例偏移。 正是由于这个基于批量统计的标准化,才有了批量规范化的名称。

请注意,如果我们尝试使用大小为1的小批量应用批量规范化,我们将无法学到任何东西。 这是因为在减去均值之后,每个隐藏单元将为0。 所以,只有使用足够大的小批量,批量规范化这种方法才是有效且稳定的。 请注意,在应用批量规范化时,批量大小的选择可能比没有批量规范化时更重要

从形式上来说,用 \mathbf{x} \in \mathcal{B} 表示一个来自小批量 \mathcal{B} 的输入,批量规范化 BN 根据以下表达式转换 \mathbf{x} :

\mathrm{BN}(\mathbf{x}) = \boldsymbol{\gamma} \odot \frac{\mathbf{x} - \hat{\boldsymbol{\mu}}_\mathcal{B}}{\hat{\boldsymbol{\sigma}}_\mathcal{B}} + \boldsymbol{\beta}. (7.5.1)

在 (7.5.1)中, \hat{\boldsymbol{\mu}}_\mathcal{B} 是小批量 \mathcal{B} 的样本均值, \hat{\boldsymbol{\sigma}}_\mathcal{B} 是小批量 \mathcal{B} 的样本标准差。 应用标准化后,生成的小批量的平均值为0和单位方差为1。 由于单位方差(与其他一些魔法数)是一个主观的选择,因此我们通常包含 拉伸参数(scale) \boldsymbol{\gamma} 和偏移参数(shift) \boldsymbol{\beta} ,它们的形状与 \mathbf{x} 相同。 请注意, \boldsymbol{\gamma} 和 \boldsymbol{\beta} 是需要与其他模型参数一起学习的参数。

由于在训练过程中,中间层的变化幅度不能过于剧烈,而批量规范化将每一层主动居中,并将它们重新调整为给定的平均值和大小(通过 \hat{\boldsymbol{\mu}}_\mathcal{B} 和 \hat{\boldsymbol{\sigma}}_\mathcal{B} )。

从形式上来看,我们计算出 (7.5.1)中的 \hat{\boldsymbol{\mu}}_\mathcal{B}  \hat{\boldsymbol{\sigma}}_\mathcal{B} ,如下所示:

\begin{aligned} \hat{\boldsymbol{\mu}}_\mathcal{B} &= \frac{1}{|\mathcal{B}|} \sum_{\mathbf{x} \in \mathcal{B}} \mathbf{x},\\ \hat{\boldsymbol{\sigma}}_\mathcal{B}^2 &= \frac{1}{|\mathcal{B}|} \sum_{\mathbf{x} \in \mathcal{B}} (\mathbf{x} - \hat{\boldsymbol{\mu}}_{\mathcal{B}})^2 + \epsilon.\end{aligned}

请注意,我们在方差估计值中添加一个小的常量 \epsilon > 0 ,以确保我们永远不会尝试除以零,即使在经验方差估计值可能消失的情况下也是如此。估计值 \hat{\boldsymbol{\mu}}_\mathcal{B}  \hat{\boldsymbol{\sigma}}_\mathcal{B} 通过使用平均值和方差的噪声(noise)估计来抵消缩放问题。 乍看起来,这种噪声是一个问题,而事实上它是有益的。

事实证明,这是深度学习中一个反复出现的主题。 由于尚未在理论上明确的原因,优化中的各种噪声源通常会导致更快的训练和较少的过拟合:这种变化似乎是正则化的一种形式。 在一些初步研究中, Bayesian Uncertainty Estimation for Batch Normalized Deep Networks (arxiv.org) 和 arxiv.org/pdf/1809.00846 分别将批量规范化的性质与贝叶斯先验相关联。 这些理论揭示了为什么批量规范化最适应 50∼100 范围中的中等批量大小的难题。

另外,批量规范化层在”训练模式“(通过小批量统计数据规范化)和“预测模式”(通过数据集统计规范化)中的功能不同。 在训练过程中,我们无法得知使用整个数据集来估计平均值和方差,所以只能根据每个小批次的平均值和方差不断训练模型。 而在预测模式下,可以根据整个数据集精确计算批量规范化所需的平均值和方差。

现在,我们了解一下批量规范化在实践中是如何工作的。

批量规范化层

回想一下,批量规范化和其他层之间的一个关键区别是,由于批量规范化在完整的小批量上运行,因此我们不能像以前在引入其他层时那样忽略批量大小。 我们在下面讨论这两种情况:全连接层和卷积层,他们的批量规范化实现略有不同。

全连接层

通常,我们将批量规范化层置于全连接层中的仿射变换和激活函数之间。 设全连接层的输入为x,权重参数和偏置参数分别为 \mathbf{W} 和 \mathbf{b} ,激活函数为 \phi ,批量规范化的运算符为 \mathrm{BN} 。 那么,使用批量规范化的全连接层的输出的计算详情如下:

\mathbf{h} = \phi(\mathrm{BN}(\mathbf{W}\mathbf{x} + \mathbf{b}) ).

回想一下,均值和方差是在应用变换的"相同"小批量上计算的。

卷积层

同样,对于卷积层,我们可以在卷积层之后和非线性激活函数之前应用批量规范化。 当卷积有多个输出通道时,我们需要对这些通道的“每个”输出执行批量规范化,每个通道都有自己的拉伸(scale)和偏移(shift)参数,这两个参数都是标量。 假设我们的小批量包含 m 个样本,并且对于每个通道,卷积的输出具有高度 p 和宽度 q 。 那么对于卷积层,我们在每个输出通道的 m \cdot p \cdot q 个元素上同时执行每个批量规范化。 因此,在计算平均值和方差时,我们会收集所有空间位置的值,然后在给定通道内应用相同的均值和方差,以便在每个空间位置对值进行规范化。

预测过程中的批量规范化

正如我们前面提到的,批量规范化在训练模式和预测模式下的行为通常不同。 首先,将训练好的模型用于预测时,我们不再需要样本均值中的噪声以及在微批次上估计每个小批次产生的样本方差了。 其次,例如,我们可能需要使用我们的模型对逐个样本进行预测。 一种常用的方法是通过移动平均估算整个训练数据集的样本均值和方差,并在预测时使用它们得到确定的输出。 可见,和暂退法一样,批量规范化层在训练模式和预测模式下的计算结果也是不一样的。


补充

批量归一化层

  • 可学习的参数为 \gamma 和 \beta
  • 作用在
    • 全连接层和卷积层输出上,激活函数前
    • 全连接层和卷积层输入上
  • 对全连接层,作用在特征维
  • 对于卷积层,作用在通道维

批量归一化在做什么?

  • 最初论文是想用它来减少内部协变量转移
  • 后续有论文支出它可能就是通过在每个小批量里加入噪音来控制模型复杂度,噪音指根据每个小批量中的样本计算的 \hat{\boldsymbol{\mu}}_\mathcal{B} (随机偏移)和 \hat{\boldsymbol{\sigma}}_\mathcal{B} (随机缩放)
  • 因此没必要跟丢弃法混合使用
  • 可以加速收敛速度,但一般不改变模型精度

(从零实现)

下面,我们从头开始实现一个具有张量的批量规范化层。

import torch
from torch import nn
from d2l import torch as d2l


def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum):
    # 通过is_grad_enabled来判断当前模式是训练模式还是预测模式
    if not torch.is_grad_enabled():
        # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差
        X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps)
    else:
        # len(X.shape) == 2是全连接层
        # len(X.shape) == 2是二维卷积层
        assert len(X.shape) in (2, 4)
        if len(X.shape) == 2:
            # 使用全连接层的情况,计算特征维上的均值和方差
            mean = X.mean(dim=0) 
            var = ((X - mean) ** 2).mean(dim=0)
        else:
            # 使用二维卷积层的情况,计算通道维上(axis=1)的均值和方差。
            # 这里我们需要保持X的形状以便后面可以做广播运算
            mean = X.mean(dim=(0, 2, 3), keepdim=True)
            var = ((X - mean) ** 2).mean(dim=(0, 2, 3), keepdim=True)
        # 训练模式下,用当前的均值和方差做标准化
        X_hat = (X - mean) / torch.sqrt(var + eps)
        # 更新移动平均的均值和方差,momentum一般取0.9
        moving_mean = momentum * moving_mean + (1.0 - momentum) * mean
        moving_var = momentum * moving_var + (1.0 - momentum) * var
    Y = gamma * X_hat + beta  # 缩放和移位
    return Y, moving_mean.data, moving_var.data

我们现在可以[创建一个正确的BatchNorm]。 这个层将保持适当的参数:拉伸gamma和偏移beta,这两个参数将在训练过程中更新。 此外,我们的层将保存均值和方差的移动平均值,以便在模型预测期间随后使用。

撇开算法细节,注意我们实现层的基础设计模式。 通常情况下,我们用一个单独的函数定义其数学原理,比如说batch_norm。 然后,我们将此功能集成到一个自定义层中,其代码主要处理数据移动到训练设备(如GPU)、分配和初始化任何必需的变量、跟踪移动平均线(此处为均值和方差)等问题。 为了方便起见,我们并不担心在这里自动推断输入形状,因此我们需要指定整个特征的数量。 不用担心,深度学习框架中的批量规范化API将为我们解决上述问题,我们稍后将展示这一点。

class BatchNorm(nn.Module):
    # num_features:完全连接层的输出数量或卷积层的输出通道数。
    # num_dims:2表示完全连接层,4表示卷积层
    def __init__(self, num_features, num_dims):
        super().__init__()
        if num_dims == 2:
            shape = (1, num_features)
        else:
            shape = (1, num_features, 1, 1)
        # 参与求梯度和迭代的拉伸和偏移参数,分别初始化成1和0
        self.gamma = nn.Parameter(torch.ones(shape))
        self.beta = nn.Parameter(torch.zeros(shape))
        # 非模型参数的变量初始化为0和1
        self.moving_mean = torch.zeros(shape)
        self.moving_var = torch.ones(shape)

    def forward(self, X):
        # 如果X不在内存上,将moving_mean和moving_var
        # 复制到X所在显存上
        if self.moving_mean.device != X.device:
            self.moving_mean = self.moving_mean.to(X.device)
            self.moving_var = self.moving_var.to(X.device)
        # 保存更新过的moving_mean和moving_var
        Y, self.moving_mean, self.moving_var = batch_norm(
            X, self.gamma, self.beta, self.moving_mean,
            self.moving_var, eps=1e-5, momentum=0.9)
        return Y

使用批量规范化层的 LeNet

为了更好理解如何[应用BatchNorm],下面我们将其应用(于LeNet模型)( 6.6节 )。 回想一下,批量规范化是在卷积层或全连接层之后、相应的激活函数之前应用的。

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5), BatchNorm(6, num_dims=4), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), BatchNorm(16, num_dims=4), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
    nn.Linear(16*4*4, 120), BatchNorm(120, num_dims=2), nn.Sigmoid(),
    nn.Linear(120, 84), BatchNorm(84, num_dims=2), nn.Sigmoid(),
    nn.Linear(84, 10))

和以前一样,我们将[在Fashion-MNIST数据集上训练网络]。 这个代码与我们第一次训练LeNet( 6.6节)时几乎完全相同,主要区别在于学习率大得多。

lr, num_epochs, batch_size = 1.0, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

输出结果:
loss 0.267, train acc 0.901, test acc 0.871
25013.1 examples/sec on cuda:0

让我们来看看从第一个批量规范化层中学到的[拉伸参数gamma和偏移参数beta]。

net[1].gamma.reshape((-1,)), net[1].beta.reshape((-1,))

输出结果:
(tensor([3.2875, 2.7298, 1.8175, 3.9650, 2.0454, 3.0062], device='cuda:0',
grad_fn=<ViewBackward0>),
tensor([-1.9988, -2.3935, -2.1846, 1.9865, 1.9519, 2.7971], device='cuda:0',
grad_fn=<ViewBackward0>))

[简明实现]

除了使用我们刚刚定义的BatchNorm,我们也可以直接使用深度学习框架中定义的BatchNorm。 该代码看起来几乎与我们上面的代码相同。

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
    nn.Linear(256, 120), nn.BatchNorm1d(120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.BatchNorm1d(84), nn.Sigmoid(),
    nn.Linear(84, 10))

下面,我们[使用相同超参数来训练模型]。 请注意,通常高级API变体运行速度快得多,因为它的代码已编译为C++或CUDA,而我们的自定义代码由Python实现。

lr, num_epochs, batch_size = 1.0, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

输出结果:
loss 0.268, train acc 0.901, test acc 0.851
43847.2 examples/sec on cuda:0

争议

直观地说,批量规范化被认为可以使优化更加平滑。 然而,我们必须小心区分直觉和对我们观察到的现象的真实解释。 回想一下,我们甚至不知道简单的神经网络(多层感知机和传统的卷积神经网络)为什么如此有效。 即使在暂退法和权重衰减的情况下,它们仍然非常灵活,因此无法通过常规的学习理论泛化保证来解释它们是否能够泛化到看不见的数据。

在提出批量规范化的论文中,作者除了介绍了其应用,还解释了其原理:通过减少内部协变量偏移(internal covariate shift)。 据推测,作者所说的内部协变量转移类似于上述的投机直觉,即变量值的分布在训练过程中会发生变化。 然而,这种解释有两个问题: 1、这种偏移与严格定义的协变量偏移(covariate shift)非常不同,所以这个名字用词不当; 2、这种解释只提供了一种不明确的直觉,但留下了一个有待后续挖掘的问题:为什么这项技术如此有效? 本书旨在传达实践者用来发展深层神经网络的直觉。 然而,重要的是将这些指导性直觉与既定的科学事实区分开来。 最终,当你掌握了这些方法,并开始撰写自己的研究论文时,你会希望清楚地区分技术和直觉。

随着批量规范化的普及,内部协变量偏移的解释反复出现在技术文献的辩论,特别是关于“如何展示机器学习研究”的更广泛的讨论中。 Ali Rahimi在接受2017年NeurIPS大会的“接受时间考验奖”(Test of Time Award)时发表了一篇令人难忘的演讲。他将“内部协变量转移”作为焦点,将现代深度学习的实践比作炼金术。 他对该示例进行了详细回顾 arxiv.org/pdf/1807.03341,概述了机器学习中令人不安的趋势。 此外,一些作者对批量规范化的成功提出了另一种解释:在某些方面,批量规范化的表现出与原始论文 arxiv.org/pdf/1805.11604中声称的行为是相反的。

然而,与机器学习文献中成千上万类似模糊的说法相比,内部协变量偏移没有更值得批评。 很可能,它作为这些辩论的焦点而产生共鸣,要归功于目标受众对它的广泛认可。 批量规范化已经被证明是一种不可或缺的方法。它适用于几乎所有图像分类器,并在学术界获得了数万引用。

小结

  • 在模型训练过程中,批量规范化利用小批量的均值和标准差,不断调整神经网络的中间输出,使整个神经网络各层的中间输出值更加稳定。
  • 批量规范化在全连接层和卷积层的使用略有不同。
  • 批量规范化层和暂退层一样,在训练模式和预测模式下计算不同。
  • 批量规范化有许多有益的副作用,主要是正则化。另一方面,”减少内部协变量偏移“的原始动机似乎不是一个有效的解释。

练习

1. 在使用批量规范化之前,我们是否可以从全连接层或卷积层中删除偏置参数?为什么?
解:
在使用批量规范化之前,偏置参数是必要的,用于调整输出层的基线,为了更好地拟合数据。
然而,批量规范化的出现改变了这一情况。批量规范化已经对数据进行均值和方差的调整,因此可以在某些情况下从后续层中删除偏置参数。

2. 比较LeNet在使用和不使用批量规范化情况下的学习率。

1)绘制训练和测试准确度的提高。

2)学习率有多高?

解:
1)使用批量规范化后,训练准确度提高0.076,测试准确度提高0.036。
2)不使用批量规范化的学习率为0.9,使用批量规范化的学习率为1.0。

3. 我们是否需要在每个层中进行批量规范化?尝试一下?
解:
不需要,批量规范化通常应用于卷积层之后,但在全连接层中可能不那么有效,输出层也不需要批量规范化。
去掉全连接层的批量规范化后,结果曲线平滑许多,代码如下:

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
    nn.Linear(256, 120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Linear(84, 10))
lr, num_epochs, batch_size = 1.0, 10, 256
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

输出结果:
loss 0.358, train acc 0.868, test acc 0.824
44300.5 examples/sec on cuda:0

4. 可以通过批量规范化来替换暂退法吗?行为会如何改变?

解:
不行,二者作用不同,如果用批量规范化替换暂退法,网络会失去防止过拟合的能力,导致在训练集上表现良好,但在测试集上性能下降。
对于此数据集和LeNet而言:
单独使用暂退法效果不佳,虽然过拟合很小,但训练和测试精度都很低。
联合使用批量规范化和暂退法,过拟合也减小,但训练和测试精度都不如单独使用批量规范化。

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
    nn.Linear(256, 120), nn.Sigmoid(),
    nn.Dropout(p=0.1),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Dropout(p=0.1),
    nn.Linear(84, 10))
lr, num_epochs, batch_size = 1.0, 10, 256
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

输出结果:
loss 0.593, train acc 0.767, test acc 0.758
48251.0 examples/sec on cuda:0

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
    nn.Linear(256, 120), nn.Sigmoid(),
    nn.Dropout(p=0.5),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Dropout(p=0.2),
    nn.Linear(84, 10))
lr, num_epochs, batch_size = 1.0, 10, 256
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

输出结果:
loss 0.465, train acc 0.827, test acc 0.799
43463.2 examples/sec on cuda:0

5. 确定参数betagamma,并观察和分析结果。

解:
每个BN层的beta和gamma参数如下。
beta控制规范化后数据的偏移,正值的beta会增加层的输出,而负值则会减少。而gamma控制着规范化后数据的缩放,gamma接近1,表示层的输出与输入的方差相似。
由下方输出数据可见:

  • 在第一个卷积层的beta参数中,有正有负,这表明某些通道的输出被增强,而另一些则被减弱。在第二个卷积层的beta参数中,也是有正有负,但大部分值接近于零,这可能表明模型在调整不同特征的重要性。
  • 第一个卷积层的gamma值都大于1,这可能意味着模型倾向于增强这些通道的响应。第二个卷积层的gamma值则更加分散,有的小于1,有的大于1,这表明模型在不同通道上进行了更细致的调整。
  • 全连接层的beta和gamma值显示出更多的变化,这可能是因为全连接层通常位于网络的深层,需要对特征进行更精细的调控。
print("1st conv_bn layer:\n beta:",net[1].bias.data, "\n gamma:", net[1].weight.data,"\n")
print("2nd conv_bn layer:\n beta:",net[5].bias.data, "\n gamma:", net[5].weight.data,"\n")
print("1st fc_bn layer:\n beta:",net[10].bias.data, "\n gamma:", net[10].weight.data,"\n")
print("2nd fc_bn layer:\n beta:",net[13].bias.data, "\n gamma:", net[13].weight.data,"\n")

输出结果:
1st conv_bn layer:
beta: tensor([ 2.1980, -0.9011, 1.2978, -2.6384, -1.5392, 3.4197], device='cuda:0')
gamma: tensor([3.3839, 2.9327, 2.3852, 2.3347, 2.8728, 3.1220], device='cuda:0')

2nd conv_bn layer:
beta: tensor([ 0.0993, 0.3042, -0.1298, 0.8404, 0.4195, -0.5486, 0.4503, -0.0330,
0.3153, 0.1534, 0.3460, -0.0427, -0.9228, 0.2183, -0.8369, -0.8550],
device='cuda:0')
gamma: tensor([2.4988, 2.1707, 2.5055, 1.8224, 1.7989, 2.1814, 1.7568, 1.4740, 2.2832,
1.5227, 1.9026, 1.0773, 0.9942, 1.8149, 1.8599, 1.7239],
device='cuda:0')

1st fc_bn layer:
beta: tensor([-0.0245, 0.1166, 0.0950, 0.0386, 0.1635, 0.5060, -0.1245, 0.1929,
-0.5530, 0.1490, -0.1133, -0.3032, 0.1465, -0.1490, -0.0126, 0.1115,
0.0447, 0.0299, 0.0776, 0.0092, 0.0822, 0.1516, -0.0094, 0.0277,
0.0135, -0.0333, 0.0192, -0.0500, -0.0984, -0.0631, -0.2383, -0.0535,
-0.0747, -0.0283, -0.0719, -0.0358, 0.0063, -0.0343, 0.0070, 0.0309,
0.1513, -0.0645, 0.0115, 0.0176, -0.1672, 0.0763, 0.0177, 0.0248,
-0.0912, -0.0083, 0.0803, -0.0313, 0.0480, 0.0199, -0.0794, -0.1338,
-0.0308, 0.0257, -0.0489, 0.1507, -0.0541, 0.2539, 0.0079, 0.1284,
0.0381, -0.0650, -0.0179, -0.0314, -0.0533, 0.0817, 0.0758, -0.0247,
0.1936, -0.0439, 0.3150, -0.1179, -0.1416, 0.0409, -0.0888, 0.1278,
-0.0250, 0.0298, 0.0142, -0.0693, 0.0463, -0.0195, 0.0362, -0.2857,
-0.1966, 0.0672, 0.0543, -0.0153, 0.0993, 0.1050, -0.0596, 0.1027,
0.0744, -0.0697, -0.0176, 0.0862, -0.1173, 0.0250, -0.3681, 0.1387,
0.1056, 0.0266, 0.1909, 0.0007, 0.1221, -0.0184, -0.0521, -0.0782,
0.0417, -0.0728, 0.0911, 0.1172, -0.0416, 0.1515, 0.0532, -0.0376],
device='cuda:0')
gamma: tensor([1.2756, 1.5418, 1.0708, 1.3135, 1.0670, 1.5910, 1.3557, 1.1501, 1.5525,
1.7481, 1.1082, 1.2013, 1.3689, 1.6460, 1.1081, 1.2684, 1.2203, 1.3251,
1.0263, 1.4561, 1.5575, 1.0961, 0.9092, 1.1701, 1.0966, 1.0168, 0.9743,
1.1996, 1.1066, 1.0645, 1.2349, 1.0441, 1.3283, 1.1595, 1.2555, 1.2969,
0.9651, 1.0274, 1.0515, 1.0634, 1.0933, 1.1851, 1.0766, 1.1201, 1.3546,
1.1821, 1.1834, 1.0578, 1.1297, 0.9273, 1.3032, 1.0791, 1.3457, 1.2220,
1.1394, 1.4909, 1.1089, 1.4429, 1.2422, 1.2053, 1.0274, 1.2676, 1.1667,
1.2373, 1.2177, 1.1584, 0.9412, 1.1183, 1.0796, 1.1262, 1.0219, 1.0995,
1.2281, 0.9920, 1.3034, 1.5340, 1.2909, 1.0649, 1.2184, 1.1837, 1.1589,
1.0227, 1.1586, 1.4437, 1.0756, 1.3237, 1.3630, 1.5791, 1.2489, 1.3890,
1.1391, 1.0462, 1.4613, 1.1840, 0.9989, 1.2002, 1.3353, 1.1928, 1.1468,
1.1662, 1.0829, 1.1969, 1.6380, 1.1627, 1.2654, 1.2967, 1.3129, 0.9896,
1.8259, 0.9502, 1.1435, 1.0486, 1.0993, 1.0852, 1.0173, 1.2393, 1.2332,
1.4734, 1.2852, 1.2255], device='cuda:0')

2nd fc_bn layer:
beta: tensor([-0.2683, -0.3347, -0.3368, -0.3290, -0.5891, -0.1135, -0.4971, -0.3300,
-0.1112, -0.1213, -0.3834, -0.2688, -0.3561, -0.4926, -0.3819, -0.1261,
-0.3548, -0.3510, -0.2811, -0.3573, -0.4327, -0.0742, -0.4484, -0.3090,
-0.1523, -0.3879, -0.2483, -0.3269, -0.4357, -0.3108, -0.3457, -0.3665,
-0.4416, -0.4562, -0.3017, -0.3621, -0.2665, -0.3088, -0.2892, -0.2357,
-0.3948, -0.4590, -0.4877, -0.2191, -0.3122, -0.3441, -0.4283, -0.1788,
-0.1257, -0.3985, -0.4673, -0.2734, -0.7079, -0.4597, -0.4621, -0.3166,
-0.3499, -0.3710, -0.3410, -0.3492, -0.4310, -0.4136, -0.3882, -0.2499,
-0.4240, -0.2790, -0.3686, -0.3919, -0.3837, -0.1865, -0.1255, -0.2355,
-0.2680, -0.1754, -0.4764, -0.3715, -0.3977, -0.3845, -0.4796, -0.2501,
-0.2424, -0.2615, -0.4265, -0.0814], device='cuda:0')
gamma: tensor([1.6097, 1.6735, 2.0016, 1.5628, 2.0836, 2.3211, 1.6245, 1.6085, 1.7604,
1.9371, 1.5817, 1.7049, 1.7142, 2.0352, 1.6485, 1.9554, 1.8731, 1.4179,
1.5706, 1.3221, 1.7860, 1.7036, 1.5655, 1.4689, 1.8135, 1.6019, 1.7513,
1.3328, 2.1763, 1.7020, 1.6603, 1.9970, 1.9163, 1.9790, 1.7699, 1.8940,
1.7004, 1.8233, 1.5174, 1.7839, 1.6828, 1.6582, 1.6630, 1.5442, 1.6966,
1.6659, 1.8867, 1.6736, 1.7121, 1.4588, 1.6776, 1.5630, 1.8211, 1.7960,
1.6351, 1.6127, 1.7675, 1.7239, 1.3793, 1.6819, 1.5891, 1.5426, 1.7509,
1.5427, 1.7553, 1.6037, 1.5818, 1.5961, 1.6122, 1.6440, 1.6181, 1.5104,
1.7219, 1.6096, 1.9389, 1.6409, 1.7378, 1.3741, 1.7777, 1.7020, 1.7389,
1.6699, 1.4464, 2.0087], device='cuda:0')

6. 查看高级API中有关BatchNorm的在线文档,以查看其他批量规范化的应用。
解:
1)torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)对2D或3D输入应用批量规范化。 https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm1d.html#batchnorm1d

2)torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)对4D输入应用批量规范化。 https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm2d.html#batchnorm2d

3)torch.nn.BatchNorm3d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)对5D输入应用批量规范化。 https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm3d.html#batchnorm3d

4)torch.nn.SyncBatchNorm(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, process_group=None, device=None, dtype=None)对N维输入应用批量规范化。相比BathchNorm,SyncBatchNorm可以利用分布式通讯接口在各卡间进行通讯,支持多卡训练。 https://pytorch.org/docs/stable/generated/torch.nn.SyncBatchNorm.html#syncbatchnorm

5)torch.nn.LazyBatchNorm1d(eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)具有延后初始化功能的torch.nn.BatchNorm1d。 https://pytorch.org/docs/stable/generated/torch.nn.LazyBatchNorm1d.html#lazybatchnorm1d

6)torch.nn.LazyBatchNorm2d(eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)具有延后初始化功能的torch.nn.BatchNorm2d。 https://pytorch.org/docs/stable/generated/torch.nn.LazyBatchNorm2d.html#lazybatchnorm2d

7)torch.nn.LazyBatchNorm3d(eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)具有延后初始化功能的torch.nn.BatchNorm3d。 https://pytorch.org/docs/stable/generated/torch.nn.LazyBatchNorm3d.html#lazybatchnorm3d

7. 研究思路:可以应用的其他“规范化”转换?可以应用概率积分变换吗?全秩协方差估计可以么?
解:
以下回答来自kimi:
概率积分变换是一种统计技术,可以将任何分布的随机变量转换为均匀分布。在深度学习中,概率积分变换可以用于规范化输入数据,但这不是常规做法,且可能需要进一步研究以确定其有效性。
全秩协方差估计通常用于统计分析中,以估计随机变量的协方差结构。在深度学习中,可以使用协方差估计来分析特征之间的相关性,但直接应用于模型规范化可能不是标准做法。

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

scdifsn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值