精准提升:从94.5%到99.4%——目标检测调优全纪录
🚀 目标检测模型调优过程记录
在进行目标检测模型的训练过程中,我们面对了许多挑战与迭代。从初始模型的训练结果到最终的调优优化,每一步的实验和调整都有其独特的思路和收获。本文记录了我在优化目标检测模型的过程中进行的几次尝试,并详细分析了每次调整带来的效果提升。🔧
一开始,我训练了一个目标检测器,目标检测任务包括了 26个类别,初始的 mAP50 在 94.5% 左右。尽管初步结果较为理想,但在混淆矩阵中仍然能看到一些类别之间存在较大的误识别和混淆,尤其是在类别间数据不平衡的情况下,部分小类别的识别效果较差。为了进一步提升模型性能,减少误识别,后续进行了几轮迭代和优化。

接下来的调优尝试,我采取了不同的策略,每次都根据前一次的分析结果,针对性地进行调整。以下是我尝试的几种方法,以及每次尝试的详细效果记录。📊
1️⃣ 第一次尝试:将目标检测拆解为单目标检测 + 图像分类
在最初的尝试中,我将原本的单一目标检测器拆解为单目标检测与图像分类的结合方式。具体可以参考我这篇文章:联合目标检测与图像分类提升数据不平衡场景下的准确率,方法的核心是将每个目标先单独识别,然后通过图像分类进一步确认类别。🎯
经过调整后,虽然 Top1的准确率 提高至 99%,但是从混淆矩阵来看,部分类别仍然存在较多的混淆,尤其是一些相似类别之间的错误识别仍然较为显著。🧐

2️⃣ 第二次尝试:统一缩放目标区域
在第一次尝试后,我发现将目标区域进行标准化处理可能会对模型训练有所帮助。因此,我采用了将目标检测出来的子图强制缩放至统一大小的策略。使用 cv2.resize(object_region, (classification_size, classification_size)) 对每个检测到的目标区域进行缩放。🔄
经过训练后,模型在一些类别上的识别效果有所改善,尤其是在一些小物体的检测上,误差略有减少。📉
3️⃣ 第三次尝试:统一缩放原始子图
在第二次尝试的基础上,我进一步扩大了缩放操作的范围,将原始图像中的所有子图按统一的缩放尺寸进行训练。这一操作提高了模型在不同尺度上的泛化能力,特别是对于不同尺寸的目标检测表现有所提升,训练过程中也能更好地对齐各类别的特征分布。📏
import os
from pathlib import Path
import cv2
from multiprocessing import Pool
from functools import partial
from tqdm import tqdmdef find_image_files(root_dir, image_extensions={'.jpg', '.jpeg', '.png', '.bmp', '.gif'}):"""递归查找根目录下所有图片文件"""root_path = Path(root_dir)image_files = [p for p in root_path.rglob('*') if p.suffix.lower() in image_extensions]return image_filesdef process_image(image_path, root_dir, output_dir, size=(224, 224)):"""处理单张图片:读取、调整大小、保存到目标目录"""try:# 计算相对路径并构造目标路径relative_path = image_path.relative_to(root_dir)target_path = Path(output_dir) / relative_path# 创建目标文件夹(如果不存在)target_path.parent.mkdir(parents=True, exist_ok=True)# 读取图像img = cv2.imread(str(image_path))if img is None:raise ValueError("图像无法读取,可能是文件损坏或格式不支持。")# 调整大小resized_img = cv2.resize(img, size)# 保存图像,保持原始格式success = cv2.imwrite(str(target_path), resized_img)if not success:raise IOError("图像保存失败。")except Exception as e:print(f"处理图片 {image_path} 时出错: {e}")def main():"""主函数:查找图片并使用多进程处理"""# 设置根目录和输出目录ROOT_DIR = "datasets/device_cls_merge_manual_with_21w_1218_train_val" # 替换为你的根目录路径OUTPUT_DIR = "datasets/device_cls_merge_manual_with_21w_1218_train_val_224" # 替换为你想保存的目标目录路径SIZE = (224, 224) # 调整后的图片大小NUM_PROCESSES = 16 # 使用的进程数# 查找所有图片文件image_files = find_image_files(ROOT_DIR)print(f"找到 {len(image_files)} 张图片。")if len(image_files) == 0:print("未找到任何图片文件,请检查根目录路径和图片格式。")return# 确保输出目录存在Path(OUTPUT_DIR).mkdir(parents=True, exist_ok=True)# 使用部分函数固定一些参数process_func = partial(process_image, root_dir=ROOT_DIR, output_dir=OUTPUT_DIR, size=SIZE)# 使用多进程处理图片,并显示进度条with Pool(processes=NUM_PROCESSES) as pool:list(tqdm(pool.imap_unordered(process_func, image_files), total=len(image_files)))print("所有图片处理完成!")if __name__ == "__main__":main()
4️⃣ 第四次尝试:引入额外数据
为了进一步提升模型的识别效果,我额外引入了一批新的数据,并使用已经训练好的模型打出类别及置信度分数,只保留置信度大于 0.9 的类别子图。这一策略显著提升了模型的准确性,特别是在一些模糊的检测任务中,置信度较高的预测结果帮助排除了很多误识别情况。📈
5️⃣ 第五次尝试:调整数据预处理和关闭自动增强
接下来,我调整了 YOLOv8 的预处理策略,并关闭了自动数据增强(auto_augment=False)。这一调整基于数据集的实际情况,考虑到某些预处理和数据增强方法可能会导致过拟合或过度处理,关闭自动增强后,模型在训练过程中更加专注于数据本身的特征,效果得到了明显的改善。💡
6️⃣ 第六次尝试:数据可视化与欠采样
最后,在进行数据可视化分析后,我发现部分类别的样本数过大,导致了模型训练时出现类别不平衡问题。因此,我对这些过多的类别进行了欠采样处理,减少了它们在训练中的占比,从而使得训练数据的分布更加均衡。📉
具体可以参考这篇文章:图像分类实用脚本:数据可视化与高数量类别截断,欠采样后,训练集中的数据分布更加合理,模型的鲁棒性也得到了提高。🔍
经过这些优化后,模型的表现有了显著提升。最终,mAP50 虽然略有提升至 99.4% 左右,但最重要的是,误识别率已经非常低,整体性能非常稳定。🎉

总结
通过这几轮的调优和实验,我在目标检测任务中取得了较为显著的进展。从最初的 94.5% 到最终的 99.4%,虽然提升幅度不算极其惊人,但每一轮的调整都有其明确的目的和成效,尤其是在类别不平衡、尺度差异、数据增强等方面的优化。最终,模型在实际测试中的误识别问题大幅度减少,几乎达到了理想状态。✨
在后续的工作中,我还将继续关注数据集的多样性与模型的泛化能力,进一步探索更多的调优方案。🔮
相关文章:
精准提升:从94.5%到99.4%——目标检测调优全纪录
🚀 目标检测模型调优过程记录 在进行目标检测模型的训练过程中,我们面对了许多挑战与迭代。从初始模型的训练结果到最终的调优优化,每一步的实验和调整都有其独特的思路和收获。本文记录了我在优化目标检测模型的过程中进行的几次尝试&#…...
【LLM论文日更】| 训练大型语言模型在连续潜在空间中进行推理
论文:https://arxiv.org/pdf/2412.06769代码:暂未开源机构 :Meta领域:思维链发表:arxiv 研究背景 研究问题:这篇文章要解决的问题是如何在大语言模型(LLMs)中实现一种新的推理范式&…...
智能家居实训室中,STC单片机驱动的“互联网+”智能家居系统设计
一、引言 随着经济的快速发展,人们对家居环境的智能化、网络化需求日益增强,智能家居的研究也因此受到了国内外相关机构的广泛关注。STC单片机凭借其卓越的性能和广泛的应用领域,成为了智能家居系统设计的优选方案。作为一种先进的微控制器&…...
《C++ 赋能强化学习:Q - learning 算法的实现之路》
在当今科技飞速发展的时代,人工智能无疑是最热门的领域之一,而强化学习作为其中的重要分支,正逐渐改变着我们解决复杂问题的方式。Q - learning 算法作为强化学习中的经典算法,在众多领域如游戏、机器人控制、资源管理等有着广泛的…...
三维模型中的UV展开是什么意思?它有什么优势?
UV展开涉及将三维模型的表面展开为一个或多个二维区域,以便将纹理图像正确地映射到模型上。这个过程类似于将一个立体物体的表面切割并平铺开来。UV坐标是用于在二维纹理图像中定位颜色和细节的坐标系统,U和V分别代表纹理图像的水平和垂直轴。 UV展开它…...
怎么在ubuntu系统上安装qt项目的打包工具linuxdeployqt
引言 安装linuxdeployqt方案一方案二 在ubuntu系统上开发的项目最后需要完成打包,qtcreator本身就用一个打包工具,在ubuntu系统上是linuxdeployqt。本文主要记录一下怎么在ubuntu系统上安装qt打包工具linuxdeployqt。 安装linuxdeployqt 前提是已经安装…...
SQL语句整理五-StarRocks
文章目录 查看版本号:SPLIT:insert 和 update 结合 select:报错:1064 - StarRocks planner use long time 3000 ms in memo phase:字段增删改: 查看版本号: select current_version(); current…...
【C#】try-catch-finally语句的执行顺序,以及在发生异常时的执行顺序
try-catch-finally语句 执行顺序 执行 try 块:程序首先尝试执行 try 块中的代码。如果在此期间没有发生异常,则跳过 catch 块,直接执行 finally 块(如果存在)。 发生异常时的处理: 如果在 try 块中发生了…...
【vue】vite + ts +vue3 安装pinia
vue3 TS 安装使用pinia状态管理_vue3 ts pinia-CSDN博客...
PointPillars:数据预处理
在 PointPillars 算法中,将点云划分为点柱(Pillars)是核心步骤之一,用于将稀疏点云数据转换为规则的张量表示,方便后续 2D 卷积操作。以下是点云划分为点柱的具体方法和实现步骤: 1. 点云划分为网格 将 3D…...
node.js的异步工作之---回调函数与回调地狱
回调函数:在 Node.js 中,很多 API 都是异步的,通常通过回调函数来处理操作完成后的结果。这种回调模式虽然非常高效,但会导致代码逐渐变得难以维护,尤其是当有多个异步操作嵌套时(即回调地狱)。…...
Mac Android studio 升级LadyBug 版本,所产生的bug
当Build 出现,这样的文字以后: Your build is currently configured to use incompatible Java 21.0.3 and Gradle 7.3.3. Cannot sync the project. We recommend upgrading to Gradle version 8.9. The minimum compatible Gradle version is 8.5. …...
stm32 hex文件烧写
STM32的HEX文件烧写是将编译后的程序代码(以HEX格式存储)下载到STM32单片机中的过程。以下是对STM32 HEX文件烧写的详细解释: 一、HEX文件简介 HEX文件,即Intel HEX文件,是一种由文本行组成的ASCII文件,每…...
【编译原理】编译原理知识点汇总·属性文法和语法制导翻译
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀编译原理_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …...
【unity c#】深入理解string,以及不同方式构造类与反射的性能测试(基于BenchmarkDotNet)
出这篇文章的主要一个原因就是ai回答的性能差异和实际测试完全不同,比如说是先获取构造函数再构造比Activator.CreateInstance(type)快,实际却相反 对测试结果的评价基于5.0,因为找不到unity6确切使用的net版本,根据c#9推测是net5…...
VSCode 插件开发实战(八):创建和管理任务 Task
前言 VSCode 的扩展能力使得开发者能够根据个人需求定制工作环境,自定义插件和任务管理是 VSCode 强大功能的一部分,通过这些功能,开发者可以自动化常见工作流,简化日常开发任务,提高整体开发效率。本文将详细介绍如何…...
在 Node.js 中正确处理 `async/await` 及数组迭代
在使用 Node.js 开发应用程序时,我们常常需要处理异步操作。例如,当我们从数据库获取数据、调用外部API或执行文件读取时,这些操作都可能需要一些时间才能完成。在这种情况下,我们通常会使用 async/await 语法来简化异步编程的复杂…...
本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——13使用Resnet-Bin
本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——13使用Resnet-Bin 根据前面的内容,目前已经可以获取到resnet的bin模型 1 .Resnet的bin测试 这里给大家一个测试视频里面黑线的demo,大家可以用来测试自己的黑线识别精度 …...
FFmpeg第三话:FFmpeg 视频解码详解
FFmpeg 探索之旅 一、FFmpeg 简介与环境搭建 二、FFmpeg 主要结构体剖析 三、FFmpeg 视频解码详解 FFmpeg第三话:FFmpeg 视频解码详解 FFmpeg 探索之旅前言一、视频解码基础二、FFmpeg 关键 API 深度剖析(一)avformat_open_input()ÿ…...
解决 vue3 中 echarts图表在el-dialog中显示问题
原因: 第一次点开不显示图表,第二次点开虽然显示图表,但是图表挤在一起,页面检查发现宽高只有100px,但是明明已经设置样式宽高100% 这可能是由于 el-dialog 还没有完全渲染完成,而你的 echarts 组件已经开始尝试渲染图…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
标注工具核心架构分析——主窗口的图像显示
🏗️ 标注工具核心架构分析 📋 系统概述 主要有两个核心类,采用经典的 Scene-View 架构模式: 🎯 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 🔧 关键函数&…...
多模态学习路线(2)——DL基础系列
目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization(RMSNorm) 二、激活函数 1. Sigmoid激活函数(二分类&…...
Go 语言中的内置运算符
1. 算术运算符 注意: (自增)和--(自减)在 Go 语言中是单独的语句,并不是运算符。 package mainimport "fmt"func main() {fmt.Println("103", 103) // 13fmt.Println("10-3…...
