https://github.com/Michael-Jing/EfficientDet-pytorch/blob/master/efficientdet_pytorch/BiFPN.py
这里面的参数不可导,
训练集有map,但是测试集map全是0,
经过验证,不是bifpn的问题。
bifpn 动态参数和静态参数
改成可求导的参数后,准确率反而下降了。
后来用的就是这个:
import torch
from torch.nn.init import xavier_uniform
import torch.nn as nn
import torch.nn.functional as F
from conv_module import ConvModule
eps = 0.0001
class ConvBlock(nn.Module):
def __init__(self, in_ch, out_ch, k, act=True):
super().__init__()
self.conv = nn.Conv2d(in_ch, out_ch, k, padding=k // 2)
self.act = act
if self.act:
self.bn = nn.BatchNorm2d(out_ch)
def forward(self, x):
x = self.conv(x)
if self.act:
x = self.bn(x)
return F.relu6(x)
else:
return x
class BiFPN_2(nn.Module):
def __init__(self, n_inputs, out_channels):
super().__init__()
self.n_inputs = n_inputs
self.intermediate_fusion_weights = nn.Parameter(
torch.ones(self.n_inputs-2, 2, dtype=torch.float32))
self.output_fusion_weights = nn.Parameter(
torch.ones(self.n_inputs, 3, dtype=torch.float32))
self.intermediate_convs = nn.ModuleList([])
for _ in range(self.n_inputs-2):
self.intermediate_convs.append(ConvBlock(
in_ch=out_channels, out_ch=out_channels, k=3))
self.output_convs = nn.ModuleList([])
for _ in range(self.n_inputs):
self.output_convs.append(ConvBlock(
in_ch=out_channels, out_ch=out_channels, k=3))
@staticmethod
def fast_fuse(xs, ws):
"Output has shape of first x"
target_sz = xs[0].shape[-1]
output = torch.zeros_like(xs[0])
for i, x in enumerate(xs):
if x.shape[-1] != target_sz:
if x.shape[-1] == target_sz//2:
x = F.interpolate(x, scale_factor=2, mode='nearest')
elif x.shape[-1] == target_sz*2:
x = F.avg_pool2d(x, 2)
else:
assert False
output += ws[i] * x
output /= ws.sum() + 1e-4
return output
def forward(self, *xs):
assert len(xs) == self.n_inputs
ifws = F.relu(self.intermediate_fusion_weights)
intermediates = []
for i in range(self.n_inputs-2):
small = xs[-1] if i == 0 else intermediates[-1]
same = xs[self.n_inputs-i-2]
fused = self.fast_fuse([same, small], ifws[i])
intermediates.append(self.intermediate_convs[i](fused))
ofws = F.relu(self.output_fusion_weights)
outputs = []
for i in range(self.n_inputs):
if i == 0:
fuse_inps = [xs[0], intermediates[-1]]
elif i > 0 and i < self.n_inputs - 1:
fuse_inps = [xs[i], intermediates[-i], outputs[-1]]
elif i == self.n_inputs - 1:
fuse_inps = [xs[i], outputs[-1]]
fused = self.fast_fuse(fuse_inps, ofws[i])
outputs.append(self.output_convs[i](fused))
return outputs
class BIFPN(nn.Module):
def __init__(self,
in_channels,
out_channels,
num_outs,
start_level = 0,
end_level = -1,
stack = 1,
add_extra_convs = False,
extra_convs_on_inputs = True,
relu_before_extra_convs = False,
no_norm_on_lateral = False,
conv_cfg = None,
norm_cfg = None,
activation = None):
super(BIFPN, self).__init__()
assert isinstance(in_channels, list)
self.in_channels = in_channels
self.out_channels = out_channels
self.num_ins = len(in_channels)
self.num_outs = num_outs
self.activation = activation
self.relu_before_extra_convs = relu_before_extra_convs
self.no_norm_on_lateral = no_norm_on_lateral
self.fp16_enabled = False
self.stack = stack
if end_level == -1:
self.backbone_end_level = self.num_ins
assert num_outs >= self.num_ins - start_level
else:
# if end_level < inputs, no extra level is allowed
self.backbone_end_level = end_level
assert end_level <= len(in_channels)
assert num_outs == end_level - start_level
self.start_level = start_level
self.end_level = end_level
self.add_extra_convs = add_extra_convs
self.extra_convs_on_inputs = extra_convs_on_inputs
self.lateral_convs = nn.ModuleList()
self.fpn_convs = nn.ModuleList()
self.stack_bifpn_convs = nn.ModuleList()
for i in range(self.start_level, self.backbone_end_level):
l_conv = ConvModule(
in_channels[i],
out_channels,
1,
conv_cfg = conv_cfg,
norm_cfg = norm_cfg if not self.no_norm_on_lateral else None,
activation = self.activation,
inplace = False)
self.lateral_convs.append(l_conv)
for ii in range(stack):
self.stack_bifpn_convs.append(BiFPNModule(channels = out_channels,
levels = self.backbone_end_level - self.start_level,
conv_cfg = conv_cfg,
norm_cfg = norm_cfg,
activation = activation))
# add extra conv layers (e.g., RetinaNet)
extra_levels = num_outs - self.backbone_end_level + self.start_level
if add_extra_convs and extra_levels >= 1:
for i in range(extra_levels):
if i == 0 and self.extra_convs_on_inputs:
in_channels = self.in_channels[self.backbone_end_level - 1]
else:
in_channels = out_channels
extra_fpn_conv = ConvModule(
in_channels,
out_channels,
3,
stride = 2,
padding = 1,
conv_cfg = conv_cfg,
norm_cfg = norm_cfg,
activation = self.activation,
inplace = False)
self.fpn_convs.append(extra_fpn_conv)
# default init_weights for conv(msra) and norm in ConvModule
def init_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
xavier_uniform(m)
def forward(self, inputs):
assert len(inputs) == len(self.in_channels)
# build laterals
laterals = [
lateral_conv(inputs[i + self.start_level])
for i, lateral_conv in enumerate(self.lateral_convs)
]
# part 1: build top-down and down-top path with stack
used_backbone_levels = len(laterals)
for bifpn_module in self.stack_bifpn_convs:
laterals = bifpn_module(laterals)
outs = laterals
# part 2: add extra levels
if self.num_outs > len(outs):
# use max pool to get more levels on top of outputs
# (e.g., Faster R-CNN, Mask R-CNN)
if not self.add_extra_convs:
for i in range(self.num_outs - used_backbone_levels):
outs.append(F.max_pool2d(outs[-1], 1, stride = 2))
# add conv layers on top of original feature maps (RetinaNet)
else:
if self.extra_convs_on_inputs:
orig = inputs[self.backbone_end_level - 1]
outs.append(self.fpn_convs[0](orig))
else:
outs.append(self.fpn_convs[0](outs[-1]))
for i in range(1, self.num_outs - used_backbone_levels):
if self.relu_before_extra_convs:
outs.append(self.fpn_convs[i](F.relu(outs[-1])))
else:
outs.append(self.fpn_convs[i](outs[-1]))
return tuple(outs)
class BiFPNModule(nn.Module):
def __init__(self,
channels,
levels,
init = 0.5,
conv_cfg = None,
norm_cfg = None,
activation = None):
super(BiFPNModule, self).__init__()
self.activation = activation
self.levels = levels
self.bifpn_convs = nn.ModuleList()
# weighted
self.w1 = nn.Parameter(torch.Tensor(2, levels).fill_(init))
self.relu1 = nn.ReLU()
self.w2 = nn.Parameter(torch.Tensor(3, levels - 2).fill_(init))
self.relu2 = nn.ReLU()
for jj in range(2):
for i in range(self.levels - 1):# 1,2,3
fpn_conv = nn.Sequential(
ConvModule(
channels,
channels,
3,
padding = 1,
groups = channels,
conv_cfg = conv_cfg,
norm_cfg = norm_cfg,
activation = self.activation,
inplace = False),
ConvModule(
channels,
channels,
1,
conv_cfg = conv_cfg,
norm_cfg = norm_cfg,
activation = self.activation,
inplace = False))
self.bifpn_convs.append(fpn_conv)
# default init_weights for conv(msra) and norm in ConvModule
def init_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
xavier_uniform(m)
def forward(self, inputs):
# for item in inputs:print(item.shape)
assert len(inputs) == self.levels
# build top-down and down-top path with stack
levels = self.levels
# w relu
w1 = self.relu1(self.w1)
w1 /= torch.sum(w1, dim = 0) + eps# normalize
w2 = self.relu2(self.w2)
w2 /= torch.sum(w2, dim = 0) + eps
# build top-down
kk = 0
pathtd = inputs
inputs_clone = []
for in_tensor in inputs:
inputs_clone.append(in_tensor.clone())
for i in range(levels - 1, 0, -1):
pathtd[i - 1] = w1[0, kk] * pathtd[i - 1] + w1[1, kk] * F.interpolate(
pathtd[i], scale_factor = 2, mode = 'nearest')
pathtd[i - 1] = self.bifpn_convs[kk](pathtd[i - 1])
kk = kk + 1
jj = kk
# build down-top
for i in range(0, levels - 2, 1):
pathtd[i + 1] = w2[0, i] * pathtd[i + 1] + w2[1, i] * F.max_pool2d(pathtd[i], kernel_size = 2) + w2[2, i] * \
inputs_clone[i + 1]
pathtd[i + 1] = self.bifpn_convs[jj](pathtd[i + 1])
jj = jj + 1
pathtd[levels - 1] = w1[0, kk] * pathtd[levels - 1] + w1[1, kk] * F.max_pool2d(pathtd[levels - 2],
kernel_size = 2)
pathtd[levels - 1] = self.bifpn_convs[jj](pathtd[levels - 1])
return pathtd
if __name__ == '__main__':
W_bifpn=3
D_bifpn=88
in_channels_stage2=24
in_channels_list = [
in_channels_stage2 * 2,
in_channels_stage2 * 4,
in_channels_stage2 * 8,
]
neck = BIFPN(in_channels=in_channels_list,
out_channels=W_bifpn,
stack=D_bifpn,
num_outs=5)
网上又出了另一版本的: