关于对卷积神经网络BN,bias,relu之间关系的理解

以下三个问题其实是一个问题

一、为什么加BN,为什么加relu

二,为什么如果+BN ,卷积层可以不加bias

三,为什么BN要在relu前

--------------------------------------------------------------------------------------------------------

relu的效果就是把负值归零,可以理解为清除与特征无关的信息。relu的输出就是有用的信息,和大面积的0。

---------------------------

BN共分为4步,

第一步均值,作用是将输入数据中心平移至0点。

第二步方差,作用是将平移至0点的数据在输出方向上拉伸或缩小为1个标准差的倍数。

第三步gamma,作用是将这1个标准差的信号变成gamma倍。

第四步beta,作用是在输出方向上平移数据。

以上是从数据角度,如果从0点角度来说

BN第一步均值,是将0点定位数据中心。

第二步方差,是将数据到0点的距离调整为以标准差为单位

第三步gamma,是将数据到0点的距离调整为gamma倍标准差为单位

第四部beta,是在输出方向移动0点,调整数据在0上或者在0下的位置。

-----------------------------------

最终0下被relu清除,0上被relu完整输出给下一个卷积。

所以BN+relu是一个整体,共同决定了输出值究竟淘汰了多少信息,以及保留信息的强度。

卷积相当于粗加工,他就把原输入过滤一遍,然后把结果扔出来,不过他不会调整力度太轻他就仍天上扔的远,太重的他就扔地上扔的近。(bias会解决这个问题,这里认为有BN卷积里就没有加bias)。

relu本质上就是一个位置固定地机器,不管来的是什么,在哪里,有多高。他就是永远在0点切一刀,切到啥是啥。 然后0下的压扁当托盘,拖着0上的一起交给下一个卷积。

这两者直接接触明显会产生三个问题:扔高了切不到等于没切;仍低了直接不切全压扁了;扔出来的东西太大太小棱角太多,切完丢给下一个卷积人家没法处理。

而这三个问题就是由BN的4各环节一一解决的:接稳(均值),打磨(方差和gamma),对准刀口(beta)。

所以BN是为了relu做准备,提供给relu一个整理好的数据集,给relu摆好位置,让relu无需动脑,直接下刀。

所以如果BN如果在relu后,那么相当于relu先蒙着眼自己切一刀,切到什么就看卷积的直接输出是什么,最极端情况是relu什么也没切到(全正)相当如relu直接输出所有数据。或者relu直接把所有信号清零(全负)。 

并且这不算完,然后BN还会再把relu产生的大量0点,重新调整为负最大值。相当于把大面积正输出,变为负输出,relu归零变 成 了 归负极 。相当于弱化有用信息,并强化无用信息,并且把大量无用信息极端最大化。

连起来比喻就相当于卷积大师傅丢一个只有BN懂的东西出来,却被relu一步抢先闭眼直接凌空一刀一锤。BN接住天一看似曾相识,话不多说就把这个上半球按照整球标准精细打磨了一遍,然后按操作手册把数据调整高度,对准relu刀口,画条黑线,线下全部打叉,丢给下一个卷积。

因为下一个卷积会针对这个奇怪的东西改变自己来加工他,所以最后结果会是怎样不好说,就只能看最后结果。

可以肯定的只有:要不就是上一个卷积认为有用的数据,相当大一部分被当成了干扰项传给了下一个卷积。要不就是上一个卷积认为无用的数据,相当大一部分被当成特征传给了下一个卷积。

所以BN要在relu前,看上去更合理。

---------------------------------------

同时因为BN的均值环节,完全无视卷积的bias效果,不管bias如何平移数据,BN均值都会将数据中心归零,都会是一个结果,所以卷积的bias无用,可以不加。而BN中beta的作用,就是卷积层中的bias的作用。可以理解为BN相当于是在卷积的卷积和bias两个环节中间,加了均值,方差,gamma三个环节把卷积的bias,变成了自己的beta。

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是基于PyTorch实现的三输入特征三输出的一维卷积神经网络,使用ResNet网络的代码示例。 ```python import torch import torch.nn as nn import torch.nn.functional as F class ResNetBlock(nn.Module): def __init__(self, in_channels, out_channels, stride=1, downsample=None): super(ResNetBlock, self).__init__() self.conv1 = nn.Conv1d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm1d(out_channels) self.conv2 = nn.Conv1d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm1d(out_channels) self.relu = nn.ReLU(inplace=True) self.downsample = downsample def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out class ResNet(nn.Module): def __init__(self, in_channels, out_channels, num_blocks=2): super(ResNet, self).__init__() self.in_channels = in_channels self.out_channels = out_channels self.conv1 = nn.Conv1d(in_channels, 64, kernel_size=7, stride=2, padding=3, bias=False) self.bn1 = nn.BatchNorm1d(64) self.relu = nn.ReLU(inplace=True) self.maxpool = nn.MaxPool1d(kernel_size=3, stride=2, padding=1) self.layer1 = self._make_layer(64, 64, num_blocks) self.layer2 = self._make_layer(64, 128, num_blocks, stride=2) self.layer3 = self._make_layer(128, 256, num_blocks, stride=2) self.avgpool = nn.AdaptiveAvgPool1d(1) self.fc1 = nn.Linear(256, 128) self.fc2 = nn.Linear(128, out_channels) def _make_layer(self, in_channels, out_channels, num_blocks, stride=1): downsample = None if stride != 1 or in_channels != out_channels: downsample = nn.Sequential( nn.Conv1d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False), nn.BatchNorm1d(out_channels), ) layers = [ResNetBlock(in_channels, out_channels, stride, downsample)] for _ in range(1, num_blocks): layers.append(ResNetBlock(out_channels, out_channels)) return nn.Sequential(*layers) def forward(self, x1, x2, x3): x = torch.cat((x1.unsqueeze(1), x2.unsqueeze(1), x3.unsqueeze(1)), dim=1) x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.avgpool(x) x = x.view(x.size(0), -1) x = self.fc1(x) x = self.relu(x) x = self.fc2(x) return x ``` 该代码实现了一个ResNet网络,其中包含了ResNetBlock和ResNet两个类。ResNetBlock是ResNet的基本模块,ResNet是整个网络。 在ResNet中,我们首先对输入的三个特征进行拼接,然后进入卷积层进行特征提取。之后,我们通过多个ResNetBlock来进行深度的特征提取。最后,我们通过全局平均池化将特征降维,然后经过两个全连接层获得最终的输出。 在_ResNetBlock_中,我们定义了两个卷积层,每个卷积层后面都跟着一个BatchNorm1d和ReLU激活函数。在_ResNet_中,我们定义了一个_make_layer方法来生成多个ResNetBlock。在这个方法中,我们也定义了一个downsample层,用于将输入的特征进行下采样,使得特征维度和输出维度相同。最后,我们将三个特征拼接成一个特征输入到ResNet中进行特征提取。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值