RepVGG学习笔记

news2024/11/26 4:30:11

📣 论文下载地址:https://arxiv.org/abs/2101.03697
📣 官方源码(Pytorch实现):https://github.com/DingXiaoH/RepVGG

0 前言

🐾 R e p V G G RepVGG RepVGG最大的创新之处是:结构重参数化。结构重参数化是一个新的概念,在训练和推理阶段采用不同的策略。比如:在训练阶段,使用一个类似 R e s N e t ResNet ResNet的多分支模型,而在推理时转化成 V G G VGG VGG的单路模型。

1 结构重参数化

🐾 结构重参数化就是在训练阶段采用多分支模型,而在推理测试阶段使用单分支模型,可以理解为如下图所示,训练时模型的基本组件为 A A A,而在测试阶段模型的基本组件为 B B B
在这里插入图片描述
🐾 那么关键的问题来了,训练的多分支模型是如何转化到测试的单分支模型的呢?👍 👍 👍 全文参考 强力推荐:结构重参数化主要分为两步,第一步主要是将 C o n v 2 D Conv2D Conv2D算子和 B N BN BN算子融合以及将只有 B N BN BN的分支转换成一个 C o n v 2 D 3 × 3 Conv2D 3\times3 Conv2D3×3分支;第二步将多个分支上的 C o n v 2 D 3 × 3 Conv2D 3\times3 Conv2D3×3卷积分支融合成单路结构的一个 3 × 3 3\times3 3×3卷积。

1.1 结构重参数化第一步(将 C o n v 2 D Conv2D Conv2D算子和 B N BN BN算子融合以及将只有 B N BN BN的分支转换成一个 C o n v 2 D Conv2D Conv2D算子)

  • Conv2D + BN ——> Conv2D
    BN ——> Conv2D
    卷积计算方式: y i = W i ∗ x i + b y_i=W_i*x_i + b yi=Wixi+b,在模型训练阶段,若忽略偏置的影响,则只有公式: y i = W i ∗ x i y_i=W_i*x_i yi=Wixi。标准化 B N BN BN的计算公式为: y i = x i − u i ( δ i ) 2 + ϵ × γ i + β i y_i = \frac{x_i - u_i}{\sqrt{(\delta_i)^2+\epsilon}}\times \gamma_i+\beta_i yi=(δi)2+ϵ xiui×γi+βi,将 B N BN BN层的计算公式按类似卷积计算公式展开就为: y i = γ i δ i 2 + ϵ x i + ( β i − u i ⋅ γ i δ i 2 + ϵ ) y_i= \frac{\gamma_i}{\sqrt{\delta_i^2+\epsilon}}x_i+(\beta_i - \frac{u_i \cdot \gamma_i}{\sqrt{\delta_i^2+\epsilon}}) yi=δi2+ϵ γixi+(βiδi2+ϵ uiγi),在所有的模型中,都是卷积层后接标准化(归一化)层,那么就有卷积层的输出是 B N BN BN层的输入,也就是说在忽略卷积偏置的条件下, y i = W i ∗ x i y_i=W_i*x_i yi=Wixi就是 B N BN BN计算公式中 x i x_i xi的输入。
    上步骤说到卷积在忽略偏置的条件下,计算公式为: y i = W i ∗ x i y_i=W_i*x_i yi=Wixi,就输入特征图和卷积核来说,卷积计算的理论过程是对应位置相乘后相加,卷积核移位,再同理计算下一个值。如下图所示:
    在这里插入图片描述

据上图所示,得到 y 1 y_1 y1位置的计算值为: w 1 ∙ x 1 + w 2 ∙ x 2 + w 3 ∙ x 3 + w 4 ∙ x 5 + w 5 ∙ x 6 + w 6 ∙ x 7 + w 7 ∙ x 9 + w 8 ∙ x 10 + w 9 ∙ x 11 w_1∙x_1+w_2∙x_2+w_3∙x_3+w_4∙x_5+w_5∙x_6+w_6∙x_7+w_7∙x_9+w_8∙x_{10}+w_9∙x_{11} w1x1+w2x2+w3x3+w4x5+w5x6+w6x7+w7x9+w8x10+w9x11,以该点位置为例,该值输入到 B N BN BN层中,计算公式为: ( w 1 ∙ x 1 + w 2 ∙ x 2 + w 3 ∙ x 3 + w 4 ∙ x 5 + w 5 ∙ x 6 + w 6 ∙ x 7 + w 7 ∙ x 9 + w 8 ∙ x 10 + w 9 ∙ x 11 ) ⋅ γ 1 δ 1 2 + ϵ + ( β 1 − u 1 ⋅ γ 1 δ 1 2 + ϵ ) (w_1∙x_1+w_2∙x_2+w_3∙x_3+w_4∙x_5+w_5∙x_6+w_6∙x_7+w_7∙x_9+w_8∙x_{10}+w_9∙x_{11}) \cdot \frac{\gamma_1}{\sqrt{\delta_1^2+\epsilon}}+(\beta_1 - \frac{u_1 \cdot \gamma_1}{\sqrt{\delta_1^2+\epsilon}}) (w1x1+w2x2+w3x3+w4x5+w5x6+w6x7+w7x9+w8x10+w9x11)δ12+ϵ γ1+(β1δ12+ϵ u1γ1)。到这里 C o n v 2 D Conv2D Conv2D就和 B N BN BN层融合在一个计算公式内了。通过公式也可以发现,该公式就是加了偏置的卷积计算公式,所以就可以融合为一个卷积分支了。(理解到这里就算差不多了,但最关键的还是代码了,接着看~~)

🐾 根据学习发现,原论文中的 c o n v 2 d + B N conv2d + BN conv2d+BN或者 B N BN BN,都是转换为 3 × 3 3\times3 3×3卷积,然后再将转化的多个 3 × 3 3\times3 3×3卷积进行融合,为一个 3 × 3 3\times3 3×3卷积,才算结束。那么就有这几种转化需要理解:

  • 1 × 1 卷积 1\times1 卷积 1×1卷积 ——> 3 × 3 卷积 3\times3 卷积 3×3卷积
    原始 1 × 1 1\times1 1×1卷积,只需要将卷积核设置 p a d = 1 pad=1 pad=1的0填充,就可以将 1 × 1 1\times1 1×1卷积转换为 3 × 3 3\times3 3×3卷积。
    如下图所示:
    在这里插入图片描述
  • B N 标准化 BN标准化 BN标准化 ——> 3 × 3 卷积 3\times3 卷积 3×3卷积
    只有 B N BN BN的模型结构,需要构建一个卷积层,然后再根据 C o n v 2 D + B N Conv2D + BN Conv2D+BN转换公式进行转换,得到 3 × 3 3\times3 3×3卷积层。

1.2 结构重参数化第二步(多分支的 3 × 3 3\times3 3×3卷积融合成一个 3 × 3 3\times3 3×3卷积)

在这里插入图片描述

根据图例,融合后的3分支卷积的计算过程可以理解为: y 1 = w 1 ∗ x 1 + b 1 、 y 2 = w 2 ∗ x 1 + b 2 、 y 3 = w 3 ∗ x 1 + b 3 y_1=w_1*x_1+b_1、y_2=w_2*x_1+b_2、y_3=w_3*x_1+b_3 y1=w1x1+b1y2=w2x1+b2y3=w3x1+b3默认输入都是 x 1 x_1 x1,多分支再进行融合可计算为: y i = ( w 1 + w 2 + w 3 ) ∗ x 1 + ( b 1 + b 2 + b 3 ) y_i=(w_1+w_2+w_3)*x_1+(b_1+b_2+b_3) yi=(w1+w2+w3)x1+(b1+b2+b3)
至此,整个的融合过程就结束了。

2 代码分析

🐾 下面就根据大佬的博文的代码来详细剖析下, C o n v 2 D + B N Conv2D + BN Conv2D+BN 转换为 3 × 3 3\times3 3×3卷积。

from collections import OrderedDict
import numpy as np
import torch
import torch.nn as nn

def main():
    torch.random.manual_seed(0)
    f1 = torch.randn(1, 2, 3, 3)
    module = nn.Sequential(OrderedDict(
        conv=nn.Conv2d(in_channels=2, out_channels=2, kernel_size=3, stride=1, padding=1, bias=False),
        bn=nn.BatchNorm2d(num_features=2)
    ))

    module.eval()

    with torch.no_grad():
        output1 = module(f1)
        print(output1)

    # fuse conv + bn
    kernel = module.conv.weight            # Conv2D 卷积核
    running_mean = module.bn.running_mean  # BN 均值
    running_var = module.bn.running_var    # BN 方差
    gamma = module.bn.weight               # BN  gamma 可学习参数
    beta = module.bn.bias                  # BN  beta  可学习参数
    eps = module.bn.eps                    # BN  eps   可学习参数
    std = (running_var + eps).sqrt()       # BN  根号下方差加eps(很小的数)
    t = (gamma / std).reshape(-1, 1, 1, 1)  # [ch] -> [ch, 1, 1, 1]
    kernel = kernel * t
    bias = beta - running_mean * gamma / std
    fused_conv = nn.Conv2d(in_channels=2, out_channels=2, kernel_size=3, stride=1, padding=1, bias=True)
    fused_conv.load_state_dict(OrderedDict(weight=kernel, bias=bias))

    with torch.no_grad():
        output2 = fused_conv(f1)
        print(output2)

    np.testing.assert_allclose(output1.numpy(), output2.numpy(), rtol=1e-03, atol=1e-05)
    print("convert module has been tested, and the result looks good!")


if __name__ == '__main__':
    main()

上述代码中可以看到是定义了一个 n n . S e q u e n t i a l nn.Sequential nn.Sequential序列容器,里面包含 3 × 3 3\times3 3×3卷积和 B N BN BN归一化,此时卷积是忽略偏置系数的,而这个卷积可以理解为是定义在模型训练阶段的,然后就是通过公式: ( x 1 1 ⋅ k 1 1 + x 2 2 ⋅ k 1 1 + x 3 3 ⋅ k 3 3 + x 4 4 ⋅ k 4 4 ) ⋅ γ 1 δ 1 2 + ϵ + ( β 1 − u 1 ⋅ γ 1 δ 1 2 + ϵ ) (x_1^1 \cdot k_1^1+x_2^2 \cdot k_1^1+x_3^3 \cdot k_3^3+x_4^4 \cdot k_4^4) \cdot \frac{\gamma_1}{\sqrt{\delta_1^2+\epsilon}}+(\beta_1 - \frac{u_1 \cdot \gamma_1}{\sqrt{\delta_1^2+\epsilon}}) (x11k11+x22k11+x33k33+x44k44)δ12+ϵ γ1+(β1δ12+ϵ u1γ1) 来进行二者的融合。根据代码再结合此公式就很好理解了,融合后的 3 × 3 3\times3 3×3卷积的偏置系数为: β 1 − u 1 ⋅ γ 1 δ 1 2 + ϵ \beta_1 - \frac{u_1 \cdot \gamma_1}{\sqrt{\delta_1^2+\epsilon}} β1δ12+ϵ u1γ1,需要注意的是融合后的卷积是有偏置系数的,而且是在模型推理阶段。然后就是卷积核的参数,其计算的公式为:待融合的卷积权重参数 × γ 1 δ 1 2 + ϵ \times \frac{\gamma_1}{\sqrt{\delta_1^2+\epsilon}} ×δ12+ϵ γ1,可以发现融合过程是严格按着上述公式计算得来的。新的卷积就融合了 C o n v 2 d D + B N Conv2dD + BN Conv2dD+BN的所有参数信息。(值得注意的就是:融合前的卷积是不带有偏置的,而融合后的卷积要设置偏置为 T r u e True True

📝 📝 📝

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

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

相关文章

安全运营 ldap监控域控信息

0x00 背景 公司有多个主域,子域,有的子域因为境外数据安全的问题无法把日志传输到境内。那么如何在没有日志的情况下监控子域或者互信域的组织单元(OU)信息呢。 由于访问互信域要在域控上进行,本文根据最小权限原则监控普通用户也可以访问的…

Packet Tracer - 配置和验证小型网络

Packet Tracer - 配置和验证小型网络 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 RTA G0/0 10.10.10.1 255.255.255.0 不适用 G0/1 10.10.20.1 255.255.255.0 不适用 SW1 VLAN1 10.10.10.2 255.255.255.0 10.10.10.1 SW2 VLAN1 10.10.20.2 255.25…

【C++】set和map的使用

对于STL容器来说,有很多相似的功能,所以这里主要将与之前不同的功能说清楚 文章目录 1.对于set与set的简单理解2. setinsert迭代器遍历countmultisetinsertfindcount 3. mapinsert与迭代器的使用统计水果次数 operator []operator[]的实现理解对整体的拆…

Nginx:常见的面试题和答案

1. 什么是Nginx? 答:Nginx是一款高性能的Web服务器和反向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议,同时用于处理高并发的请求,提供快速、可靠的服务。 2. Nginx的优点是什么? Nginx的优点包括&#xff1a…

【BeautifulSoup上】——05全栈开发——如桃花来

目录索引 介绍:解析库: 安装:pip install BeautifulSoup4pip install lxml 标签选择器:1.string属性:.name属性:获取标签中的属性值: 实用——标准选择器:使用find_all()根据标签名查…

五、C++内存管理机制 —— primitives(侯捷)

侯捷 C八部曲笔记汇总 - - - 持续更新 ! ! ! 一、C 面向对象高级开发 1、C面向对象高级编程(上) 2、C面向对象高级编程(下) 二、STL 标准库和泛型编程 1、分配器、序列式容器 2、关联式容器 3、迭代器、 算法、仿函数 4、适配器、补充 三、C 设计模式 四、C 新标准 五、C 内存管…

2023-04-29 动态规划介绍

2023-04-29 动态规划介绍 动态规划是运筹学课程的一部分 多阶段决策问题 有一类活动的过程,可以分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果 当然,每个阶段的决策的选取不是任意确…

dc-6靶机

1.使用nmap进行信息搜集,存活主机,端口 192.168.85.184是存活主机,发现开放22,80端口 2.访问192.168.85.184的80端口 发现被重定向了,修改hosts文件 vim /etc/hosts 添加一行 192.168.85.174 wordy3.对网站进行信息搜…

彻底解决 Lost connection to MySQL server at ‘reading initial communication packet’, system error: 0 解决方法

当我遇到这错误的时候,我去网上也找过对应解决方法,出现这个的原因有很多种情况 大多是解决Linux系统里的 我是windows系统里的MySQL服务出问题了,所有那些方法对我来说毫无意义. 好了,说一下我的解决办法,其实也很简单 只需要卸载mysql服务,注册表也要删干净,也要把环境变…

C的文件操作

🐖前言 🐕1.为什们我们要用文件 在我们之前写程序时,如果使用scanf函数用键盘输入数据,这些东西都放到内存当中,一旦退出程序,那么这些数据就会消失,比如就像我们写的通讯录,不管是…

Shiro相关知识

1、Shiro功能概述 Apache Shiro是一个功能强大且易于使用的 Java 安全框架,可执行身份验证、授权、加密和会话管理。 主要功能: Authentication:身份认证。登录时验证身份信息。 Authorization:授权操作。访问控制的过程&…

CSS布局基础(标签类型,盒子模型)

布局基础 元素显示类型,盒子模型 标签类型块元素常见块元素 行内元素常见行内元素 行内块元素常见行内块 模式转换显示类型显著区别 盒子模型盒子组成布局描述边框圆角 内边距外边距块元素居中盒子内行内(块)元素居中 外边距使用陷阱两盒子外…

【进阶C语言】动态版通讯录的实现(详细讲解+全部码源)

前言 📕作者简介:热爱跑步的恒川,致力于C/C、Java、Python等多编程语言,热爱跑步,喜爱音乐的一位博主。 📗本文收录于C语言进阶系列,本专栏主要内容为数据的存储、指针的进阶、字符串和内存函数…

Linux基础IO【重定向及缓冲区理解】

✨个人主页: 北 海 🎉所属专栏: Linux学习之旅 🎃操作环境: CentOS 7.6 阿里云远程服务器 文章目录 🌇前言🏙️正文1、文件描述符1.1、先描述,再组织1.2、files_struct1.3、分配规则…

Java数组的学习(基础)

目录 第一章:数组的概念介绍 1.数组的概念 2.数组的初始化/数组的创建/数组的定义 第二章:数组的使用 数组添加元素的方法/数组的赋值 数组的遍历 数组之选择排序的升序 数组之冒泡排序的升序 数组的最小值 数组的反转 数组中常见的异常 第三…

Python程序员想要转行,可以从这几个方面着手

最近有很多朋友问我一个问题,不论是我们做程序员还是做产品经理或者其他行业,到了30岁或35岁之后,都会面临各种各样的问题,比如达到职业天花板。有没有一种方法能够解决这种问题呢?我想分享一下我的观点和身边的案例。…

《Netty》从零开始学netty源码(四十七)之PooledByteBuf的方法

setBytes() 从channel中读取数据并写到PooledByteBuf中,分配缓存的过程与getBytes一样,只是duplicate为false。 capacity() 动态更新容量,根据新传入的容量值更改length。 如果新容量值与旧值相同则无需扩容如果为非池化内存则根据新容量值…

Zabbix部署详解

文章目录 Zabbix安装部署一、zabbix-server端部署二、zabbix-agent端部署 Zabbix安装部署 环境准备 VMware Workstation Pro 15.0 版本 系统 Centos7 内存 4G 处理器 2G 硬盘 50G 网络适配器 NAT 两台服务器,一台做zabbix-server、一台做zabbix-agent 关闭防火墙、…

【华为OD机试真题】信号发射和接收(javaC++python)100%通过率 超详细代码注释

信号发射和接收 知识点数组栈 单调栈时间限制: 1s 空间限制: 256MB 限定语言:不限 题目描述: 有一个二维的天线矩阵,每根天线可以向其他天线发射信号也能接收其他天线的信号,为了简化起见,我们约定每根天线只能向东和向南发射信号,换言之,每根天线只能接收东向或南向发…

unity-VRTK-simulator开发学习日记2(抛物线 导包|使用|调用方法)

导包 使用抛物线 1.层级目录下添加抛物线曲线 2.将跟踪控制器 给到抛物线的“跟随资源” (选择哪只手射出射线) 3.激活按键 找到模拟手柄按键 找到simulator的交互的几个按键(ButtonOne为例) value,默认值为false&a…