QARepVGG--含demo实现

news2025/2/26 18:49:21

文章目录

  • 前言
  • 引入
  • Demo实现
  • 总结


前言

 在上一篇博文RepVGG中,介绍了RepVGG网络。RepVGG 作为一种高效的重参数化网络,通过训练时的多分支结构(3x3卷积、1x1卷积、恒等映射)和推理时的单分支合并,在精度与速度间取得了优秀平衡。然而,其在低精度(如INT8)量化后常出现显著精度损失。
 本文将要介绍的QARepVGG(Make RepVGG Greater Again: A Quantization-aware Approach)的提出正是为了解决这一问题。其核心贡献在于基础的Block设计:
在这里插入图片描述

引入

 文章做了详细的消融实验来一步一步的推理出这种结构,本文在此不多做赘述。只大概提一下:RepVGG其实是由三个单元构成:权重、BN和ReLU。卷积操作一般不会影响权重值的改变,基本服从0~1分布;而根据BN层的公式,会出现一个乘法项,导致方差可能发生改变;另外,如果输入的数值范围很大,经过ReLU也会产生大的方差项,导致量化困难。
 因此,QARepVGG去掉了BN层,并在三个分支后新加了一个BN层来将分布改成一个量化友好的分布。
 当然,建议读者阅读原论文,好多实验的设计跟分析很透彻。

Demo实现

 本文旨在复现一个QARepVGG Block,读者可一键运行:

import torch
import torch.nn as nn

class QARepVGGBlock(nn.Module):
    def __init__(self, in_channels, out_channels):
        super().__init__()
        assert in_channels == out_channels, "输入输出通道必须相同!"
        
        # 分支1:3x3卷积 + BN
        self.conv3x3 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False)
        self.bn3x3 = nn.BatchNorm2d(out_channels)
        
        # 分支2:1x1卷积(无BN)
        self.conv1x1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False)
        
        # 分支3:恒等映射(无BN)
        self.identity = nn.Identity()  # 直接传递输入
        
        # 合并后的BN层
        self.final_bn = nn.BatchNorm2d(out_channels)
        
        # 初始化权重(关键!)
        self._init_weights()

    def _init_weights(self):
        """显式初始化权重"""
        nn.init.kaiming_normal_(self.conv3x3.weight, mode='fan_out', nonlinearity='relu')
        nn.init.zeros_(self.conv1x1.weight)  # 初始化为零,与恒等映射互补

    def forward(self, x):
        # 分支1:3x3卷积 + BN
        branch3x3 = self.bn3x3(self.conv3x3(x))
        # 分支2:1x1卷积
        branch1x1 = self.conv1x1(x)
        # 分支3:恒等映射
        branch_id = self.identity(x)
        # 合并后通过最终BN
        out = self.final_bn(branch3x3 + branch1x1 + branch_id)
        return out

    def reparameterize(self):
        """将多分支合并为单一3x3卷积,并融合BN参数"""
        # 1. 将各分支转换为等效3x3卷积
        # 分支1:3x3卷积 + BN3x3
        kernel3x3, bias3x3 = self._fuse_conv_bn(self.conv3x3, self.bn3x3)
        
        # 分支2:1x1卷积(无BN),填充为3x3
        kernel1x1 = self._pad_1x1_to_3x3(self.conv1x1.weight)
        bias1x1 = torch.zeros_like(bias3x3)  # 无偏置
        
        # 分支3:恒等映射(视为1x1单位矩阵卷积,填充为3x3)
        identity_kernel = torch.eye(self.conv3x3.in_channels, device=self.conv3x3.weight.device)
        identity_kernel = identity_kernel.view(self.conv3x3.in_channels, self.conv3x3.in_channels, 1, 1)
        kernel_id = self._pad_1x1_to_3x3(identity_kernel)
        bias_id = torch.zeros_like(bias3x3)
        
        # 2. 合并所有分支的权重和偏置
        merged_kernel = kernel3x3 + kernel1x1 + kernel_id
        merged_bias = bias3x3 + bias1x1 + bias_id
        
        # 3. 融合最终BN层参数
        scale = self.final_bn.weight / (self.final_bn.running_var + self.final_bn.eps).sqrt()
        merged_kernel = merged_kernel * scale.view(-1, 1, 1, 1)
        merged_bias = scale * (merged_bias - self.final_bn.running_mean) + self.final_bn.bias
        
        # 4. 构建合并后的卷积层
        merged_conv = nn.Conv2d(
            self.conv3x3.in_channels,
            self.conv3x3.out_channels,
            kernel_size=3,
            padding=1,
            bias=True
        )
        merged_conv.weight.data = merged_kernel
        merged_conv.bias.data = merged_bias
        return merged_conv

    def _fuse_conv_bn(self, conv, bn):
        """融合卷积和BN的权重与偏置"""
        kernel = conv.weight
        running_mean = bn.running_mean
        running_var = bn.running_var
        gamma = bn.weight
        beta = bn.bias
        eps = bn.eps

        std = (running_var + eps).sqrt()
        scale_factor = gamma / std

        fused_kernel = kernel * scale_factor.view(-1, 1, 1, 1)
        fused_bias = beta - running_mean * scale_factor
        return fused_kernel, fused_bias

    def _pad_1x1_to_3x3(self, kernel):
        """将1x1卷积核填充为3x3(中心为原权重,其余为0)"""
        if kernel.size(-1) == 1:
            padded = torch.zeros(kernel.size(0), kernel.size(1), 3, 3, device=kernel.device)
            padded[:, :, 1, 1] = kernel.squeeze()
            return padded
        return kernel 


def test_qarepvgg():
    torch.manual_seed(42)

    # 输入数据(小方差加速BN收敛)
    x = torch.randn(2, 3, 4, 4) * 0.1

    # 初始化模块
    block = QARepVGGBlock(3, 3)

    # 训练模式:更新BN统计量
    block.train()
    for _ in range(100):  # 充分训练
        y = block(x)
        y.sum().backward()  # 伪反向传播

    # 推理模式:合并权重
    block.eval()
    with torch.no_grad():
        # 原始输出
        orig_out = block(x)

        # 合并后的卷积
        merged_conv = block.reparameterize()
        merged_out = merged_conv(x)

    # 打印关键数据
    print("out:", orig_out.mean().item())
    print("merge:", merged_out.mean().item())
    print("diff:", torch.abs(orig_out - merged_out).max().item())

    # 验证一致性(容差1e-6)
    assert torch.allclose(orig_out, merged_out, atol=1e-6), f"合并失败!最大差值:{torch.abs(orig_out - merged_out).max().item()}"
    print("✅ 测试通过!")

test_qarepvgg()

在这里插入图片描述

总结

 欢迎留言交流讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2306517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

kotlin 知识点 七 泛型的高级特性

对泛型进行实化 泛型实化这个功能对于绝大多数Java 程序员来讲是非常陌生的,因为Java 中完全没有这个概 念。而如果我们想要深刻地理解泛型实化,就要先解释一下Java 的泛型擦除机制才行。 在JDK 1.5之前,Java 是没有泛型功能的,…

Transformer LLaMA

一、Transformer Transformer:一种基于自注意力机制的神经网络结构,通过并行计算和多层特征抽取,有效解决了长序列依赖问题,实现了在自然语言处理等领域的突破。 Transformer 架构摆脱了RNNs,完全依靠 Attention的优…

Qt学习 网络编程 TPC通信

一 基本网络端口 1 网络编程基本概念 通讯方式:信息的通讯时通过网络来进行,通讯方式有两种,TCP和UDP通信,TCP通讯是专用通道,指定某个信息只能走某个通道,UDP则是非专用通道,比如一个车队&am…

ESP32-S3 实战指南:BOOT-KEY 按键驱动开发全解析

一、基础知识 本篇我们使用 BOOT 按键来学习一下 GPIO 功能,首先补充一下相关术语介绍。 1、GPIO(General Purpose Input/Output) GPIO 是微控制器上的通用引脚,既可以作为输入(读取外部信号)&#xff0…

ssh配置 远程控制 远程协作 github本地配置

0.设备版本 windows11 ubuntu24.0.4 1.1 在 Linux 上启用 SSH 服务 首先,确保 Linux 计算机上安装并启用了 SSH 服务。 安装和启动 OpenSSH 服务(如果未安装) # 在终端安装 OpenSSH 服务(如果尚未安装) sudo apt …

C++知识整理day9——继承(基类与派生类之间的转换、派生类的默认成员函数、多继承问题)

文章目录 1.继承的概念和定义2.基类与派生类之间的转换3.继承中的作用域4.派生类的默认成员函数5.实现一个不能被继承的类6.继承与友元7.继承与静态成员8.多继承和菱形继承问题8.1 继承分类及菱形继承8.2 虚继承 1.继承的概念和定义 概念: 继承(inheritance)机制是⾯…

2024年国赛高教杯数学建模A题板凳龙闹元宵解题全过程文档及程序

2024年国赛高教杯数学建模 A题 板凳龙闹元宵 原题再现 “板凳龙”,又称“盘龙”,是浙闽地区的传统地方民俗文化活动。人们将少则几十条,多则上百条的板凳首尾相连,形成蜿蜒曲折的板凳龙。盘龙时,龙头在前领头&#x…

华为认证考试证书下载步骤(纸质+电子版)

华为考试证书可以通过官方渠道下载相应的电子证书,部分高级认证如HCIE还支持申请纸质证书。 一、华为电子版证书申请步骤如下: ①访问华为培训与认证网站 打开浏览器,登录华为培训与认证官方网站 ②登录个人账号 在网站首页,点…

【Android】用 chrome://inspect/#devices 调试H5页面

通常做Android开发的过程中,不可避免的需要遇到去与H5交互,甚至有时候需要去调试H5的信息。 这里分享一下Android工程里如何调试H5页面信息: 直接在浏览器地址栏输入 : chrome://inspect/#devices 直接连接手机usb,打开开发者模式…

贪心算法精品题

1.找钱问题 本题的贪心策略在于我们希望就可能的保留作用大的5元 class Solution { public:bool lemonadeChange(vector<int>& bills) {std::map<int ,int> _map;for(auto ch:bills){if(ch 5) _map[ch];else if(ch 10){if(_map[5] 0) return false;else{_m…

WEB1~6通杀

##解题思路 这六道题&#xff0c;通杀了&#xff0c;只因为是PHP的特性 来&#xff0c;看web6&#xff0c;过滤最复杂的正则&#xff0c;而且不能解析成大于999的值&#xff0c;但是&#xff0c;php是弱类型的语言&#xff0c;我只要输入任意字符数字&#xff0c;最终值就为0&…

孜然单授权系统V2.0PHP授权系统

孜然单授权V1.0系统&#xff0c;延续了2022年开发的孜然多应用授权系统V2.0 变更&#xff1a;多应用变单系统&#xff0c;去除没用的垃圾代码&#xff0c;从0开发&#xff0c;去除了一些没用的功能 完善了开发文档&#xff0c;之前那套是我写着玩的屎山代码&#xff0c;V1.0将展…

Apache SeaTunnel 构建实时数据同步管道(最新版)

文章作者 王海林 白鲸开源 数据集成引擎研发 Apache SeaTunnel Committer & PMC Member&#xff0c;Apache SkyWalking Committer&#xff0c;多年平台研发经验&#xff0c;目前专注于数据集成领域。 导读 在当今数字化快速发展的时代&#xff0c;数据已然成为企业决策…

服务器离线部署DeepSeek

目标 本次部署的目标是在本地服务器上部署DeepSeek。但是该服务不能连接外网&#xff0c;因此只能使用离线部署的方式。为了一次完成部署。现在云服务器上进行尝试。 云服务器部署尝试 云服务器配置 CentOS72080Ti 11GB 安装准备 1、上传iso并配置为本地yum源 安装前先将…

ComfyUI:Stable Diffusion 及 LoRA、VAE 、ControlNet模型解析

目录 Stable Diffusion流程 扩散过程 去噪过程 checkpoints LoRA LoRA 位置与结构 LoRA 层与原层的关系 LoRA 层的参数拆解 VAE 训练特定 VAE 时更新的参数部分 ControlNet ControlNet 位置与结构 ControlNet 的训练过程 ControlNet 的参数处理与信息融合 Contr…

微信小程序:多菜单栏设计效果

一、实现效果 二、代码 wxml 编辑前端界面,步骤 菜单逻辑: 逐步取出数组中的项,首先取出顶部菜单项,然后选中后取出选中的底部数据(左侧菜单+右侧内容),然后点击左侧菜单取出选中的左侧菜单对应的右侧内容 ①这里我的数据是全部封装到一个数组对象的,首先我的循环…

【Linux Oracle】time命令+oracle exp压缩

Linux && Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.说明 Linux中的time命令&#xff1a;主要用于测量命令的执行时间&#xff0c;并显示该命令在执行过程中所使用的系统资源情况&#xff0c;如CPU时间、内存和…

20分钟 Bash 上手指南

文章目录 bash 概念与学习目的第一个 bash 脚本bash 语法变量的使用位置参数管道符号&#xff08;过滤条件&#xff09;重定向符号条件测试命令条件语句case 条件分支Arrayfor 循环函数exit 关键字 bash 脚本记录历史命令查询文件分发内容 bash 概念与学习目的 bash&#xff0…

【虚拟仪器技术】labview操作指南和虚拟仪器技术习题答案(一)

今天是2025年2月24日&#xff0c;画的是fate/Grand Order里面的阿尔托莉雅.卡斯特&#xff0c;武内老师的画。 目录 第1章 第2章 第3章 第4章 第5章 关注作者了解更多 我的其他CSDN专栏 毕业设计 求职面试 大学英语 过程控制系统 工程测试技术 虚拟仪器技术 可编程…

LabVIEW电能质量分析软件

随着电力系统的复杂性增加&#xff0c;电能质量问题日益突出&#xff0c;传统的电能质量检测装置多采用DSP技术&#xff0c;不仅开发周期长、功能单一&#xff0c;而且在多功能集成方面存在局限性。基于LabVIEW虚拟仪器开发平台的电能质量分析软件利用FFT、STFT、WT、HHT等多种…