深度学习——批量归一化(笔记)

2023-05-16

主流的卷积网络基本都设计批量归一化这个层

1.为什么要批量归一化?

 ①网络层很深,数据在底层,损失函数在最顶层。反向传播后,顶层的梯度大,所以顶层训练的较快。数据在底层,底层的梯度小,底层训练的慢。(学习率不变)

②数据在最底部,底部层的训练较慢。

Ⅰ底部层一变化,所有的层都要跟着变化

Ⅱ顶部的层需要重新学习很多次

Ⅲ导致收敛变慢

③批量归一化可以在学习底部层的时候避免变化顶部层。

2.批量归一化的思想:

①固定小批量里面的均值和方差(因为方差和均值的分布在不同的层有变化 )

 ②可学习参数:如果均值为0,方差为1的分布不是很适合的话,可以学习一个均值和方差使得对网络更好一些。

 

3.批量归一化层

①可学习的参数γ和β

②作用在:

Ⅰ全连接层和卷积层输出之后,(批量归一化层这里:线性变换,均值方差拉的比较好)激活函数之前

Ⅱ全连接层和卷积层的输入上

③对全连接层,作用在特征维 ( 二维的输入,每一行就是样本,每一列就是特征。全连接层对每一个特征计算标量的均值和方差。不一样的是每一个全连接层的输入和输出都做这件事,不只是做在数据上面。而是重新用学到的γ和β,对方差和均值做校验)

④对卷积层,作用在通道维(1*1卷积等价于全连接层,对于每一个像素有多通道。比如有一个像素对应通道是100,这个像素有100维的向量。向量就是这个像素的特征。所以在输入的时候,每一个像素就是一个样本。

卷积层输入:批量大小*高*宽*通道数  样本数:批量大小*高*宽。所以就是所有的像素当作样本,一个像素对应的所有通道当作特征)

 4.批量归一化的作用是什么

①最初的论文减少内部协变量的转移

②后续论文,通过每个小批量里加入噪音控制模型复杂度

 加入随机偏移和方差,然后通过学到的稳定的均值方差,使得变化不剧烈

③没必要和丢弃法一起

【总结】

①批量归一化固定小批量中的均值和方差,然后学习出适合偏移和缩放

②可以加速收敛速度,学习率可以变大,但一般不改变模型的精度

【代码实现】

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


# X是输入 ,gamma-beta可学习参数,moving_mean,moving_var全局的均值和方差,是在预测的时候使用,eps避免分母出现0,momentum更新移动的平均和方差通常取0.9
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:  # 训练模式下
        assert len(X.shape) in (2, 4)  # 2是全连接层(两个维度,batch和全连接层的大小) 4是卷积层(batch,通道数,高,宽)
        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)
        # 更新移动平均的均值和方差
        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


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模型
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))

# 训练
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())
d2l.plt.show()

简易实现

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

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))

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())
d2l.plt.show()

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深度学习——批量归一化(笔记) 的相关文章

随机推荐

  • 数组的初始化 array initializer is not allowed here

    此处不允许使用数组初始值设定项 array initializer is not allowed here 数组的使用分声明和初始化两部分 xff0c 两者可同时进行 xff0c 也可分开进行 int array 声明 array 61 n
  • Maven打包所有依赖到一个可执行jar中,将外部依赖加入到classPath中

    首先说一下比较常用的两种打包方式 xff1a 前提 xff1a maven构建可执行jar包时 xff0c 如果项目依赖了pom中定义的dependency之外的外部jar包 xff0c maven jar plugin默认是不会把这 些额
  • postgresql数据库|数据库实操----表复制详解

    前言 xff1a 通常情况下 xff0c 我们对数据库的增删改查的时候 xff0c 为了确保数据的安全 xff0c 需要备份表 xff0c 那么 xff0c 一种方法是通过pg dump 这个工具做SQL转储操作 xff0c 此方法比较复杂
  • Centos7 配置防火墙 firewall

    一 firewall 1 从CentOS7开始 xff0c 默认使用firewall来配置防火墙 xff0c 没有安装iptables xff08 旧版默认安装 xff09 2 firewall的配置文件是以xml的格式 xff0c 存储在
  • Windows多媒体开发框架介绍

    Windows 多媒体开发框架介绍 欢迎来到 Windows 的多媒体开发世界2D 绘图 API1 GDI2 GDI 43 3 Direct2D 音频 API1 MME2 DirectSound3 Windows Core AudioCor
  • 【Ubuntu】在QT运行程序后无结果显示,只有终端运行的解决办法

    转自 http stackoverflow com questions 3255035 qt creator run in terminal https bugs launchpad net ubuntu 43 source qtcreat
  • 【蓝桥杯嵌入式】关于CT117E下载程序出问题解决方案(含keil mdk4和keil mdk5移植)

    废话 万事开头难 xff0c 然后中间难 xff0c 最后难 寒假刚开始 xff0c 我看到了蓝桥杯嵌入式 很快啊 xff01 报名 买板一气呵成 没想到这块CT117E板子它不讲武德 xff0c 来骗 xff0c 来偷袭我这个二十岁的小伙
  • c语言冒泡排序详解(分析每一步,附代码)

    冒泡排序 xff08 Bubble Sort xff09 xff0c 是一种计算机科学领域的较简单的排序算法 它重复地走访过要排序的元素列 xff0c 依次比较两个相邻的元素 xff0c 如果顺序 xff08 如从大到小 首字母从Z到A x
  • 解决maven update project 后项目jdk变成1.5

    一 问题描述 在Eclipse中新建了一个Maven工程 然后更改JDK版本为1 7 结果每次使用Maven gt Update project的时候JDK版本都恢复成1 5 二 原因分析 Maven官方文档有如下描述 xff1a 编译器插
  • C语言——整型和浮点型混合运算

    1 int和double混合运算 C语言int和double混合运算时 xff0c 会自动将int类型的数据转换为double类型的数据 xff0c 最后得到的结果也是double类型 如下例 xff1a double a 61 4 0 9
  • C语言——函数指针

    目录 1 函数指针概念 1 1 函数指针的声明 1 2 函数指针的定义 1 3 使用typedef定义函数指针的别名 1 4 将常数转换为函数指针 1 5 函数指针的调用 1 6 将函数指针作为函数的传入参数 2 简单的例子 1 函数指针概
  • C语言——多线程基础(pthread)

    目录 1 线程的定义以及线程的创建 1 1 线程和进程的概念 1 2 使用pthread create 函数创建进程 2 使用pthread join 等待线程结束 2 1 使用pthread join 等待线程结束 2 1 使用pthre
  • C++——双端队列(deque)

    1 双端队列 xff08 deque xff09 双端队列 xff08 deque xff09 是队列的一种变形 xff0c 一般队列只能在队尾添加元素 xff08 push xff09 xff0c 在队首删除元素 xff08 pop xf
  • Linux|集群初始化脚本--osiniit.sh简介

    前言 xff1a 不管是什么部署 xff0c 前期的准备工作通常都是比较繁琐的 xff0c 但同时这些工作又具有程式化的特征 xff0c 也就是说都是有一定的流程的 xff0c 固定的步骤的 OK xff0c shell脚本处理这样的程式问
  • C++——优先级队列(priority_queue)

    目录 1 优先级队列 xff08 priority queue xff09 1 1 基本概念 1 2 优先级队列的定义 1 3 通过重写仿函数来支持自定义数据类型 1 4 通过运算符重载来支持自定义比较函数 1 5 优先级队列的基本操作 2
  • 操作系统——进程状态

    进程从创建到执行 xff0c 再到执行完毕销毁的过程中 xff0c 经历了不同的进程状态 xff0c 进程状态部分取决于进程当前的活动 xff0c 可以将进程状态分为 xff08 1 xff09 三状态模型 xff1b xff08 2 xf
  • 操作系统——进程调度

    目录 1 基本概念 1 1 CPU I O执行周期 1 2 CPU调度程序 xff08 CPU scheduler xff09 1 3 进程状态模型 1 4 抢占调度 1 5 调度程序 xff08 dispatcher xff09 1 6
  • C++实现优先级队列模板类

    1 优先级队列 1 1 基本原理 仿照C 43 43 STL 中的优先级队列priority queue xff0c 主要实现以下功能 xff1a 向队列中添加元素后 xff0c 队列自动调整 xff0c 保证队列中优先级最高的元素在队列头
  • C++通过WMI获取计算机电池电压的程序以及我出现的问题解决

    C 43 43 通过WMI获取计算机电池电压的程序以及我出现的问题解决 ConsoleApplication1 cpp 定义控制台应用程序的入口点 include stdafx h include DXGI h include vector
  • 深度学习——批量归一化(笔记)

    主流的卷积网络基本都设计批量归一化这个层 1 为什么要批量归一化 xff1f 网络层很深 xff0c 数据在底层 xff0c 损失函数在最顶层 反向传播后 xff0c 顶层的梯度大 xff0c 所以顶层训练的较快 数据在底层 xff0c 底