卷积神经网络卷积层BN层计算原理和卷积BN层融合

部署运行你感兴趣的模型镜像

模块融合:将一些相邻模块进行融合以提高计算效率,比如conv+relu或者conv+batch normalization+relu,最常提到的BN融合指的是conv+bn通过计算公式将bn的参数融入到weight中,并生成一个bias;

上图详细描述了BN层计算原理以及如何融合卷积层和BN层,这里进行验证:

定义三个模型:

定义模型1 : 一层卷积层和一层BN层网络

import numpy as np
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

class ConvWithBn(nn.Module):
    def __init__(self, ):
        super(ConvWithBn, self).__init__()
        self.conv1 = nn.Conv2d(3, 8, kernel_size=3, stride=1, padding=1, bias=False) 
        self.bn1 = nn.BatchNorm2d(8)
        self._initialize_weights()

    def forward(self, x):
        x = self.bn1(self.conv1(x))
        return x
    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
                if m.bias is not None:
                    m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.normal_(0, 1)
                m.bias.data.normal_(0, 1)
                m.running_mean.data.normal_(0, 1)
                m.running_var.data.uniform_(1, 2)

定义模型2 : 一个卷积层网络,和上面卷层配置相同

class Conv(nn.Module):
    def __init__(self, ):
        super(Conv, self).__init__()
        self.conv1 = nn.Conv2d(3, 8, kernel_size=3, stride=1, padding=1, bias=False) 
    def forward(self, x):
        x = self.conv1(x)
        return x

定义模型3 : 一层卷积网络,和模型2卷积核数相同,但Bias不为零

class ConvWithBias(nn.Module):
    def __init__(self, ):
        super(ConvWithBias, self).__init__()
        self.conv1 = nn.Conv2d(3, 8, kernel_size=3, stride=1, padding=1, bias=True) 
    def forward(self, x):
        x = self.conv1(x)
        return x

注意,在cnn中,如果卷积层之后接Bn层,那么一般设置bias为0,因为bias会在下一层BN归一化时减去均值消掉,徒增计算,这也是为什么我们看到很多时候卷积层设置bias,有时候又不设置。

这里模型1为conv+bn,这里对卷积层和BN层进行了初始化,特别是BN层的移动平均和方差初始化,因为这个数值默认初始化是0,是通过训练迭代出来的;

模型2为conv,并且我们用模型1的卷层权重去初始化模型2;

模型3为conv,这里我们合并模型1的卷层和BN层,然后用合并后的参数初始化模型3;

如果计算没问题的话,那么相同输入情况下,模型2输出手动计算BN后,应该和模型1输出一样,模型1的卷积和bn合并后,用于初始化模型3,模型3输出应该和模型1一样。

定义输入并且计算模型1和模型2输出

Model1 = ConvWithBn()
model1_cpkt = Model1.state_dict()
Model1.eval()
Model2 = Conv()
model2_cpkt = {k:v for k,v in model1_cpkt.items() if k in Model2.state_dict()}
Model2.load_state_dict(model2_cpkt)
Model2.eval()

input = torch.randn(1,3,64,64)
out1 = Model1(input)
out2 = Model2(input)

手动计算卷积层

这里手动计算模型2的卷积过程,然后和模型2输出进行对比。

卷积原理如图

模型2有8个卷积核,每个kernel尺度为(3,3,3)对应待卷积特征图(C,H,W),因为pad=1,stride=1,卷积之后输出特征图尺度为(1,8,64,64),首先对输出进行填充:

input_pad = F.pad(input,(1,1,1,1),"constant",value=0)
print("Pad input size:",input_pad.size())

输出为:

然后手动计算卷积:

convout = torch.zeros(1,8,64,64)
kerel_w = model2_cpkt['conv1.weight']
for k in range(8):  # kernel
    for h in range(64):  # H
        for w in range(64): # W
            convout[0,k,h,w] = torch.sum(input_pad[0,:,h:(h+3),w:(w+3)]*kerel_w[k,:])

# 计算输出和模型2输出误差            
print(torch.sum(convout - out2))

输出:

ok,输出和模型2输出一样,说明计算没毛病。

手动计算BN层

BN层的具体计算如图:

BN层的输出Y与输入X之间的关系是:Y = (X - running_mean) / sqrt(running_var + eps) * gamma + beta

这里我们在模型2的输出基础上,还原BN层计算,如果没问题,那么输出应该和模型1输出一样,首先获取模型1的BN层参数:

bnw = model1_cpkt['bn1.weight']
bnb = model1_cpkt['bn1.bias']
mean = model1_cpkt['bn1.running_mean']
var = model1_cpkt['bn1.running_var']

主要注意的是var是方差,不是标准差。

Pytorch计算需要注意Tensor维度,这里转为一致:

bnwexp = bnw.unsqueeze(0).unsqueeze(2).unsqueeze(3)
bnbexp = bnb.unsqueeze(0).unsqueeze(2).unsqueeze(3)
meanexp = mean.unsqueeze(0).unsqueeze(2).unsqueeze(3)
varexp = var.unsqueeze(0).unsqueeze(2).unsqueeze(3)

套用公式计算:

bnout = bnwexp*((out2 - meanexp)/torch.sqrt(varexp+1e-5)) +bnbexp
torch.sum(bnout - out1)

输出:

可以换看到模型2输出经过模型1的BN层后,输出和模型1输出一样,误差可以忽略。

合并Conv和BN层

在开头图中详细说明了如何合并卷积和BN层,这里把模型1的两层合并为一层,也就是模型3.

Model3 = ConvWithBias()
# 提取模型1每一层参数
conv1w = model1_cpkt['conv1.weight']
bnw = model1_cpkt['bn1.weight']
bnb = model1_cpkt['bn1.bias']
bnmean = model1_cpkt['bn1.running_mean']
bnvar = model1_cpkt['bn1.running_var']
# 维度扩展
bnwexp = bnw.unsqueeze(1).unsqueeze(2).unsqueeze(3)
bnvarexp = bnvar.unsqueeze(1).unsqueeze(2).unsqueeze(3)
# 合并卷积BN层
new_conv1w = (bnwexp*conv1w)/(torch.sqrt(bnvarexp+1e-5))
new_conv2b = (bnb - bnw*bnmean/(torch.sqrt(bnvar+1e-5)))
merge_state_dict = {}
merge_state_dict['conv1.weight'] = new_conv1w
merge_state_dict['conv1.bias'] = new_conv2b

Model3.load_state_dict(merge_state_dict)
Model3.eval()
out3 = Model3(input)
print("Bias of merged ConvBn : ",torch.sum(out3 - out1))

输出:

可以看到合并ConvBn带来的误差可以忽略。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>首先,用户的问题是关于理解“卷积模块”卷积层”在深度学习中的区别。他们问的是“卷积模块与卷积层的区别”,在上下文中提到“深度学习“神经网络”。 回顾系统级指令: - 所有行内数学表达式必须使用 `$...$` 格式。 - 独立公式必须使用 `$$...$$` 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考站内引用:用户提供了几个引用,我需要参考它们,但用户说“上述引用仅供参考”,并强调“请尽可能多的参考用户上一次的问题你的回答”。用户上一次的问题是“我想理解卷积模块卷积层深度学习中的区别”,所以没有历史上下文,我只能基于这个。 用户提供的引用: - [^1]: 关于卷积神经网络的简介。 - [^2]: 关于NiN架构使用1x1卷积。 - [^3]: 关于PyTorch中的卷积实现。 - [^4]: 关于可视化卷积层。 我需要将这些引用融入回答中,自然地添加引用标识。 关键点:解释卷积模块卷积层的区别。 在深度学习中: - **卷积层(Convolutional Layer)**:这是神经网络中的基本构建块。它执行卷积操作,用于提取特征。例如,在CNN中,卷积层应用卷积核到输入数据上,产生特征图。数学上,卷积操作可以表示为:如果输入是 $x$,卷积核是 $w$,则输出是 $y = x * w$(其中*表示卷积)。 - **卷积模块(Convolutional Module)**:这通常指的是一个更复杂的结构,可能包括多个或组件。例如,在ResNet中,一个“残差模块”可能包含多个卷积层、批归一化激活函数。所以,卷积模块是更高次的抽象,由多个组成,包括卷积层。 区别总结: - 卷积层是单个操作。 - 卷积模块是一个组合单元,可能包含多个卷积层其他(如池化、激活函数)。 在引用中: - [^3] 提到PyTorch中的卷积实现,区分了 `Conv2d` `functional.conv2d`,但都是卷积操作。 - [^2] 提到NiN架构使用1x1卷积作为空间MLP,这可以看作是一个模块的一部分。 - [^4] 讨论可视化不同,但焦点在上。 结构我的回答: 1. **介绍卷积层**:解释什么是卷积层,使用数学表达式。 2. **介绍卷积模块**:解释什么是卷积模块,举例说明。 3. **区别总结**:清晰对比两者。 4. **生成相关问题**:在最后添加相关问题。 使用中文回答。 确保LaTeX正确:行内数学用 `$...$`,独立公式用 `$$...$$`。 例如,对于卷积操作:行内用 `$y = x * w$`,如果详细,独立公式用。 回答结构: - 逐步解释。 - 引用来源:自然地添加引用标识,如[^1]。 相关问题:基于主题生成。 相关问题建议: 1. 卷积层如何工作? 2. 常见的卷积模块有哪些? 3. 如何设计高效的卷积模块? 最后,确保回答真实可靠。</think>### 卷积模块与卷积层的区别 在深度学习中,尤其是卷积神经网络(CNN)中,“卷积层卷积模块”是两个相关但不同的概念。让我一步步为你解释它们的区别,确保结构清晰易懂。我会基于深度学习的基本原理相关引用进行说明,帮助您逐步理解。 #### 1. **卷积层(Convolutional Layer)** - **定义功能**:卷积层是神经网络中的基本计算单元,它执行卷积操作(convolution operation),用于从输入数据(如图像)中提取局部特征。具体来说,它通过滑动一个卷积核(kernel)或过滤器(filter)来生成特征图(feature map)。数学上,卷积操作可以表示为:如果输入数据是 $x$(例如一个图像张量),卷积核是 $w$(一个权重矩阵),则输出特征图 $y$ 的计算公式为: $$ y = x * w $$ 其中 $*$ 表示卷积运算。这个过程涉及元素级乘法,输出大小取决于输入尺寸、卷积核大小、步长(stride)填充(padding)。 - **特点**: - 卷积层是原子级的操作,专注于特征提取。例如,在CNN中,第一卷积可能检测边缘(如水平或垂直线),后续可能检测更复杂的模式(如角落或纹理)[^4]。 - 在实现中,卷积层通常由框架如PyTorch提供,例如 `torch.nn.Conv2d`,它接受输入张量大小为 `(batch, channel, H, W)`(其中 `batch` 是批大小,`channel` 是通道数,`H` `W` 是高度宽度),并输出一个新的特征张量[^3]。 - 卷积层可以独立使用,但通常与其他(如激活函数或池化)结合以增强非线性。 #### 2. **卷积模块(Convolutional Module)** - **定义功能**:卷积模块是一个更高次的构建块,它由多个(包括卷积层)组合而成,形成一个完整的处理单元。模块化设计旨在实现更复杂的特征组合或特定功能(如特征重用或残差学习)。例如,在NiN(Network in Network)架构中,一个卷积模块可能包含多个卷积层(如1×1卷积空间MLP,以更好地整合特征,而不只是简单堆叠卷积层[^2]。 - **特点**: - 卷积模块是复合结构:它可能包括卷积层、批归一化(Batch Normalization)、激活函数(如ReLU)、池化(如MaxPooling)等。例如,一个典型的模块在PyTorch中可能定义为: ```python class ConvModule(nn.Module): def __init__(self, in_channels, out_channels): super().__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1) self.bn = nn.BatchNorm2d(out_channels) self.relu = nn.ReLU() self.pool = nn.MaxPool2d(2, 2) def forward(self, x): x = self.conv(x) x = self.bn(x) x = self.relu(x) x = self.pool(x) return x ``` 这个模块将多个操作封装为一个单元,便于复用优化。 - 模块化设计的好处:它提高了模型的效率表达能力。例如,1×1卷积在模块中可以用于特征降维或空间组合,减少参数数量,同时共享特征信息[^2]。在可视化中,模块的输出可能揭示更高级的特征(如检测眼睛或面部部件),而单个卷积层只能处理低级特征[^4]。 - 卷积模块常用于现代CNN架构(如ResNet的残差模块或Inception模块),以解决梯度消失或提升特征融合。 #### 3. **关键区别总结** - **抽象级**: - 卷积层是基础操作(原子级),专注于单一的卷积计算。 - 卷积模块是组合单元(模块级),由多个(包括卷积层)构成,实现更复杂的处理流程。 - **功能范围**: - 卷积层仅执行卷积操作,输出特征图。 - 卷积模块整合了多步处理(如卷积 + 归一化 + 激活),能处理特征组合、降维或非线性增强。 - **参数效率**: - 单个卷积层参数较少,但堆叠多个可能导致效率低下。 - 卷积模块通过设计(如使用1×1卷积)优化参数共享计算效率,减少冗余[^2][^3]。 - **实际应用**: - 在简单模型中,可能直接使用多个卷积层。 - 在复杂模型中(如目标检测或图像识别),卷积模块作为可重用组件,简化架构设计并提升性能[^1][^4]。 #### 4. **示例说明** - **卷积层单独使用**:在LeNet等早期CNN中,一个卷积层可能直接后接池化,例如: $$ \text{输入} \xrightarrow{\text{Conv2d}} \text{特征图} \xrightarrow{\text{MaxPool}} \text{输出} $$ 这适合提取低级特征。 - **卷积模块应用**:在ResNet中,一个残差模块包含多个卷积层、批归一化跳跃连接(skip connection),形成一个整体单元,用于学习残差映射,避免梯度消失[^2]。 总之,卷积层是“砖块”,而卷积模块是“预制墙”——后者通过组合多个“砖块”“粘合剂”(如激活函数)构建更强大的结构。理解这个区别有助于设计高效神经网络架构[^1][^3]。 如果您有具体代码或模型示例,我可以进一步解释!
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值