news 2026/8/11 6:57:17

YOLO目标检测中基于K-Means聚类的锚框生成原理与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLO目标检测中基于K-Means聚类的锚框生成原理与实战

1. 项目概述:为什么YOLO需要自己生成Anchors?

在目标检测领域,YOLO系列算法以其速度和精度的良好平衡而闻名。无论是做工业质检、自动驾驶感知,还是开发一个智能安防应用,当你拿到一批自己标注的数据集准备训练模型时,一个绕不开的关键步骤就是配置模型的“锚框”。很多新手朋友可能会直接套用COCO或VOC数据集的预设锚框,结果训练出来的模型效果总是不尽人意,检测框要么对不上目标的大小,要么定位精度差。这背后的核心原因,就在于“锚框”与你的数据特性不匹配。

锚框,可以理解为模型在图像上预先放置的一系列“猜测框”。YOLO网络并不直接预测目标在图像中的绝对坐标,而是预测目标相对于这些预设锚框的偏移量。因此,锚框的尺寸和宽高比,必须与你数据集中真实目标框的分布高度吻合。如果你的数据集中都是接近正方形的人脸,而锚框却是为横宽比很大的车辆设计的,那模型学习起来就会事倍功半,收敛慢且精度低。

手动设计锚框是一项繁琐且需要经验的工作。而K-Means聚类算法,为我们提供了一种数据驱动的、自动化的解决方案。它通过分析你数据集中所有真实标注框的宽高,自动聚类出最具代表性的N个锚框尺寸。这个过程,就是“Anchors聚类生成”。今天,我就结合自己多次在工业项目中的实战经验,从头到尾拆解这个过程,不仅告诉你“怎么做”,更重点剖析“为什么这么做”,以及过程中那些容易踩坑的细节。

2. 核心思路与算法选型:为什么是K-Means,而不是别的?

2.1 锚框的本质与聚类目标

首先,我们要明确聚类算法的输入是什么。我们的数据集标注通常是边界框的(x_center, y_center, width, height),其中宽和高是绝对像素值。但直接对绝对像素值进行聚类有一个致命问题:它受图像原始分辨率影响极大。一张1920x1080图片中的“汽车”框,和一张416x416图片中的“汽车”框,绝对尺寸天差地别,但它们的“形状”(宽高比)可能是相似的。

因此,标准做法是对宽和高进行归一化。通常除以图像的宽度和高度,得到相对于图像尺寸的比例值(w/ img_w, h/ img_h)。这样,锚框就成为了与图像尺寸无关的、描述目标形状的相对比例。聚类的目标就是:找到K个这样的(w, h)点,使得数据集中所有真实框到其最近锚框的“距离”之和最小。

2.2 距离度量:IOU距离的引入

这里就引出了第一个关键选择:如何定义“距离”?在经典的K-Means中,我们通常使用欧氏距离。但在锚框聚类中,使用欧氏距离sqrt((w1-w2)^2 + (h1-h2)^2)合理吗?并不完全合理。

假设有两个锚框A(0.2, 0.1)和B(0.3, 0.15),一个真实框是(0.25, 0.12)。从欧氏距离看,它可能离两者都不远。但从目标检测任务的核心评价指标——交并比来看,一个0.2x0.1的框和一个0.25x0.12的框,其IOU可能很高;而一个0.3x0.15的框与其IOU可能反而低。我们的目标是最大化锚框与真实框的IOU,因此,一个更直接的距离度量是1 - IOU

在YOLO原作者Joseph Redmon的代码以及后续各种实现中,普遍采用了基于IOU的距离度量:d(box, centroid) = 1 - IOU(box, centroid)。这里的IOU计算需要一点技巧,因为我们的数据点是(w, h),没有中心坐标。通用的做法是,假设两个框的中心点重合,那么它们的交集面积就是min(w1, w2) * min(h1, h2),并集面积是w1*h1 + w2*h2 - intersection。这样计算出的IOU我们称之为“中心点重合IOU”。使用这个距离度量,K-Means会倾向于聚类出那些与真实框在形状上IOU更高的锚框,这直接对齐了我们的优化目标。

实操心得:有些开源实现为了简便,仍然使用欧氏距离。对于目标尺寸分布比较均匀的数据集,差别可能不大。但对于宽高比差异巨大(比如同时有很扁的车辆和很瘦的行人)的数据集,使用IOU距离能显著提升聚类出的锚框质量。我个人的经验是,无脑选择IOU距离,这是经过实践检验的更优方案。

2.3 K值的选择:多少个锚框算够用?

K值是K-Means算法需要预先设定的超参数,即你想生成多少个锚框。在YOLOv3/v4/v5中,通常为每个特征图预测层分配3个锚框,而常见的结构有3个预测层(例如,针对大、中、小目标),所以总的锚框数K通常是9。在YOLOv8中,官方不再需要手动配置锚框,但其内部机制依然存在。

如何确定K值?一个实用的方法是绘制“平均IOU vs. K值”曲线。具体做法是:对不同的K值(比如从3到12)分别运行聚类算法,计算聚类完成后,所有真实框与其分配到的最近锚框的平均IOU。随着K值增大,平均IOU会单调上升(因为锚框更多,总能找到更匹配的)。我们会观察曲线的“拐点”,即IOU收益开始明显下降的点,那个点对应的K值通常是一个性价比高的选择。

注意事项:K值并非越大越好。更多的锚框意味着模型输出更多的预测参数,会增加计算量和过拟合的风险。对于大多数数据集,9个锚框是一个经验上的“甜点”。如果你的数据集目标尺度非常单一(比如全是人脸),可能6个甚至更少就够了;如果尺度极其多样(从几十像素到上千像素的目标都有),可能需要考虑12个,并配合更复杂的多尺度训练策略。

3. 数据准备与预处理:磨刀不误砍柴工

3.1 标注格式解析与统一

你的标注数据可能来自LabelImg、CVAT、Roboflow等不同工具,格式可能是VOC XML、COCO JSON或YOLO自带的TXT格式。聚类脚本的第一步,就是正确解析这些格式,提取出每个边界框的归一化宽高(w, h)

以最常见的YOLO TXT格式为例,每行表示一个目标:class_id x_center y_center width height。这里的坐标和宽高都是相对于图像宽高归一化到[0, 1]的值。所以我们可以直接读取widthheight列作为我们的数据点。

# 示例:解析YOLO格式标注文件 def load_annotations(txt_path): boxes = [] with open(txt_path, 'r') as f: for line in f.readlines(): parts = line.strip().split() if len(parts) < 5: continue # 跳过空行或格式错误行 # parts[0]是类别id, parts[1:5]是x_center, y_center, width, height _, _, _, w, h = map(float, parts[:5]) boxes.append([w, h]) # 注意:这里直接使用归一化的w, h return boxes

对于VOC或COCO格式,你需要根据图像的实际尺寸,将绝对坐标(xmin, ymin, xmax, ymax)转换为归一化的(x_center, y_center, width, height)

常见问题:如果你的数据集图像尺寸不统一怎么办?这是非常常见的情况。归一化正是为了解决这个问题。无论原图是640x480还是1920x1080,归一化后的宽高比例都消除了绝对尺寸的影响。聚类过程只关心目标的形状比例,不关心其绝对大小。后续训练时,输入图像会被统一缩放到一个固定尺寸(如640x640),锚框的尺寸也是相对于这个网络输入尺寸来理解的。

3.2 数据清洗与过滤

不是所有标注框都适合用来聚类。在投入聚类前,进行必要的数据清洗能提升结果质量:

  1. 过滤无效框:宽或高为0的框,或者标注明显错误的框(如width > 1height > 1,这超出了归一化范围)。
  2. 考虑类别平衡:如果你的数据集类别极度不平衡(例如,“汽车”标注有10万个,“交通锥”只有100个),直接用所有框聚类,结果会被大类别主导。你可以选择对每个类别的框进行采样,或者按类别分别聚类后再合并筛选。对于一般应用,如果所有目标都是你需要检测的,且尺度分布跨类别相似,直接使用全部数据即可。
  3. 处理极端宽高比:数据集中可能存在一些极其瘦长或扁平的框(如电线杆、地平线)。这些框数量虽少,但会强烈影响聚类中心的位置。你需要根据业务场景决定:如果这些极端目标也是你的检测对象,那么应该保留;如果它们是标注噪声或非关注目标,可以考虑过滤掉(例如,设定宽高比w/h > 10< 0.1的框不参与聚类)。
# 示例:简单的数据过滤 filtered_boxes = [] for w, h in all_boxes: # 过滤无效和极端框 if w <= 0 or h <= 0 or w > 1 or h > 1: continue aspect_ratio = w / h if aspect_ratio > 20 or aspect_ratio < 0.05: # 过滤极端宽高比 continue filtered_boxes.append([w, h])

4. K-Means聚类算法实现细节

4.1 基于IOU距离的K-Means核心代码

下面我们实现一个使用IOU距离的K-Means聚类。这里假设输入数据data是一个Nx2的数组,每一行是[width, height]

import numpy as np import random def iou(box, clusters): """ 计算一个框box与一组聚类中心clusters的IOU。 box: [w, h] clusters: Kx2 矩阵,每一行是一个聚类中心 [w, h] 假设中心点重合。 """ # 计算交集面积 inter_area = np.minimum(box[0], clusters[:, 0]) * np.minimum(box[1], clusters[:, 1]) # 计算并集面积 box_area = box[0] * box[1] cluster_area = clusters[:, 0] * clusters[:, 1] union_area = box_area + cluster_area - inter_area # 计算IOU,避免除零 iou = inter_area / (union_area + 1e-16) return iou def kmeans_iou(data, k, max_iter=100, tol=1e-4): """ 基于IOU距离的K-Means聚类。 data: Nx2 矩阵,每行一个归一化的 [w, h] k: 聚类数量 max_iter: 最大迭代次数 tol: 中心点变化容忍度,用于提前停止 """ num_samples = data.shape[0] # 1. 初始化聚类中心:随机选择k个数据点 indices = random.sample(range(num_samples), k) centers = data[indices].copy() # 用于记录每个样本所属的簇 labels = np.zeros(num_samples, dtype=int) for iteration in range(max_iter): # 2. 分配步骤:将每个数据点分配到IOU最大的中心 distances = np.zeros((num_samples, k)) for i in range(num_samples): # 计算与所有中心的IOU,距离定义为 1 - IOU iou_val = iou(data[i], centers) distances[i] = 1 - iou_val # 每个点选择距离最小(即IOU最大)的簇 new_labels = np.argmin(distances, axis=1) # 如果分配结果没有变化,提前结束 if np.all(new_labels == labels): print(f"迭代 {iteration} 次后收敛") break labels = new_labels.copy() # 3. 更新步骤:重新计算聚类中心 new_centers = np.zeros_like(centers) for j in range(k): # 找到属于第j簇的所有点 cluster_points = data[labels == j] if len(cluster_points) > 0: # 更新中心为该簇内所有点的均值 new_centers[j] = cluster_points.mean(axis=0) else: # 如果某个簇没有点,则重新随机初始化一个中心 new_centers[j] = data[random.randint(0, num_samples-1)] # 检查中心点变化是否小于容忍度 center_shift = np.sqrt(((new_centers - centers) ** 2).sum(axis=1)).max() if center_shift < tol: print(f"迭代 {iteration} 次后中心点变化小于容忍度 {tol}") break centers = new_centers.copy() # 计算最终的平均IOU avg_iou = 0 for i in range(num_samples): avg_iou += np.max(iou(data[i], centers)) avg_iou /= num_samples return centers, labels, avg_iou

4.2 聚类中心的排序与适配YOLO格式

聚类得到的中心点centers是归一化的(w, h)。但YOLO配置文件(如.yaml.cfg)中需要的锚框尺寸,通常是相对于网络输入尺寸的绝对像素值。

假设你的YOLO网络训练时输入的图像尺寸是img_size=640。那么,你需要将归一化的锚框尺寸转换回去:

img_size = 640 anchors_pixel = centers * img_size # centers是Kx2的矩阵

接下来,通常需要对锚框进行排序。YOLO的预测层是从大到小(或从小到大)对应不同尺度的目标。因此,将锚框按面积(w * h)排序是一个好习惯,便于后续手动分配到不同的特征层。

# 按面积排序 areas = anchors_pixel[:, 0] * anchors_pixel[:, 1] sorted_indices = np.argsort(areas) # 升序索引,面积小的在前 sorted_anchors = anchors_pixel[sorted_indices] # 打印结果,格式化为YOLO配置文件需要的样式 print("生成的锚框(相对于输入尺寸{}x{}):".format(img_size, img_size)) for i, (w, h) in enumerate(sorted_anchors): print(f" - [{int(w)}, {int(h)}]") # 输出可能类似:[[10,13], [16,30], [33,23], [30,61], [62,45], [59,119], [116,90], [156,198], [373,326]]

实操心得img_size的选择至关重要。它必须与你训练时的输入图像尺寸一致。如果你使用多尺度训练(例如YOLOv5的--img-size 640参数,但实际训练时会在一定范围内随机缩放),那么应该以基准尺寸进行聚类。通常,使用你设定的固定尺寸或随机缩放范围的下限(如640)是安全的。我习惯用640,因为这是许多预训练模型的基准尺寸,兼容性好。

5. 评估与调优:你的锚框到底好不好?

生成锚框后,不能直接拿来就用,必须进行评估。

5.1 核心评估指标:平均IOU与召回率

  1. 平均IOU:上面代码中已经计算了。它表示所有真实框与其最佳匹配锚框的平均重合度。这个值越高,说明锚框的形状与数据分布越匹配。一般来说,对于K=9,平均IOU能达到0.7以上就算不错,0.75以上很好,超过0.8说明匹配度极高。
  2. 最佳匹配召回率:这是一个更严格的指标。我们设定一个IOU阈值(例如0.5,这是目标检测中常用的正样本阈值)。计算有多少比例的真实框,能找到至少一个与之IOU超过该阈值的锚框。这个指标反映了锚框覆盖目标形状的“广度”。理想情况下,我们希望召回率接近100%。如果召回率很低(比如<80%),说明有很多目标形状没有被任何锚框很好地覆盖,需要增加K值或检查数据。
def evaluate_anchors(data, anchors, iou_threshold=0.5): """ 评估锚框质量。 data: 真实框数据 [N, 2] anchors: 生成的锚框 [K, 2] iou_threshold: 判定为“匹配”的IOU阈值 """ num_data = data.shape[0] best_iou_per_gt = np.zeros(num_data) for i in range(num_data): iou_val = iou(data[i], anchors) # 计算与所有锚框的IOU best_iou_per_gt[i] = np.max(iou_val) avg_iou = np.mean(best_iou_per_gt) recall = np.mean(best_iou_per_gt > iou_threshold) * 100 print(f"平均IOU: {avg_iou:.4f}") print(f"IOU阈值 {iou_threshold} 下的最佳匹配召回率: {recall:.2f}%") # 可视化IOU分布 import matplotlib.pyplot as plt plt.hist(best_iou_per_gt, bins=50, alpha=0.7) plt.xlabel('Best IOU') plt.ylabel('Count') plt.title('Distribution of Best IOU between GT and Anchors') plt.axvline(x=iou_threshold, color='r', linestyle='--', label=f'Threshold={iou_threshold}') plt.legend() plt.show() return avg_iou, recall

5.2 可视化分析:锚框与数据分布的对比

将你数据集中所有真实框的(w, h)散点图画出来,同时将生成的锚框用醒目的标记(如红色五角星)叠加在上面。这能给你最直观的感受:锚框是否落在了数据点密集的区域?是否覆盖了主要的分布范围?

def plot_anchors_vs_data(data, anchors, img_size=640): """ 绘制真实框分布与锚框位置。 """ data_pixel = data * img_size anchors_pixel = anchors * img_size plt.figure(figsize=(10, 10)) plt.scatter(data_pixel[:, 0], data_pixel[:, 1], s=1, alpha=0.3, label='Ground Truth Boxes') plt.scatter(anchors_pixel[:, 0], anchors_pixel[:, 1], s=200, marker='*', c='red', edgecolors='black', linewidths=1.5, label='Generated Anchors') for i, (w, h) in enumerate(anchors_pixel): plt.text(w, h, f'{i}', fontsize=12, ha='center', va='center', color='darkred', weight='bold') plt.xlabel('Width (pixels)') plt.ylabel('Height (pixels)') plt.title('Ground Truth Box Distribution vs. Anchor Boxes') plt.legend() plt.grid(True, alpha=0.3) plt.axis('equal') # 保证x轴和y轴比例相同,便于观察形状 plt.show()

通过这个图,你可以立刻发现一些问题:如果某个锚框孤零零地落在数据点稀疏的区域,那它可能是在拟合噪声,可以考虑在后续手动微调或删除。如果数据点密集的区域没有锚框覆盖,那可能需要增加K值。

6. 高级技巧与实战避坑指南

6.1 多尺度数据集的聚类策略

如果你的数据集目标尺度跨度极大(例如,同时有几十像素的小目标和上千像素的大目标),直接聚类可能会使小目标的锚框被大目标的数据点“淹没”。因为K-Means是基于距离的,大目标框在数值上波动范围大,更容易影响中心点位置。

解决方案

  1. 分层聚类:先将数据按面积(w*h)分为大、中、小三组,然后对每组分别进行K-Means(例如每组聚3个类),最后将结果合并。这能保证每个尺度区间都有足够的锚框代表。
  2. 加权K-Means:在计算距离或更新中心点时,给不同尺度的框赋予不同的权重。例如,可以给中小目标更高的权重,以确保它们也能得到好的锚框。不过,权重的设置需要一些实验。
  3. 使用对数空间:对宽和高取对数(log(w), log(h))再进行欧氏距离聚类。这相当于在几何尺度上衡量差异,能缓解数值量级差异带来的问题。但要注意,最终生成的锚框需要取指数变换回来。
# 分层聚类示例(简化版) def hierarchical_kmeans(data, k_total=9, scale_groups=3): """ 将数据按面积分组后分别聚类。 scale_groups: 分成几组(例如3组:小、中、大) k_total: 总锚框数,需能被scale_groups整除 """ k_per_group = k_total // scale_groups areas = data[:, 0] * data[:, 1] # 按面积分位数分组 percentiles = np.percentile(areas, [33, 67]) # 三分位点 group_indices = [] group_indices.append(np.where(areas <= percentiles[0])[0]) # 小目标 group_indices.append(np.where((areas > percentiles[0]) & (areas <= percentiles[1]))[0]) # 中目标 group_indices.append(np.where(areas > percentiles[1])[0]) # 大目标 all_centers = [] for indices in group_indices: if len(indices) > k_per_group: group_data = data[indices] centers, _, _ = kmeans_iou(group_data, k=k_per_group) all_centers.extend(centers) else: # 如果该组数据太少,直接使用这些数据点作为中心(或重复采样) all_centers.extend(data[indices]) # 合并所有中心,如果数量不够k_total,可以补随机点或重复 all_centers = np.array(all_centers) # 可能需要再次运行一次全局K-Means对合并的中心进行微调 final_centers, _, avg_iou = kmeans_iou(data, k=k_total, init_centers=all_centers[:k_total]) return final_centers, avg_iou

6.2 与YOLO训练流程的集成

生成锚框后,如何用到YOLO训练中?

  1. YOLOv5/PyTorch版:修改模型配置文件(如yolov5s.yaml)中的anchors列表。将生成的锚框按面积排序后,通常分成三组,分别对应P3/8, P4/16, P5/32三个特征层(检测小、中、大目标)。你需要根据你的模型结构来确定分组方式。YOLOv5的锚框配置格式如下:

    anchors: - [10,13, 16,30, 33,23] # P3/8 小目标层 - [30,61, 62,45, 59,119] # P4/16 中目标层 - [116,90, 156,198, 373,326] # P5/32 大目标层

    如何分配?一个经验法则是:计算每个锚框的面积,将最小的3个分配给检测小目标的浅层特征图,最大的3个分配给检测大目标的深层特征图,中间的分配给中层。最准确的方法是分析每个特征层上先验框的感受野,但这比较复杂。通常按面积排序分组就能取得不错的效果。

  2. YOLOv3/v4 (Darknet版):修改.cfg配置文件中的[yolo]层下的anchors参数。格式是连续的浮点数,注意Darknet的anchors是绝对像素值,且顺序是width, height配对出现。

  3. YOLOv8:官方设计是自适应计算锚框,理论上不需要手动指定。但在某些特殊数据集上,关闭自适应功能并手动指定聚类出的锚框,有时能带来微弱的性能提升。这需要通过实验验证。

踩坑实录:有一次我在一个无人机航拍数据集上训练,目标都是地面上的车辆和行人,尺寸相对集中。我使用了COCO的锚框,结果mAP一直上不去。后来用自己的数据聚类生成锚框后,mAP提升了近5个百分点。关键教训:永远不要无脑使用默认锚框,特别是当你的数据分布与通用数据集(如COCO)差异较大时。花半小时聚类一下,回报可能非常显著。

6.3 迭代优化与自动化脚本

在实际项目中,数据集可能会不断更新和扩充。一个好的实践是将锚框生成脚本集成到你的数据预处理流水线中。每次数据有重大更新时,重新运行一次聚类,更新模型配置。

你可以编写一个自动化脚本,完成以下工作:

  1. 遍历所有标注文件,收集所有边界框。
  2. 运行K-Means聚类(可尝试不同的K值和初始化方法)。
  3. 评估聚类结果(平均IOU、召回率)。
  4. 可视化锚框与数据分布。
  5. 自动将最佳锚框按格式写入模型的配置文件。
  6. (可选)与训练脚本联动,在训练开始前自动检查并提示锚框是否需要更新。

7. 常见问题排查与解决方案

在实际操作中,你可能会遇到以下问题:

问题1:聚类结果不稳定,每次运行得到的锚框差异很大。

  • 原因:K-Means对初始中心点的选择敏感。随机初始化可能导致结果陷入不同的局部最优。
  • 解决方案
    1. 使用K-Means++算法进行初始化,它通过一种概率方法选择相距较远的点作为初始中心,能有效改善稳定性和结果质量。许多库(如sklearn.cluster.KMeans)默认就使用K-Means++。
    2. 多次运行(例如10次),选择平均IOU最高的那一次结果。
    3. 固定随机数种子(random.seed()np.random.seed()),确保结果可复现。

问题2:生成的某个锚框宽高比非常极端(比如宽是高的20倍),这正常吗?

  • 原因:你的数据集中可能存在此类极端形状的目标(例如横跨图像的道路标志线、垂直的灯柱)。
  • 排查:回到数据可视化步骤,检查散点图。如果确实有少量数据点分布在那个极端区域,那么这个锚框是合理的,它就是为了匹配那些目标。
  • 决策:如果这类目标不是你的主要检测对象,或者你认为这是标注噪声,可以在聚类前就将这些极端宽高比的框过滤掉(如本章第3.2节所述)。

问题3:平均IOU很高(>0.8),但模型训练后的精度提升不明显。

  • 原因:锚框质量只是影响模型性能的众多因素之一。数据质量(标注准确性、类别平衡)、模型架构、超参数设置、训练策略等都可能成为瓶颈。
  • 排查步骤
    1. 检查召回率:可能平均IOU高是因为大多数框匹配得好,但有一小部分关键目标(如小目标)没有锚框能匹配(召回率低)。重点提升这部分目标的锚框覆盖。
    2. 检查锚框分配:你是否正确地将聚类出的锚框分配给了合适的特征层?错误的分配会导致锚框与特征图感受野不匹配。
    3. 进行消融实验:在完全相同的训练设置下,仅替换锚框,比较验证集mAP。确保对比是公平的。

问题4:使用聚类生成的锚框后,训练初期损失值变得异常大或不稳定。

  • 原因:新的锚框尺寸可能与模型预训练权重(如果有的话)所隐含的先验分布差异巨大,导致网络需要更剧烈的调整。
  • 解决方案
    1. 更长的热身期:在训练开始时使用较小的学习率进行一段时间的热身,让网络慢慢适应新的锚框。
    2. 考虑微调:如果是从预训练模型开始,可以尝试先冻结骨干网络(Backbone)训练几轮,让检测头(Head)先适应新的锚框,再解冻进行全网络训练。
    3. 检查数值范围:确认生成的锚框像素值没有异常(例如,超过网络输入尺寸)。这会导致计算出的IOU等指标异常。

问题5:我的数据集图像尺寸不固定,应该如何设置img_size进行聚类?

  • 最佳实践:以你训练时设定的网络输入尺寸为准。如果你使用多尺度训练(如640-1280随机缩放),那么选择缩放范围的中值或下限(如640)作为聚类用的img_size是合理的。因为锚框是相对尺寸,只要训练和推理时图像resize的逻辑一致,锚框就能正确工作。更严谨的做法是,用每张图的原始尺寸归一化框,聚类得到相对锚框,然后在训练配置中指定基准img_size,代码会自动将相对锚框转换为该基准尺寸下的绝对像素值。大多数现代YOLO实现(如YOLOv5)都支持这种相对锚框的配置方式。

最后,记住锚框聚类是一个强大的工具,但它不是“银弹”。它解决了先验框与数据形状匹配的问题,但模型的最终性能是数据、模型、训练技巧共同作用的结果。将锚框优化作为你模型调优流水线中标准的一环,结合扎实的数据工程和严谨的实验,才能持续提升你的目标检测系统性能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/11 6:54:48

403 禁地:Nginx 拒绝所有生者进入

一、Nginx 基础介绍 1. 核心定位 高性能事件驱动 Web 服务&#xff0c;可做&#xff1a;静态资源服务器、反向代理、负载均衡、HTTPS 加密、限流、防盗链、gzip 压缩、虚拟主机、PHP 动态解析。 2. 核心优势 异步非阻塞 IO&#xff08;epoll&#xff09;&#xff1a;单进程支撑…

作者头像 李华
网站建设 2026/8/11 6:53:10

Qt无边框窗口开发实战:自定义标题栏、圆角阴影与拖拽拉伸

1. 项目概述&#xff1a;为什么我们需要一个“无边框”的现代窗口&#xff1f;在桌面应用开发中&#xff0c;尤其是使用 Qt 这类成熟的 GUI 框架时&#xff0c;我们常常会遇到一个矛盾&#xff1a;系统原生的窗口标题栏和边框&#xff0c;虽然提供了最小化、最大化、关闭和拖拽…

作者头像 李华
网站建设 2026/8/11 6:52:06

从零构建AI Agent命令行工具:LangChain与Typer实战指南

1. 项目概述&#xff1a;为什么我们需要一个AI Agent CLI&#xff1f;最近在折腾AI应用开发&#xff0c;发现一个挺有意思的现象&#xff1a;大家用LangChain、AutoGPT这些框架搭出来的Agent&#xff0c;功能很强大&#xff0c;但交互方式往往还停留在Web界面或者简单的脚本调用…

作者头像 李华
网站建设 2026/8/11 6:51:50

C++ vector容器深度解析:从动态数组原理到高效编程实践

1. 从“动态数组”到“瑞士军刀”&#xff1a;为什么C程序员离不开vector如果你刚开始学C&#xff0c;或者从C语言转过来&#xff0c;第一次看到vector这个词&#xff0c;可能会有点懵。它不像int、char那样直白&#xff0c;也不像array那样熟悉。但我要告诉你&#xff0c;在C的…

作者头像 李华
网站建设 2026/8/11 6:50:55

低成本网络存储!iSCSI 从 0 搭建 + 多路径高可用实操

CentOS7 iSCSI IP-SAN 完整实战博客 前言 传统服务器本地硬盘容量有限、无法多机共享&#xff0c;光纤FC SAN存储成本高昂。iSCSI基于TCP/IP以太网实现块级远程存储共享&#xff0c;俗称IP-SAN&#xff0c;普通千兆/万兆网线即可搭建共享存储&#xff0c;中小企业、机房实训首选…

作者头像 李华