批量归一化

目录

1.批量归一化

2.代码

2.1从零开始实现

2.2简洁实现


1.批量归一化

正向是forward函数(从底向上),算backward的时候是从上往下。出现的问题就是梯度在上面的时候比较大,在下面就比较小,上面就会很快收敛,下面收敛很慢,每次更新下面的靠近数据的东西,这些东西会尝试去抽取那些比较底层的特征,比如局部边缘等很简单的纹理信息,上面就是一些高层语义的信息。因此上面更新收敛就很快,下面更新就很慢,但是每次下面一变上面得重新开始训练,所以上面就白学了。每次底部一变顶部就要跟着变,所以收敛就比较慢。

所以思考,我们能不能在学习底部得时候(改变底部的特征的时候),避免顶部会不断的重新训练。这就是批量归一化考虑的问题。

核心的思想就是,B就是小批量所有下标的索引,对小批量里的所有样本求和再除以批量大小就是均值,是可学习的参数。假设分布(均值为0,方差为1)不是那么适合的话,可以通过学习一个新的均值和方差来使得值对神经网络要好一点,但是会限定住的变化不要太猛烈。

批量归一化是个线性变换,把均值方差弄得比较好。对于全连接,假如是2维的输入,每一行是样本,每一列是特征,批归一化作用在特征上,对每一个特征计算一个标量的均值标量的方差,然后把特征变成均值为0方差为1。对每一个全连接的输出或者输入都做一个这样子的事情,而不是只作用在数据上,另外也会用学到的重新作用一下对均值和方差做一下矫正。

对于卷积层,作用在通道维,比如1*1的卷积层,等价于一个全连接层,对每一个像素,一个像素不是有多通道吗,如果通道数是100的话,这个像素其实是有一个长为100维的向量,可以认为向量是像素的一个特征。对于有高宽的输入来说,里面的每一个像素就是一个样本,对于卷积层来讲,假设输入是批量大小,乘以通道数,乘以高,乘以宽的话,样本数就是批量大小乘以高和宽,里面所有的像素都是一个样本。对应的通道就是特征。

2.代码

2.1从零开始实现

python 复制代码
import torch 
from torch import nn
from d2l import torch as d2l

"""从头开始实现一个具有张量的批量规范化层"""
def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum):
#moving_mean, moving_var可以看作个数据集上的均值和方差,而不是小批量上的均值和方差,
#做推理的时候用的。eps就是为了避免除0的一个东西。momentum就是用来更新moving_mean, moving_var
#的
    # 通过autograd来判断当前模式是训练模式还是预测模式
    if not torch.is_grad_enabled():
        # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差
        X_hat=(X-moving_mean)/torch.sqrt(moving_var+eps)
        #解释一下为什么不用批量的均值,因为做in_first的时候可能就是一张图片或者是一个样本
        #而不是一个批量。就算不出来
    else:
        assert len(X.shape) in (2,4)
        #要么等于2,就是全连接层,要么等于4就是卷积层
        if len(X.shape)==2:
            # 使用全连接层的情况,计算特征维上的均值和方差
            #对于2维输入。0维为样本,1维(列)为特征,就是计算每一列上的均值和方差
            mean=X.mean(dim=0)
            #按行求均值,就是对于每一列都算一个均值出来(跨行对同一列元素求均值)
            var=((X-mean)**2).mean(dim=0)
        else:
            # 使用二维卷积层的情况,计算通道维上(axis=1)的均值和方差。
            # 这里我们需要保持X的形状以便后面可以做广播运算,这里的mean是当前小批量的均值
            mean=X.mean((dim=0,2,3),keepdim=True)
            var=((X-mean)**2).mean((0,2,3),keepdim=True)
        # 训练模式下,用当前的均值和方差做标准化
        X_hat=(X-mean)/torch.sqrt(var+eps)
        # 更新移动平均的均值和方差
        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层
class BatchNorm(nn.module):
    def __init__(self,num_features,num_dims):
    # num_features:完全连接层的输出数量或卷积层的输出通道数。
    # num_dims:2表示完全连接层,4表示卷积层
        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):
    if self.moving_mean.device!=X.device
    # 如果X不在内存上,将moving_mean和moving_var复制到X所在显存上
    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"""
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.273, train acc 0.899, test acc 0.807
32293.9 examples/sec on cuda:0"""


        
python 复制代码
#让我们来看看从第一个批量规范化层中学到的拉伸参数gamma和偏移参数beta。
net[1].gamma.reshape((-1,)), net[1].beta.reshape((-1,))
"""结果输出:
(tensor([0.4863, 2.8573, 2.3190, 4.3188, 3.8588, 1.7942], device='cuda:0',
        grad_fn=<ReshapeAliasBackward0>),
 tensor([-0.0124,  1.4839, -1.7753,  2.3564, -3.8801, -2.1589], device='cuda:0',
        grad_fn=<ReshapeAliasBackward0>))"""

2.2简洁实现

python 复制代码
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))

d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
"""结果输出:
loss 0.267, train acc 0.902, test acc 0.708
50597.3 examples/sec on cuda:0
"""
相关推荐
成富6 分钟前
文本转SQL(Text-to-SQL),场景介绍与 Spring AI 实现
数据库·人工智能·sql·spring·oracle
CSDN云计算19 分钟前
如何以开源加速AI企业落地,红帽带来新解法
人工智能·开源·openshift·红帽·instructlab
艾派森30 分钟前
大数据分析案例-基于随机森林算法的智能手机价格预测模型
人工智能·python·随机森林·机器学习·数据挖掘
hairenjing112332 分钟前
在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序
android·人工智能·windows·macos·智能手机
小蜗子36 分钟前
Multi‐modal knowledge graph inference via media convergenceand logic rule
人工智能·知识图谱
SpikeKing1 小时前
LLM - 使用 LLaMA-Factory 微调大模型 环境配置与训练推理 教程 (1)
人工智能·llm·大语言模型·llama·环境配置·llamafactory·训练框架
黄焖鸡能干四碗1 小时前
信息化运维方案,实施方案,开发方案,信息中心安全运维资料(软件资料word)
大数据·人工智能·软件需求·设计规范·规格说明书
1 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习
ctrey_1 小时前
2024-11-4 学习人工智能的Day21 openCV(3)
人工智能·opencv·学习
攻城狮_Dream2 小时前
“探索未来医疗:生成式人工智能在医疗领域的革命性应用“
人工智能·设计·医疗·毕业