YOLOv3原理讲解之Anchor Box

Anchor Box

在讲解YOLO之前,有必要先解释什么是Anchor box。
在做目标检测任务时,我们首先需要将数据标注并得到训练集、验证集、测试集。已标注的数据label,其实就是在原始图片中用矩形框框出目标,得到的矩形框参数(中心点坐标、长、宽)就是label。Anchor box其实就是从训练集中将所有的矩形框的大小尺寸统计处最常出现的某几个矩形框,这里我们可以采用K-Means来得到。这样也就不难理解使用anchor的目的了:目标先验。即我们提前告诉模型,应该去用多大的矩形框去寻找目标,帮助模型快速收敛。

在使用K-Means前,需要将标注数据转换获得每个bounding box(标注框)的高宽并归一化:
(假设你的标注数据是voc数据集的格式)

import numpy as np
import glob
import xml.etree.ElementTree as ET
# normalize width and height
def normal_wh(path):
    dataset = list()
    for xml_file in glob.glob('{}/*xml'.format(path)):
        tree = ET.parse(xml_file)
        height = int(tree.findtext('./size/height'))
        width = int(tree.findtext('./size/width))
        for obj in tree.iter('object'):
            xmin = int(obj.findtext('bndbox/xmin')) / width
            ymin = int(obj.findtext('bndbox/ymin')) / height
            xmax = int(obj.findtext('bndbox/xmax')) / width
            ymax= int(obj.findtext('bndbox/ymax')) / height
            dataset.append([xmax-xmin, ymax-ymin])
    return np.array(dataset)

接下来就是使用K-Means算法将刚刚处理后的bounding box的高宽集合进行分类,按照YOLOv3论文中设定的9类。K-Means中的距离计算使用1-IOU,每个bounding box的坐标由其高宽表示。记住:我们要寻找的是高宽相似的bounding box聚类后中心box作为anchor box。

import numpy as np

def iou(bbox, clusters):
    """
    计算某一个bounding box与当前的聚类中心框(这里为9个)的交并比。
    :param bbox:单个待归类的bounding box
    :param clusters:当前的聚类中心框
    :return: 交并比
    """
    # 将clusters中的中心框宽限定不大于当前box的宽
    x = np.minimum(clusters[:, 0], box[0])
    # 将clusters中的中心框高限定不大于当前box的高
    y = np.minimum(clusters[:, 1], box[1])
    if np.count_nonzero(x == 0) > 0 or np.np.count_nonzero(y == 0) > 0:
        raise ValueError('This box is invalid!')

    intersection = x * y
    box_area = box[0] * box[1]
    clusters_area = clusters[:, 0] * clusters[:, 1]
    #交并比公式
    box_iou = intersection / (box_area + cluster_area - intersection)

    return box_iou

解释一下上面的intersection:我们用某一个box去和clusters中每一个中心框求交并比,所以主要地,也就是两个框的共同覆盖区域。那么,共同覆盖区域自然地也就是两者高宽中较小的,因此求得了任意两个框的共同覆盖区域=intersection。

接下来便是K-Means算法:

def kmeans(boxes, k, dist=np.median):
    """
    使用任意两个框的交并比作为距离矩阵进行K-Means计算
    :params boxes: numpy array of shape (r, 2), where r is the number of rows
    :param k: number of clusters
    :param dist: distance function
    :return: numpy array of shape (k, 2)
    """
    rows = boxes.shape[0]
    #距离矩阵 r * k
    distances = np.empty((rows, k))
    #上一次聚类中心,用以记录算法是否收敛
    last_clusters = np.zeros((rows, ))
    np.random.seed(42)
    #初始化聚类中心,随机选择k个框
    clusters = boxes[np.random.choice(rows, k, replace=False)]
    while True:
        for row in range(rows):
            #定义距离公式:d[box, centroid] = 1 - IOU(box, centroid)
            #这里有个矛盾的地方,我们希望IOU越大越好,而距离越小越好
            #所以距离的设定为1-IOU
            distances[row] = 1 - iou(boxes[row], clusters)
        #将当前框分配给距离最近的聚类中心
        nearest_clusters = np.argmin(distances, axis=1)
        #cluster是否已经收敛:每个中心不再变化(all)则终止循环
        if (last_clusters == nearest_clusters).all():
            break
        #更新每个聚类的中心,这里将每个类中的中位数作为新的中心
        for cluster in range(k):
            clusters[cluster] = dist(boxes[neasrest_clusters == cluster], axis=0)
        last_clusters = clusters
    return clusters

解释一下为什么要用中位数来计算和更新聚类中心:根据参考1中的原因,在YOLO9000论文中,作者在VOC2007数据集上的结果为:

YOLO9000 IOU结果

可是,如果我们选择平均数来计算的话会发现很难得到这个结果。于是参考1的作者选择了中位数的方法,得到的结果如下:

5个cluster
9个cluster

可以看到,这个结果十分接近论文的结果。而且K-means的初始状态会对结果有一定的影响,所以可能每次的结果会有浮动。我在代码里将随机因子设定为固定值。

参考:

  1. https://github.com/lars76/kmeans-anchor-boxes/blob/master/kmeans.py
  2. https://www.cnblogs.com/lzq116/p/12145673.html
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354