目标检测 SSD: Single Shot MultiBox Detector - L2Norm模块处理conv4_3的特征输出
conv4_3特征输出之前需要经过L2Norm模块
conv4_3的特征和后面几层feature map的数值大小不匹配,归一化之后数值大小统计,取值范围相同,有利于模型收敛
方式1
import torch
import torch.nn as nn
from torch.autograd import Function
from torch.autograd import Variable
import torch.nn.init as init
class L2Norm(nn.Module):
def __init__(self,n_channels, scale):
super(L2Norm,self).__init__()
self.n_channels = n_channels
self.gamma = scale or None
self.eps = 1e-10
self.weight = nn.Parameter(torch.Tensor(self.n_channels))
self.reset_parameters()
def reset_parameters(self):
init.constant_(self.weight,self.gamma)
def forward(self, x):
norm = x.pow(2).sum(dim=1, keepdim=True).sqrt()+self.eps
x = torch.div(x,norm)
out = self.weight.unsqueeze(0).unsqueeze(2).unsqueeze(3).expand_as(x) * x
return out
方式2
import torch
import torch.nn.init as init
import torch.nn as nn
import torch.nn.functional as F
class L2NormScale(nn.Module):
def __init__(self, n_channels, init_scale):
super(L2NormScale, self).__init__()
self.n_channels = n_channels
self.init_scale = init_scale
self.weight = nn.Parameter(torch.Tensor(self.n_channels))
init.constant(self.weight, self.init_scale)
def forward(self, x):
x = F.normalize(x, dim=1)
x = self.weight.unsqueeze(0).unsqueeze(2).unsqueeze(3).expand_as(x) * x
return x
PyTorch封装好的函数
torch.nn.functional.normalize(input, p=2, dim=1, eps=1e-12, out=None)
对指定维度执行 规范化
对于默认参数, 它使用沿维度的Euclidean范数进行标准化.
v
=
v
max
(
∥
v
∥
p
,
ϵ
)
v=\frac{v}{\max \left(\|v\|_{p}, \epsilon\right)}
v=max(∥v∥p,ϵ)v
参数:
input – 任意形状的输入张量
p (float) – 范数公式中的指数值. 默认值: 等于2就是2范数
dim (int) – 进行规约的维度. 默认值: 1,一般是通道维度
eps (float) – 避免除以零的小值. 默认值: 1e-12
out (Tensor, 可选的) – 输出张量. 如果 out 被设置, 此操作不可微分.
不封装直接写的方式
norm = conv4_3_feats.pow(2).sum(dim=1, keepdim=True).sqrt()+1e-10 # (N, 1, 38, 38)
conv4_3_feats = conv4_3_feats / norm # (N, 512, 38, 38)
conv4_3_feats = conv4_3_feats * self.rescale_factors # (N, 512, 38, 38)
mmdetection 里的实现
class L2Norm(nn.Module):
def __init__(self, n_dims, scale=20., eps=1e-10):
super(L2Norm, self).__init__()
self.n_dims = n_dims
self.weight = nn.Parameter(torch.Tensor(self.n_dims))
self.eps = eps
self.scale = scale
def forward(self, x):
# normalization layer convert to FP32 in FP16 training
x_float = x.float()
norm = x_float.pow(2).sum(1, keepdim=True).sqrt() + self.eps
return (self.weight[None, :, None, None].float().expand_as(x_float) *
x_float / norm).type_as(x)