首页 > 其他分享 >秃姐学AI系列之:批量归一化 + 代码实现

秃姐学AI系列之:批量归一化 + 代码实现

时间:2024-08-28 10:26:13浏览次数:8  
标签:nn AI self 秃姐学 moving 归一化 var mean

目录

批量归一化

核心想法

批归一化在做什么

总结

代码实现

从零实现

创建一个正确的BatchNorm层

应用BatchNorm于LeNet模型

简单实现

QA


批量归一化

训练深层神经网络是十分困难的,特别是在较短的时间内使他们收敛更加棘手。

因为数据在网络最开始,而损失在结尾。训练的过程是一个前向传播的过程,而参数更新是一个从后往前的更新方式。会导致越靠近损失的参数,梯度更新越大(因为是一些很小的值不断的乘,会变得越来越小),而最终导致后面的层训练的比较快

虽然底部层训练的慢,但是底部层一变化,所有的都得跟着变。导致最后的那些层需要重新学习多次!从而导致收敛变慢。

批量归一化(batch normalization),这是一种流行且有效的技术,可持续加速深层网络的收敛速度。 再结合 残差块,批量归一化 使得研究人员能够训练100层以上的网络。

虽然这个思想不新了,但是这个层确实是近几年出来的,大概在16年左右。当你要做很深的神经网络之后,会发现加入批量归一化,效果很好。基本成为现在不可避免的一个层了。

核心想法

当我们训练时,中间层中的变量(例如,多层感知机中的仿射变换输出)可能具有更广的变化范围:不论是沿着从输入到输出的层,跨同一层中的单元,或是随着时间的推移,模型参数的随着训练更新变幻莫测。

所以批量归一化的思想就是,我固定住分布,不管哪一层的 输出 还是 梯度,都符合某一个分布。使得网络没有特别大的转变,那么在学习细微的数值的时候就比较容易。当然具体什么分布,分布细微的东西可以再调整。

  • 固定小批量里面的 均值 方差

  • 然后在做额外的调整(可学习的参数)

式子中的 \mu _{B} 和 \sigma _{B} 是根据数据学出来的,而 \gamma 和 \beta 是一个可学习的参数

这两个参数的意义是  假设直接把数据设为均值为0,方差为1 不是那么适合,那就可以去需欸一个新的均值和方差去更加适应网络

但是会限制住 \gamma 和 \beta 不要变化的过于猛烈

  • 可学习的参数为 \gamma 和 \beta
  • 作用在
    • 全连接层和卷积层输出上,激活函数前
    • 全连接层和卷积层输入上,对输入做一个均值变化,使得输入的 方差、均值 比较好

为什么要放在激活函数之前:ReLU把你所有东西都变成正数,如果放在ReLU之后,批归一化层又给你算的奇奇怪怪的

可以认为批归一化是个线性变换

  • 对全连接层,作用在特征维度
  • 对于卷积层,作用在通道维度

批归一化在做什么

  • 最初论文是想用它来减少内部协变量转移
  • 后续有论文指出它可能就是通过在每个小批量里加入噪音来控制模型复杂度

认为 \hat{\mu _{B}} 是随机偏移(当前样本计算而来),\hat{\sigma _{B}} 是随机缩放(当前样本计算而来)

  • 因此没必要和丢弃法混合使用 

按照上面的思路的话,本来批归一化就是一个控制模型复杂度的方法,丢弃法也是。在 批归一化 上再加 丢弃,可能就没那么有用了。 

总结

  • 批量归一化:固定小批量中的均值和方差,然后学习出适合的偏移和缩放
  • 可以加速收敛速度,但一般不改变模型的精度
  • 在模型训练过程中,批量归一化不断调整神经网络的中间输出,使整个神经网络各层的中间输出值更加稳定。
  • 批量归一化在全连接层和卷积层的使用略有不同。
  • 批量归一化层 和 丢弃法 一样,在训练模式和预测模式下计算不同。
  • 批量归一化 有许多有益的副作用,主要是正则化。另一方面,”减少内部协变量偏移“的原始动机似乎不是一个有效的解释。

代码实现

从零实现

详细注释版

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

# 参数(X, 学习的参数:gamma、beta,预测用的全局的均值和方差:moving_mean、moving_var,极小值:eps,用来更新全局均值和方差的参数:momentum,通常取0.9 or 固定数字)
def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum):
    # 通过is_grad_enabled来判断当前模式是训练模式还是预测模式
    if not torch.is_grad_enabled():
        # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差,因为预测的时候可能没有批量,只有一张图片 or 一个样本
        X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps)
    else:
        assert len(X.shape) in (2, 4)
        if len(X.shape) == 2:   #X.shape = 2:全连接层
            # 使用全连接层的情况,计算特征维上的均值和方差
            mean = X.mean(dim=0)  #(1,n)的行向量,按行求均值 = 计算每一列的均值
            var = ((X - mean) ** 2).mean(dim=0)   # 依旧是按行,所以我们的方差也是行向量
        else:    # X.shape = 4:卷积层
            # 使用二维卷积层的情况,计算通道维上(axis=1)的均值和方差。
            # 这里我们需要保持X的形状以便后面可以做广播运算
            mean = X.mean(dim=(0, 2, 3), keepdim=True)  #(1,n,1,1)的形状
            var = ((X - mean) ** 2).mean(dim=(0, 2, 3), keepdim=True)  #(1,n,1,1)的形状
        # 训练模式下,用当前的均值和方差做标准化
        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层

我们现在可以创建一个正确的 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

应用BatchNorm于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))  # 没有必要对输出计算归一化

简单实现

除了使用我们刚刚定义的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))

QA

  • Xavier 和 batch normalization 以及其他正则化手段有什么区别

Xavier 是选取比较好的初始化方法,使得网络在开始的时候比较稳定,但不能保证之后

BN 保证在整个模型训练的时候都强行的在每一层后面做归一化(其实不应该叫normalization,学深度学习的数学没学好,应该是归一化,不是正则化)

  • BN是不是一般用于深层网络,浅层MLP加上BN效果好像不好

BN对深度网络效果更好,对于浅层网络没有太多太多用处,因为只有网络深度起来了才会出现我们上面提到的后面的层更快的训练好,从而被反复作废、训练、作废、训练的情况

  •  BN是做了线性变换,和加一个线性层有什么区别?

没啥太大的区别,只能说如果加了一个线性层,线性层可能不一定能学到 BN 学到的那些东西。只是一个线性层,做一个线性变换,没办法给数值做变化(均值为1,方差为0)

  • layerNorm 和 batchNorm的区别

一般来说,layerNorm 用于比较大的网络,作用在图上,batchNorm就为1,做不了batchNorm

标签:nn,AI,self,秃姐学,moving,归一化,var,mean
From: https://blog.csdn.net/m0_62415132/article/details/141573282

相关文章

  • C. Turtle and Good Pairs
    https://codeforces.com/contest/2003/problem/C题意:。。。思路:如果要使满足条件的有序对最多,那么首先如果两个字符相等,那么无论如何排列,最终的贡献值都不会变。再看字符不相等的情况,假如有aabbcc,那么abcabc总是优于aabbcc,因为如果一个字符出现了多次,那么像aab,bcc这种就会没......
  • ai取名生成器在哪?让你的命名过程更轻松
    创建产品或企业时,一个响亮的名字往往能让你一秒记住。但寻找一个完美的名字却是一项艰巨的任务,足以让人头疼不已。不过别担心,今天就要向你介绍一些ai取名生成器在线免费工具。只需一句话,它们就能为你提供一系列充满创意的名称。还在为取名而烦恼的小伙伴,快看过来吧~▌ai取......
  • 1-0.AI工具
    1.知道或使用过的AI大模型平台ChatGPT文心一言豆包。2.AI提升学习效率的例子解答疑惑:学习数学时,如高等数学中的多元函数积分问题,向AI提问,它会给出解题步骤和思路,帮助快速理解,同时比如在上学期学习计算机网络这一门课的时候,会涉及到许多新的名词,这时我会直接向AI......
  • 掀起社交娱乐新浪潮!AI如何应用到短视频APP?
    随着人工智能技术的迅速发展和全球社交媒体用户的增长,AI视频生成应用正逐渐成为短视频社交媒体中的关键工具。AI工具不仅可以提高内容的创造效率,还能为用户带来全新的互动体验。人工智能(AI)已经成为我们日常生活和工作中不可或缺的一部分,随着数据的爆炸性增长和计算能力的提......
  • AI产品经理知识体系:驾驭未来的核心技能
    随着人工智能(AI)的快速发展,AI产品经理的角色变得至关重要。他们不仅需要具备传统产品经理的技能,还必须深入理解AI技术的各个方面,才能推动AI技术在实际产品中的落地和应用。从最基础的技术知识,到跨领域的创新思维,AI产品经理的知识体系需要非常全面。本文将基于AI产品经理知识......
  • .NET与AI的碰撞:开启智能编程新纪元
    设想一下,你是一名.NET开发者,正为一个复杂的数据分析项目绞尽脑汁。突然,你的IDE仿佛有了生命,不仅能理解你的想法,还能自动生成大部分代码,甚至主动指出潜在的优化空间。这听起来像是科幻电影中的场景,但实际上,这种“魔法”正在成为现实!让我们一同探讨.NET世界正在经历的激动人心的变革......
  • SDKD 2024 Summer Training Contest E2补题
    SDKD2024SummerTrainingContestE2A-PaperWatering题意对x进行至多k次操作(平方或开方后向下取整),求可以得到多少不同的数。思路平方完一定不同,且平方完后一定能开方出整数,所以只用额外考虑开方后平方的情况。若开方再平方与原来不同,则答案加上当前变化数的次数,直到变......
  • Datawhale AI夏令营 Task 1 《深度学习详解》 - 1.1 通过案例了解机器学习的学
        一、学习目标通过具体案例深入理解机器学习的概念、工作原理以及在实际应用中的作用。二、主要内容案例介绍:详细阐述了图像识别、语音识别、自然语言处理等领域的具体案例,如人脸识别系统、智能语音助手、文本......
  • 聚星文社AIGC推文软件
    聚星文社AIGC推文软件是一款由聚星文社开发的推文编辑和发布工具。Docshttps://iimenvrieak.feishu.cn/docx/ZhRNdEWT6oGdCwxdhOPcdds7nof这款软件可以帮助用户快速编写和发布推文,提供了丰富的编辑功能和推广工具。用户可以在软件中编辑推文内容,同时也能够添加图片、链接......
  • Datawhale X 李宏毅苹果书AI夏令营 Task1打卡
    3.1局部极小值与鞍点3.1.1临界点及其分类参数对于损失函数的微分为零时,就无法进一步优化了,训练即停止了。所以我们把这些梯度为零的点统称为临界点。临界点可以分为两类:极值点(局部极小值)和鞍点。鞍点就是指那些梯度为零但不是局部极小值或者局部极大值的点,因为其在损失......