正则化方式的选择:
原生态SSD中,特征金字塔不同特征层上的特征值,有不同的尺度范围(different ranges of value),SSD采取的做法为:使用L2-Normal正则化conv4_3;FSSD使用方案更简单有效:将concate后的特征,后接一个BN层,从row2、6对比可知,新增BN层可以带来0.5%的性能提升。
(1)L2 Normalization公式及作用:
对于一行向量,其L2归一化公式入下所示,其中D为向量长度:
在深度神经网络中,偶尔会出现多个量纲不同的向量拼接在一起的情况,此时就可以使用L2归一化统一拼接后的向量的量纲,使得网络能够快速收敛。
(2)mmdetection中的vgg-ssd.py中处理方式:
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)
转换为onnx->ncnn时,ncnn执行这一层的处理会出错,因为在ncnn中虽然支持Operation_POW处理,但是在此处保存的blobs数据只有一个,Operation_POW输入需要传入两个blobs。
若要满足ncnn的处理,需要将自定义的norm处理改为pytorch的算子,这样转换成ncnn就不会报错了:
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
init.constant(self.weight, self.scale)
def forward(self, x):
norm = x.norm(p=2, dim=1, keepdim=True) + self.eps
x = torch.div(x,norm)
out = self.weight.unsqueeze(0).unsqueeze(2).unsqueeze(3).expand_as(x) * x
return out
延伸阅读参考:https://blog.csdn.net/flyfish1986/article/details/105586716