YOLOv5系列(四十五) 参数重结构化(融合Conv+BatchNorm2d)

本文介绍了YOLOv5中卷积与批归一化融合的概念,通过参数重结构化提升模型推理速度。详细推导了将BN层转换为1x1卷积的过程,并提供了PyTorch代码实现。融合后的模型在保持BN特性的同时,简化了结构。
摘要由CSDN通过智能技术生成
文章目录
  • 1. 参数融合概念介绍
  • 2. 参数融合详细推导
  • 3. 参数融合代码实现

这篇文章是想要记录yolov5在模型搭建过程中的一个融合模块,就是把卷积与批归一化的参数进行融合,想卷积带有批归一化的性质,使得推理过程中可以加快模型推理速度,简化整个模型结构,实现训练与推理两个阶段的解耦。


1. 参数融合概念介绍

我最早接触参数重结构化这个词是看见了大佬丁霄汉发表的几篇论文:RepVGG,RepMLP,RepLKNet,这些构建新backbone的论文无一例外的全部使用了参数重结构化的思想。

RepVGG将3x3,1x1,identity分支的残差结果利用数学计算方法等价为一个3x3的卷积结构,实现训练与推断过程的解耦;RepMLP将局部的CNN先验信息加进了全连接层,使得其与MLP相结合等等。这里需要注意,重结构化层MLP结构也不是说变成Linear层,而是简化为1x1的卷积。(后续有机会把这几篇文章介绍一下,或者直接看大佬的知乎:https://www.zhihu.com/people/ding-xiao-yi-93/posts)

BN(批归一化)层常用于在卷积层之后,对feature maps进行归一化,从而加速网络学习,也具有一定的正则化效果。训练时,BN需要学习一个minibatch数据的均值、方差,然后利用这些信息进行归一化。而在推理过程,通常为了加速,都会把BN融入到其上层卷积中,这样就将两步运算变成了一步,也就达到了加速目的。

那么这里yolov5所实现的,是参数重结构化的一个小内容,就是把卷积与批归一化进行融合,变成一个新的卷积,但是包含BN层的特性。所以相比之下,算是参数重结构化系列的一个小小idea,可以稍微的加快推理速度。因为使用的是csp结构,所以没有涉及多并联分支的卷积模块(所以这一点其实也可以魔改下yolov5试试)。


2. 参数融合详细推导

在yolov5的注释中给了一个推导的参考资料:Fusing batch normalization and convolution in runtime,代码也是基于这篇文章来稍微修改的。

其实现的主要思想就是将bn层转化为一个1x1的卷积:

img

然后就变成了两个卷积层的迭代处理,公式为:
f ^ i , j = W B N ⋅ ( W c o n v ⋅ f i , j + b c o n v ) + b B N = ( W B N ⋅ W c o n v ) ⋅ f i , j + ( W B N ⋅ b c o n v + b B N ) = W ⋅ f i , j + b \begin{aligned} \hat{\mathrm{f}}_{\mathrm{i,j}}& =\mathrm{W_{BN}\cdot(W_{conv}\cdot f_{i,j}+b_{conv})+b_{BN}} \\ &\mathrm{=(W_{BN}\cdot W_{conv})\cdotp f_{i,j}+(W_{BN}\cdot b_{conv}+b_{BN})} \\ &=\mathrm{W\cdot f_{i,j}+b} \end{aligned} f^i,j=WBN(Wconvfi,j+bconv)+bBN=(WBNWconv)fi,j+(WBNbconv+bBN)=Wfi,j+b

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux创始人LinusTorvalds有一句名言:Talk is cheap, Show me the code.(冗谈不够,放码过来!)。 代码阅读是从入门到提高的必由之路。尤其对深度学习,许多框架隐藏了神经网络底层的实现,只能在上层调包使用,对其内部原理很难认识清晰,不利于进一步优化和创新。  YOLOv3是一种基于深度学习的端到端实时目标检测方法,以速度快见长。YOLOv3的实现Darknet是使用C语言开发的轻型开源深度学习框架,依赖少,可移植性好,可以作为很好的代码阅读案例,让我们深入探究其实现原理。  本课程将解析YOLOv3的实现原理和源码,具体内容包括: YOLO目标检测原理  神经网络及Darknet的C语言实现,尤其是反向传播的梯度求解和误差计算 代码阅读工具及方法 深度学习计算的利器:BLAS和GEMM GPU的CUDA编程方法及在Darknet的应用 YOLOv3的程序流程及各层的源码解析本课程将提供注释后的Darknet的源码程序文件。  除本课程《YOLOv3目标检测:原理与源码解析》外,本人推出了有关YOLOv3目标检测的系列课程,包括:   《YOLOv3目标检测实战:训练自己的数据集》  《YOLOv3目标检测实战:交通标志识别》  《YOLOv3目标检测:原理与源码解析》  《YOLOv3目标检测:网络模型改进方法》 建议先学习课程《YOLOv3目标检测实战:训练自己的数据集》或课程《YOLOv3目标检测实战:交通标志识别》,对YOLOv3的使用方法了解以后再学习本课程。
好的,这个问题是技术问题,可以回答。以下是一个示例代码: ```python import torch import torch.nn as nn class ConvLSTMCell(nn.Module): def __init__(self, input_size, hidden_size): super(ConvLSTMCell, self).__init__() self.input_size = input_size self.hidden_size = hidden_size self.Gates = nn.Conv2d(input_size + hidden_size, 4 * hidden_size, 3, padding = 1) def forward(self, input, hidden, cell): combined = torch.cat((input, hidden), dim=1) gates = self.Gates(combined) forget_gate, input_gate, output_gate, cell_gate = gates.chunk(4, dim=1) forget_gate = torch.sigmoid(forget_gate) input_gate = torch.sigmoid(input_gate) cell_gate = torch.tanh(cell_gate) output_gate = torch.sigmoid(output_gate) cell = (forget_gate * cell) + (input_gate * cell_gate) hidden = output_gate * torch.tanh(cell) return hidden, cell class ConvLSTMLayer(nn.Module): def __init__(self, input_size, hidden_size): super(ConvLSTMLayer, self).__init__() self.input_size = input_size self.hidden_size = hidden_size self.ConvLSTMCell = ConvLSTMCell(input_size, hidden_size) def forward(self, input, Hidden_State=None, Cell_State=None): if Hidden_State is None: Hidden_State = torch.zeros(input.size()[0], self.hidden_size, input.size()[2], input.size()[3]).cuda() if Cell_State is None: Cell_State = torch.zeros(input.size()[0], self.hidden_size, input.size()[2], input.size()[3]).cuda() Hidden_State_List = [] for timestep in range(input.size()[1]): Hidden_State, Cell_State = self.ConvLSTMCell(input[:,timestep,:,:,:], Hidden_State, Cell_State) Hidden_State_List.append(Hidden_State) return torch.stack(Hidden_State_List, dim=1), Hidden_State, Cell_State class ConvBlock(nn.Module): def __init__(self, in_channels, out_channels): super(ConvBlock, self).__init__() self.conv = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True) ) def forward(self, x): return self.conv(x) class SegmentationModel(nn.Module): def __init__(self): super(SegmentationModel, self).__init__() self.convlstm_layer = ConvLSTMLayer(512, 512) self.conv_block1 = ConvBlock(512, 256) self.conv_block2 = ConvBlock(256, 128) self.classification = nn.Sequential( nn.Conv2d(128, 2, kernel_size=1, stride=1, padding=0), nn.BatchNorm2d(2), nn.Softmax(dim=1) ) def forward(self, x): x, _, _ = self.convlstm_layer(x) x = self.conv_block1(x) x = self.conv_block2(x) x = self.classification(x) return x ``` 这是一个简单的分割模型,由ConvLSTMLayer和两个ConvBlock组成,输入5帧大小为512 x W x H的数据,输出大小为2 x W x H的分割结果。其中ConvLSTMLayer是一个5帧ConvLSTM层,用于学习时序信息。ConvBlock是一个包含卷积和BatchNorm的卷积操作块,用于提取特征。分类层使用1x1卷积进行2分类,并使用Softmax进行归一化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小酒馆燃着灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值