• ssd 的anchor生成详解


    最近面试,被各种问,特别被问到一些很细节的东西我一知半解,特尴尬。遂下定决心看懂并记住每一个细节!不怕被问!
    ssd的anchor是如何生成的?
    首先需要了解一些参数数值的意义:

    # SSD300 CONFIGS
    voc = {
        'num_classes': 21,
        'lr_steps': (80000, 100000, 120000),
        'max_iter': 1200000,
        'feature_maps': [38, 19, 10, 5, 3, 1],
        'min_dim': 300,
        'steps': [8, 16, 32, 64, 100, 300],
        'min_sizes': [30, 60, 111, 162, 213, 264],
        'max_sizes': [60, 111, 162, 213, 264, 315],
        'aspect_ratios': [[2], [2, 3], [2, 3], [2, 3], [2], [2]],
        'variance': [0.1, 0.2],
        'clip': True,
        'name': 'VOC',
    }
    

    直接上anchor的生成代码:prior_box.py

    from __future__ import division
    from math import sqrt as sqrt
    from itertools import product as product
    import torch
    
    # SSD300 CONFIGS
    # voc = {
    #     'num_classes': 21,
    #     'lr_steps': (80000, 100000, 120000),
    #     'max_iter': 1200000,
    #     'feature_maps': [38, 19, 10, 5, 3, 1],
    #     'min_dim': 300,
    #     'steps': [8, 16, 32, 64, 100, 300],
    #     'min_sizes': [30, 60, 111, 162, 213, 264],
    #     'max_sizes': [60, 111, 162, 213, 264, 315],
    #     'aspect_ratios': [[2], [2, 3], [2, 3], [2, 3], [2], [2]],
    #     'variance': [0.1, 0.2],
    #     'clip': True,
    #     'name': 'VOC',
    # }
    
    
    class PriorBox(object):
        """Compute priorbox coordinates in center-offset form for each source
        feature map.
        """
        def __init__(self, cfg):
            super(PriorBox, self).__init__()
            self.image_size = cfg['min_dim']
            # number of priors for feature map location (either 4 or 6)
            self.num_priors = len(cfg['aspect_ratios'])
            self.variance = cfg['variance'] or [0.1]
            self.feature_maps = cfg['feature_maps']
            self.min_sizes = cfg['min_sizes']
            self.max_sizes = cfg['max_sizes']
            self.steps = cfg['steps']
            self.aspect_ratios = cfg['aspect_ratios']
            self.clip = cfg['clip']
            self.version = cfg['name']
            for v in self.variance:
                if v <= 0:
                    raise ValueError('Variances must be greater than 0')
    
        def forward(self):
            mean = []
            for k, f in enumerate(self.feature_maps): #[38, 19, 10, 5, 3, 1],
                for i, j in product(range(f), repeat=2):
                    f_k = self.image_size / self.steps[k]
                    # unit center x,y
                    cx = (j + 0.5) / f_k
                    cy = (i + 0.5) / f_k
    
                    # aspect_ratio: 1
                    # rel size: min_size
                    s_k = self.min_sizes[k]/self.image_size
                    mean += [cx, cy, s_k, s_k]
    
                    # aspect_ratio: 1
                    # rel size: sqrt(s_k * s_(k+1))
                    s_k_prime = sqrt(s_k * (self.max_sizes[k]/self.image_size))
                    mean += [cx, cy, s_k_prime, s_k_prime]
    
                    # rest of aspect ratios
                    for ar in self.aspect_ratios[k]:
                        mean += [cx, cy, s_k*sqrt(ar), s_k/sqrt(ar)]
                        mean += [cx, cy, s_k/sqrt(ar), s_k*sqrt(ar)]
            # back to torch land
            output = torch.Tensor(mean).view(-1, 4) ##[8732,4]
            if self.clip:
                output.clamp_(max=1, min=0)
            return output
    

    这里注意init里面初始化的一些参数就是上面voc里面的。这里主要是用到了
    'feature_maps': [38, 19, 10, 5, 3, 1], 是6个feature map的大小
    'steps': [8, 16, 32, 64, 100, 300], 是下采样的倍数
    'min_sizes': [30, 60, 111, 162, 213, 264], 相对于300大小
    'max_sizes': [60, 111, 162, 213, 264, 315],相对于300大小
    'aspect_ratios': [[2], [2, 3], [2, 3], [2, 3], [2], [2]],
    'clip': True,
    然后我们来解析anchor是如何生成的。
    上面用product生成笛卡尔坐标,遍历每个像素位置,也就是说为每个像素位置配上anchor。
    关于product,https://www.cnblogs.com/yanghailin/p/14769384.html,可以看这里3.itertools.product
    或者这里简单介绍一下:
    product(A,B)函数,返回A和B中的元素组成的笛卡尔积的元组,
    itertools.product(*iterables, repeat=1)
    iterables是可迭代对象,repeat指定iterable重复几次,即:
    product(A,repeat=3)等价于product(A,A,A)

    import itertools
    for item in itertools.product([1,2,3,4],[100,200]):
        print(item)
        '''
    (1, 100)
    (1, 200)
    (2, 100)
    (2, 200)
    (3, 100)
    (3, 200)
    (4, 100)
    (4, 200)
        '''
    
     for k, f in enumerate(self.feature_maps): #[38, 19, 10, 5, 3, 1],
                for i, j in product(range(f), repeat=2):
    

    cx,cy是feature map上面的每个点,然后再配上w和h。
    注意anchor的四个值都是相对值,都是0-1的小数。

    mean一般而言是4个,但是aspect_ratios个数是2的话,就是6个。
    这里for循环多一轮,就会多出2个,所以是6个

    for ar in self.aspect_ratios[k]:
      mean += [cx, cy, s_k*sqrt(ar), s_k/sqrt(ar)]
      mean += [cx, cy, s_k/sqrt(ar), s_k*sqrt(ar)]
    

    s_k = self.min_sizes[k]/self.image_size
    s_k_prime = sqrt(s_k * (self.max_sizes[k]/self.image_size))

    [cx, cy, s_k, s_k]
    [cx, cy, s_k_prime, s_k_prime]
    [cx, cy, s_k*sqrt(ar), s_k/sqrt(ar)]  ##ar取2或者3
    [cx, cy, s_k/sqrt(ar), s_k*sqrt(ar)]
    

    从上面计算规则来看,前面两个是正方形,一个大的一个小的正方形。
    然后后面两个是矩形,一个长的矩形,一个宽的矩形。
    当k=0,f=38的时候,featuremap的大小是38,ar只有一个2,所以是一个点对应4个anchor。画图如下:

    然后feature map 等于38,19, 10, 5, 3, 1也是类似的,只不过
    'aspect_ratios': [[2], [2, 3], [2, 3], [2, 3], [2], [2]],
    19, 10, 5的时候aspect_ratios个数为2,一个点就会对应6个anchor。
    所以,整个6个大循环下来,得到的anchor的总的个数为:

    还有一点就是
    'min_sizes': [30, 60, 111, 162, 213, 264], 相对于300大小
    'max_sizes': [60, 111, 162, 213, 264, 315],相对于300大小
    相对于300大小,因为看到代码里面
    s_k = self.min_sizes[k]/self.image_size
    都是除以的self.image_size(300)。
    然后'feature_maps': [38, 19, 10, 5, 3, 1], 是6个feature map的大小
    其实这里就可以看出,在feature等于38的尺寸上面,是最大的尺寸,找的最小的min_sizes是30(相对于300),所以可以看到是在浅层特征上面找小目标!

    然后anchor的格式就是下面这些值。

    tensor([[0.0133, 0.0133, 0.1000, 0.1000],
            [0.0133, 0.0133, 0.1414, 0.1414],
            [0.0133, 0.0133, 0.1414, 0.0707],
            ...,
            [0.5000, 0.5000, 0.9612, 0.9612],
            [0.5000, 0.5000, 1.2445, 0.6223],
            [0.5000, 0.5000, 0.6223, 1.2445]])
    

    最后,这里详细讲解了anchor的生成方式,其实,真的,all in code。
    这里再详细不过了,要是下次再被问到,不要答不上来哦~
    主要就是这4个,记住就好:

    [cx, cy, s_k, s_k]
    [cx, cy, s_k_prime, s_k_prime]
    [cx, cy, s_k*sqrt(ar), s_k/sqrt(ar)]  ##ar取2或者3
    [cx, cy, s_k/sqrt(ar), s_k*sqrt(ar)]
    

    ~~~~~2021年06月11日09:42:46 更新~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    这里有一个很重要的细节!我是看到后面计算loss的时候才回过头来看的。因为后面看到truths直接与这里生成的anchor做相交运算。

    overlaps = jaccard(
            truths,
            point_form(priors)
        )
    

    我就在想怎么能直接做交并比呢,因为truths相对于300300的比例,难道这里的anchor也是相对于300300的吗?
    然后仔细看这里的代码,发现一个细节!
    就是

     for k, f in enumerate(self.feature_maps): #[38, 19, 10, 5, 3, 1],
                for i, j in product(range(f), repeat=2):
                    f_k = self.image_size / self.steps[k]
                    # unit center x,y
                    cx = (j + 0.5) / f_k
                    cy = (i + 0.5) / f_k
    
                    # aspect_ratio: 1
                    # rel size: min_size
                    s_k = self.min_sizes[k]/self.image_size
                    mean += [cx, cy, s_k, s_k]
    

    第一循环是step=8, f_k = self.image_size / self.steps[k]-->f_k = 300/8=37.5
    所以可以看到cx = (j + 0.5) / f_k 和 cy = (i + 0.5) / f_k都是相对于第一层feature map大小38的。
    但是看到w和h都是相对于self.image_size(300)的! s_k = self.min_sizes[k]/self.image_size。
    然后整出的anchor mean += [cx, cy, s_k, s_k]可以直接和在300上面的比例直接做交并比运算。想想确实是可以的!
    因为你300下采样8倍的feature map38上面,38每移动一个点在300上面相当于移动了8个点。所以,位置是相对的,映射到300上面就是乘以倍数移动。举例

    j=3时候
    cx=(j+0.5)/37.5=0.09333       0.09333*300=27.999
    j=4时候
    cx=(j+0.5)/37.5=0.12       0.12*300=36
    

    可见,36-28=8,j变化了1,在300上面就变化了8,就是300在下采样8倍38的大小上,每移动1位就相当于在300上移动了8。
    所以后面把在38大小上面的比例用在300上面,就是默认这种行为(每移动1位就相当于在300上移动了8)。但是他对应的宽和高还是按照300的比例来的。这里没有毛病。
    只是位置有个倍移,宽和高还是按照300上面的比例来。
    所以这里得到的anchor可以直接和groundtruth做交并比。

    额外链接:
    深入理解anchor https://blog.csdn.net/qianqing13579/article/details/82106664

    好记性不如烂键盘---点滴、积累、进步!
  • 相关阅读:
    CSS样式表
    lianxi!
    传值
    lei!
    3.10
    if else&& stwitch break
    if else 语句
    2016.3.6
    进制转换
    PHP 面向对象的三大特征
  • 原文地址:https://www.cnblogs.com/yanghailin/p/14868575.html
Copyright © 2020-2023  润新知