YOLOv3 代码详解(6) —— anchorboxes的获取 kmeans.py

前言:

yolo系列的论文阅读
论文阅读 || 深度学习之目标检测 重磅出击YOLOv3
论文阅读 || 深度学习之目标检测yolov2
论文阅读 || 深度学习之目标检测yolov1

该篇讲解的工程连接是:
tensorflow的yolov3:https://github.com/YunYang1994/tensorflow-yolov3

自己对该工程的解析博客:
YOLOv3 || 1. 使用自己的数据集训练yolov3
YOLOv3 || 2. dataset.py解析
YOLOv3 || 3. dataset.py的多进程改造
YOLOv3 || 4. yolov3.py 网络结构的搭建和loss的定义
YOLOv3 || 5. train.py
YOLOv3 || 6. anchorboxes的获取 kmeans.py
YOLOv3 || 7. yolov3的pb文件的测试

该篇博客介绍的是yolov3的anchorboxes的获取

1 原理讲解

yolov3 训练的时候,需要提前设置 anchor boxes,不同的数据集会有不同的anchor boxes,COCO数据集官方已经提供。当我们训练自己的数据集时,就需要自己提前获取自己数据集的,具体方法如下:

  • step1:获取数据集中所有的 bounding box
    因为想要的只是anchor 的大小信息,所以舍去了位置信息、类别信息。
    将每张图片中的所有的 bbox的坐标汇总,不分类的。标签形式由坐标【左上角-右下角】,转换成【w,h】
  • step2:初始化k个anchor box
    随机从所有 bounding boxes中选取k个anchor boxes 作为初始值。
  • step3:计算每个bounding box 与每个anchor box的 IOU值
    常用的聚类方法使用欧氏距离来衡量差异,可直接对 bounding box的宽高进行聚类,产生k个宽高组合的 anchorbox。这样的结果在box尺寸较大时,误差会更大。
    以IOU为基准的聚类方式,能够避免这个问题。我们已经知道 IOU为两个框的交并比,该值越大越好,所以使用【d = 1-IOU】来作为误差,来获取每一个bounding box与k个基准anchor box的偏差d
  • step4:将所有的bounding box进行分类
    迭代每个 bounding box,在与 k个anchorbox的误差 { d i , 1 , d 1 , 2 , . . . d i , k } \{ d_{i,1},d_{1,2},...d_{i,k}\} {di,1,d1,2,...di,k}中选取误差最小的anchor。比如 b o u n d i n g b o x 0 boundingbox_{0} boundingbox0 d 0 , 3 d_{0,3} d0,3最小,则 b o u n d i n g b o x 0 boundingbox_{0} boundingbox0属于第3个anchor。
    这样我们就将所有的 boundingbox分为了k组
  • step5:anchorbox的更新
    针对每个anchorbox下的boundingbox,我们计算这些boundingbox的宽高的均值,作为该anchorbox的新的尺寸值
  • step6:重复 分类boundingbox-更新anchorbox 的操作
    重复step4-step5,使的anchorbox的值不在更新(也就是boundingbox的分类不再更新)
  • step7:将获取的k个anchor从大到小排序并保存
  • 另外,也计算下anchorbox的精确度。
    使用最后得到的anchor boxes与每个bounding box计算其IOU值,对于每个bounding box选取其最高的那个IOU值(代表其属于某一个anchor box类),然后求所有bounding box该IOU值的平均值也即最后的精确度值。

2 代码讲解

已知文本中的某一行的数据为 【./data/images/2_561.png 392,92,887,776,0】
其中第一项为图片的路径,后面数字为boxes的左上角右下角的坐标,以及目标所属类别。
distances = 1 - self.iou(boxes, clusters)

class YOLO_Kmeans:

   def __init__(self, cluster_number, filename):
       """初始化"""
       self.cluster_number = cluster_number
       self.filename = filename
       
   def iou(self, boxes, clusters):  # 1 box -> k clusters
       """计算iou"""    
       n = boxes.shape[0]
       k = self.cluster_number

       box_area = boxes[:, 0] * boxes[:, 1]
       box_area = box_area.repeat(k)
       box_area = np.reshape(box_area, (n, k))

       cluster_area = clusters[:, 0] * clusters[:, 1]
       cluster_area = np.tile(cluster_area, [1, n])
       cluster_area = np.reshape(cluster_area, (n, k))

       box_w_matrix = np.reshape(boxes[:, 0].repeat(k), (n, k))
       cluster_w_matrix = np.reshape(np.tile(clusters[:, 0], (1, n)), (n, k))
       min_w_matrix = np.minimum(cluster_w_matrix, box_w_matrix)

       box_h_matrix = np.reshape(boxes[:, 1].repeat(k), (n, k))
       cluster_h_matrix = np.reshape(np.tile(clusters[:, 1], (1, n)), (n, k))
       min_h_matrix = np.minimum(cluster_h_matrix, box_h_matrix)
       inter_area = np.multiply(min_w_matrix, min_h_matrix)

       result = inter_area / (box_area + cluster_area - inter_area)
       return result
       
   def kmeans(self, boxes, k, dist=np.median):
       """对boxes进行聚类"""    
       box_number = boxes.shape[0]
       print(boxes.shape)
       distances = np.empty((box_number, k))    # 记录boxes距离每个的预选框中的距离
       last_nearest = np.zeros((box_number,))    # 记录boxes距离预选框中最近的编号
       np.random.seed()

       # 随机选出k个预选框
       clusters = boxes[np.random.choice(box_number, k, replace=False)]  # init k clusters

       while True:
           # 计算所有框和k个预选框的(1-iou)。其中预选框随着循环不断被更新
           distances = 1 - self.iou(boxes, clusters)

           # 获取每个boxes与9个预选框中哪个距离最近
           current_nearest = np.argmin(distances, axis=1)

           if (last_nearest == current_nearest).all():
               break  # clusters won't change

           for cluster in range(k):
               # 更新预选框
               clusters[cluster] = dist(boxes[current_nearest == cluster], axis=0)
               # 其中current_nearest为向量,cluster为值。
               # current_nearest == cluster,返回的是与current_nearest一样shape的内容为true、false的数组
               # a = boxes[current_nearest == cluster],是将为true的位置上的数值获取出来,赋值给a
           last_nearest = current_nearest
           
       return clusters
       
   def getAllBoxes(self):
       """从文本中获取所有的boxes的高宽"""    
       
       f = open(self.filename, 'r')
       dataSet = []
       for line in f:
           infos = line.split(" ")
           length = len(infos)

           # 数据集的图片的尺寸可能存在大小不一,
           # 对数据集进行聚类,要保证图片的尺寸保持一致的情况下。
           image = cv2.imread(infos[0])
           ih, iw = [416, 416]
           h, w, _ = image.shape
           scale = min(iw / w, ih / h)
           # nw, nh = int(scale * w), int(scale * h)
           # # 将输入图片的长边缩放的target_size,并保持原图片的比例
           # image_resized = cv2.resize(image, (nw, nh))

           for i in range(1, length):
               width = int(float(infos[i].split(",")[2])) - int(float(infos[i].split(",")[0]))
               height = int(float(infos[i].split(",")[3])) - int(float(infos[i].split(",")[1]))

               width, height = width*scale, height*scale
               dataSet.append([width, height])
       result = np.array(dataSet)
       f.close()
       return result
       
   def avg_iou(self, boxes, clusters):
       accuracy = np.mean([np.max(self.iou(boxes, clusters), axis=1)])
       return accuracy


   def result2txt(self, data):
       f = open("./data/anchors/yolo_anchors416.txt", 'w')
       row = np.shape(data)[0]
       for i in range(row):
           if i == 0:
               x_y = "%d,%d" % (data[i][0], data[i][1])
           else:
               x_y = ", %d,%d" % (data[i][0], data[i][1])
          f.write(x_y)
       f.close()
      
     
     
if __name__ == "__main__":
   cluster_number = 9
   filename = "./data/dataset/train.txt"
   
   kmeans = YOLO_Kmeans(cluster_number, filename)
   all_boxes = kmeans.getAllBoxes()                             # step1:获取所有的框的边长
   result = kmeans.kmeans(all_boxes, k=self.cluster_number)     # step2-6:聚类得到9个代表框
   
   result = result[np.lexsort(result.T[0, None])]               # step1:对9个框进行排序
   kmeans.result2txt(result)                                    # 保存结果
   print("K anchors:\n {}".format(result))
   print("Accuracy: {:.2f}%".format(self.avg_iou(all_boxes, result) * 100))

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是根据 KMeans_cluster.py 中的数据绘制特征雷达图的示例代码: ```python import numpy as np import matplotlib.pyplot as plt from math import pi # 从 KMeans_cluster.py获取数据 data = [[ 0.57, 0.84, 0.96, 0.34, 0.68], [ 0.94, 0.67, 0.48, 0.91, 0.12], [ 0.12, 0.36, 0.61, 0.19, 0.97], [ 0.18, 0.91, 0.20, 0.72, 0.48], [ 0.43, 0.25, 0.40, 0.48, 0.85]] # 特征名称,这里使用 A、B、C、D、E 代替 features = ['A', 'B', 'C', 'D', 'E'] # 将数据转换为绘制雷达图所需的格式 # 雷达图需要将第一个特征与最后一个特征相连,因此需要将最后一个特征放到第一个位置 data = np.concatenate((data, data[:, 0:1]), axis=1) features = np.concatenate((features, features[0:1])) # 计算角度 angles = np.linspace(0, 2 * np.pi, len(features), endpoint=False) # 将角度转换为弧度 angles = angles.astype(np.float16) # 创建一个极坐标子图 fig = plt.figure() ax = fig.add_subplot(111, polar=True) # 画出雷达图 for i in range(len(data)): values = data[i] # 将第一个值与最后一个值相连 values = np.concatenate((values, values[0:1])) # 画出一条折线图 ax.plot(angles, values, linewidth=2, label=f'Cluster {i+1}') # 填充颜色 ax.fill(angles, values, alpha=0.25) # 设置雷达图的标签 ax.set_thetagrids(angles * 180 / np.pi, features) # 添加标题和图例 plt.title('Feature Radar Chart') plt.legend(loc='best') # 显示图形 plt.show() ``` 这段代码中,我们首先获取KMeans_cluster.py 中的数据,然后将数据转换为绘制雷达图所需的格式。接着,我们计算了每个特征对应的角度,并创建了一个极坐标子图。然后,我们使用 for 循环遍历每个簇的数据,分别画出一条折线图,并填充颜色。最后,我们设置了雷达图的标签、添加了标题和图例,最终显示图形。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值