【大模型实战】微调Qwen2.5 VL模型,增强目标检测任务。

文章目录

  • 制作数据集
  • 使用微调的模型制作数据集

制作数据集

这个章节将详细解析一个将Labelme标注数据集转换为Qwen2.5-VL模型训练格式的Python脚本。该工具实现了图像大小调整、边界框坐标转换和数据格式标准化等功能。生成适用Qwen2.5-VL的数据集。

核心功能概述

  • 图像处理:将图像调整为固定尺寸

  • 坐标转换:同步调整边界框坐标

  • 格式转换:生成Qwen2.5-VL兼容的JSONL格式

import os
import json
import numpy as np
from PIL import Image
from tqdm import tqdmdef direct_resize(image, target_size=(1024, 1024)):"""直接调整图像到目标尺寸(不保持宽高比)参数:image: PIL.Image对象 - 原始图像target_size: (width, height) - 目标图像尺寸返回:resized_image: PIL.Image对象 - 调整后的图像scale: (scale_x, scale_y) - 宽高缩放比例"""orig_w, orig_h = image.sizetarget_w, target_h = target_size# 计算缩放比例scale_x = target_w / orig_wscale_y = target_h / orig_h# 直接缩放图像resized_image = image.resize(target_size, Image.Resampling.LANCZOS)return resized_image, (scale_x, scale_y)def direct_resize_bbox(original_size, target_size, bbox, scale):"""直接缩放边界框坐标(不保持宽高比)参数:original_size: (width, height) 原始图像尺寸target_size: (width, height) 目标图像尺寸bbox: [x_min, y_min, x_max, y_max] 原始边界框坐标scale: (scale_x, scale_y) 宽高缩放比例返回:normalized_bbox: [x1, y1, x2, y2] 归一化后的坐标(0-1范围)"""orig_w, orig_h = original_sizetarget_w, target_h = target_sizescale_x, scale_y = scale# 解包原始bbox坐标x_min, y_min, x_max, y_max = bbox# 应用缩放x_min_scaled = x_min * scale_xy_min_scaled = y_min * scale_yx_max_scaled = x_max * scale_xy_max_scaled = y_max * scale_yreturn [round(x_min_scaled, 4), round(y_min_scaled, 4), round(x_max_scaled, 4), round(y_max_scaled, 4)]def labelme_to_qwenvl(labelme_dir, output_file, target_size=(1024, 1024), default_description="请定位图像中的物体"):"""转换Labelme数据集为Qwen2.5-VL格式参数:labelme_dir: Labelme数据集目录路径output_file: 输出JSONL文件路径target_size: (width, height) 目标图像尺寸default_description: 默认图像描述文本"""# 创建输出目录output_dir = os.path.join(os.path.dirname(labelme_dir), "resized_images")os.makedirs(output_dir, exist_ok=True)# 收集所有Labelme JSON文件json_files = [f for f in os.listdir(labelme_dir) if f.endswith('.json')]error_count = 0processed_count = 0with open(output_file, 'w', encoding='utf-8') as out_f:for json_file in tqdm(json_files, desc="转换数据集中"):json_path = os.path.join(labelme_dir, json_file)try:# 读取Labelme标注文件with open(json_path, 'r', encoding='utf-8') as f:labelme_data = json.load(f)# 获取图像信息img_name = labelme_data['imagePath']img_path = os.path.join(labelme_dir, img_name)img_width = labelme_data['imageWidth']img_height = labelme_data['imageHeight']original_size = (img_width, img_height)# 打开并处理图像with Image.open(img_path) as img:# 直接调整图像大小(不保持宽高比)resized_img, scale = direct_resize(img, target_size)# 保存调整后的图像new_img_name = f"resized_{img_name}"new_img_path = os.path.join(output_dir, new_img_name)resized_img.save(new_img_path)# 收集所有对象的边界框和标签objects = []for shape in labelme_data['shapes']:if shape['shape_type'] != 'rectangle':continue  # 跳过非矩形标注label = shape['label']points = np.array(shape['points'])# 转换为[x_min, y_min, x_max, y_max]格式x_coords = points[:, 0]y_coords = points[:, 1]x_min, x_max = min(x_coords), max(x_coords)y_min, y_max = min(y_coords), max(y_coords)bbox = [x_min, y_min, x_max, y_max]# 应用直接缩放转换normalized_bbox = direct_resize_bbox(original_size,target_size,bbox,scale)objects.append({"bbox_2d": normalized_bbox,"label": label})# 构建Qwen2.5-VL格式assistant_content = "```json\n" + json.dumps(objects, ensure_ascii=False) + "\n```"sample = {"messages": [{"role": "user","content": f"<image>{default_description}"},{"role": "assistant","content": assistant_content}],"images": [new_img_path]  # 使用新图片路径}# 写入JSONL文件out_f.write(json.dumps(sample, ensure_ascii=False) + '\n')processed_count += 1except Exception as e:print(f"处理文件 {json_file} 时出错: {str(e)}")error_count += 1print(f"\n转换完成! 输出文件: {output_file}")print(f"成功处理: {processed_count} 个文件")print(f"失败: {error_count} 个文件")print(f"调整后的图像已保存到: {output_dir}")if __name__ == "__main__":# ===== 配置参数 =====LABELME_DIR = "../labelme-car-618"  # 替换为你的Labelme数据集目录OUTPUT_FILE = "qwen_vg_dataset.jsonl"  # 输出文件名TARGET_SIZE = (512, 512)  # 目标图像尺寸# 任务提示语task_prompt = """请仔细标注图像中每辆出租车、每辆私家车、每辆卡车、每辆公交车的精确边界框。对于每辆出租车,提供一个JSON对象包含:- 'bbox_2d': 由四个整数组成的数组 [x1, y1, x2, y2],分别表示左上角和右下角坐标- 'label': 出租车字符串值 'taxi',私家车字符串值 'car',卡车字符串值 'truck',公交车字符串值 'bus'确保:1. 边界框紧密贴合整个车辆(包括车轮和车顶)2. 坐标是相对于图像尺寸的绝对像素值3. 只标注完全可见的出租车4. 仅输出有效的JSON对象,每辆出租车一个对象,不要添加额外文本或解释"""# 执行转换labelme_to_qwenvl(labelme_dir=LABELME_DIR,output_file=OUTPUT_FILE,target_size=TARGET_SIZE,default_description=task_prompt.strip()  # 去除首尾空白)  

使用微调的模型制作数据集

import glob
import json
import os
import re
import ast  # 新增用于解析非标准JSON
import cv2
from qwen_vl_utils import process_vision_info
from transformers import AutoModelForVision2Seq, AutoProcessor# 初始化模型
model = AutoModelForVision2Seq.from_pretrained("output/Qwen2.5-VL-7B-Instruct/v2-20250625-112537/checkpoint-47-merged",torch_dtype='auto',device_map="auto"
)
processor = AutoProcessor.from_pretrained("output/Qwen2.5-VL-7B-Instruct/v2-20250625-112537/checkpoint-47-merged")prompt = """
请仔细标注图像中每辆出租车的精确边界框。对于每辆出租车,提供一个JSON对象包含:
- 'bbox_2d': 由四个整数组成的数组 [x1, y1, x2, y2],分别表示左上角和右下角坐标
- 'label': 字符串值 'taxi'确保:
1. 边界框紧密贴合整个车辆(包括车轮和车顶)
2. 坐标是相对于图像尺寸的绝对像素值
3. 只标注完全可见的出租车(忽略部分遮挡的车辆)
4. 仅输出有效的JSON对象,每辆出租车一个对象,不要添加额外文本或解释
"""def extract_taxi_data(response):"""从响应中提取出租车边界框数据(增强解析能力)"""# 尝试提取JSON代码块json_str = Nonematch = re.search(r'```json\n(.*?)\n```', response, re.DOTALL)if match:json_str = match.group(1).strip()else:# 尝试直接提取JSON数组match = re.search(r'\[.*\]', response, re.DOTALL)if match:json_str = match.group(0).strip()if not json_str:print("未找到有效的JSON数据")return []# 增强JSON解析(处理单引号等非标准格式)try:# 先尝试标准JSON解析return json.loads(json_str)except json.JSONDecodeError:try:# 尝试使用ast解析Python字面量return ast.literal_eval(json_str)except (SyntaxError, ValueError) as e:print(f"JSON解析错误: {e}")return []def auto_annotate(image_path, output_json):img = cv2.imread(image_path)if img is None:print(f"无法读取图像: {image_path}")returnheight, width = img.shape[:2]messages = [{"role": "user","content": [{"type": "image", "image": image_path},{"type": "text", "text": prompt},],}]# 准备输入text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)image_inputs, video_inputs = process_vision_info(messages)# 模型推理inputs = processor(text=[text],images=image_inputs,videos=video_inputs,padding=True,return_tensors="pt",)inputs = inputs.to("cuda")generated_ids = model.generate(**inputs,max_new_tokens=2048)response = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]print("模型原始响应:\n", response)# 解析输出json_data = extract_taxi_data(response)print("解析后的JSON数据:", json_data)shapes = []for item in json_data:if 'bbox_2d' not in item or len(item['bbox_2d']) != 4:print(f"跳过无效的bbox数据: {item}")continuex1, y1, x2, y2 = item['bbox_2d']# 关键修复:处理归一化坐标(模型返回的是0-1之间的值)# 检查是否是归一化坐标(所有值在0-1之间)if all(0 <= val <= 1 for val in [x1, y1, x2, y2]):# 归一化坐标 → 绝对坐标x1_abs = int(x1 * width)y1_abs = int(y1 * height)x2_abs = int(x2 * width)y2_abs = int(y2 * height)else:# 已经是绝对坐标(直接取整)x1_abs = int(x1)y1_abs = int(y1)x2_abs = int(x2)y2_abs = int(y2)  # 修复:原来是int(x2)# 确保坐标在图像范围内x1_abs = max(0, min(x1_abs, width - 1))y1_abs = max(0, min(y1_abs, height - 1))x2_abs = max(0, min(x2_abs, width - 1))y2_abs = max(0, min(y2_abs, height - 1))# 确保是有效矩形if x1_abs >= x2_abs or y1_abs >= y2_abs:print(f"跳过无效的矩形: [{x1_abs}, {y1_abs}, {x2_abs}, {y2_abs}]")continueshapes.append({"label": item.get('label', 'taxi').strip(),"points": [[x1_abs, y1_abs], [x2_abs, y2_abs]],"group_id": None,"shape_type": "rectangle","flags": {}})# 构建labelme格式labelme_data = {"version": "5.1.1","flags": {},"shapes": shapes,"imagePath": os.path.basename(image_path),"imageData": None,"imageHeight": height,"imageWidth": width}# 保存JSONwith open(output_json, 'w', encoding='utf-8') as f:json.dump(labelme_data, f, ensure_ascii=False, indent=2)print(f"标注已保存至: {output_json}, 检测到 {len(shapes)} 辆出租车")return labelme_dataif __name__ == "__main__":root_labelme = "../Labelme_Taxi"# 清理旧JSON文件for json_file in glob.glob(os.path.join(root_labelme, "*.json")):os.remove(json_file)# 处理所有JPG图像for img_path in glob.glob(os.path.join(root_labelme, "*.jpg")):print(f"\n处理图像: {img_path}")auto_annotate(img_path, img_path.replace('.jpg', '.json'))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/86334.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/86334.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python实用小脚本-118】基于Flask的用户认证系统:app.py、forms.py与user.py解析

在当今的网络应用中&#xff0c;用户认证是一个不可或缺的功能。无论是社交平台、电商平台还是企业管理系统&#xff0c;都需要确保只有授权用户才能访问特定的资源。本文将详细介绍一个基于 Flask 框架的用户认证系统&#xff0c;该系统由三个主要文件组成&#xff1a;app.py、…

phpstudy apache伪静态.htaccess文件置空丢失问题解决

phpstudy apache伪静态.htaccess文件置空丢失 在使用phpstudy本地部署项目的时候&#xff0c;创建网站-根目录选择public等运行目录&#xff0c;并且点击确认后&#xff0c;会碰到原本项目中的apache伪静态.htaccess文件被置空丢失的问题&#xff0c;导致项目无法正常访问。 解…

【thinkphp5】Session和Cache记录微信accesstoken

记录一个项目实际遇到的坑&#xff0c;不要把token存放在session&#xff0c;要存在在cache里面&#xff01;&#xff01; 因为Session并不能设置expire过期时间&#xff0c;Session::set()方法第三个参数是作用域&#xff0c;而非过期时间&#xff01;&#xff01;&#xff0…

网络协议完全指南:从HTTP长短连接到TCP-UDP的深度对话

&#x1f310; 网络协议完全指南&#xff1a;从HTTP长短连接到TCP-UDP的深度对话 本文采用对话形式&#xff0c;通过小李和小王的问答&#xff0c;深入浅出地讲解网络协议、长短连接等核心概念&#xff0c;帮助读者建立完整的网络知识体系。 引言 在Java后端开发中&#xff0c…

04-StarRocks集群运维FAQ

StarRocks集群运维FAQ 概述 本文档整理了StarRocks集群运维过程中常见的问题和解决方案,涵盖了集群管理、节点维护、监控告警、故障处理等各个方面,帮助运维人员高效管理StarRocks集群。 集群管理FAQ Q1: 如何查看集群状态? A: 集群状态查看方法: 1. 查看FE节点状态 …

通过Prompt提示构建思维链

《DEEPSEEK原生应用与智能体开发实践 王晓华 书籍 图书》【摘要 书评 试读】- 京东图书 思维链技术开启了人工智能通向人类智能的崭新路径。它让模型不再仅仅是机械地执行指令&#xff0c;而是开始具备类似人类的思考方式&#xff0c;能够理解问题的本质&#xff0c;进行深层次…

OpenCV边缘填充方式详解

一、边缘填充概述 在图像处理中&#xff0c;边缘填充&#xff08;Border Padding&#xff09;是一项基础而重要的技术&#xff0c;特别是在进行卷积操作&#xff08;如滤波、边缘检测等&#xff09;时&#xff0c;处理图像边缘像素需要用到周围的像素值。由于图像边缘的像素没…

如何评估RAG系统?全面指标体系

构建一个可靠的 检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;系统&#xff0c;不仅要关注模型的构建&#xff0c;更重要的是对系统性能进行科学、系统的评估。评估不仅衡量系统的效果&#xff0c;也为迭代优化提供依据。 本文将围绕 RAG 系统的评…

力扣-合并区间

题目 56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 解析: 先按开始维度排序&#xff0c;之后依次合并&#xff0c;如果开头 < 当前区间的最后就合并&#xff0c;> 就开辟新区间 代码: class Solution {public int[][] merge(int[][] intervals) {int n in…

【软考高级系统架构论文】论基于构件的软件开发方法及其应用

论文真题 基于构作的 软件开发 (Component-Based Software Development,CBSD) 是一种基于分布对象技术、强调通过可复用构件设计与构造软件系统的软件复用途径。基于构件的软件系统中的构件可以是COTS (Commercial-Off-the-Shelf) 构件,也可以是通过其它途径获得的构件(如自…

recipes的版本比较老如何更新到新版本?

在 Yocto 项目中,当你发现 “meta-openembedded” 层中的某些 recipe 版本太旧,而你想使用更新版本时,最佳实践是在你自己项目的自定义层 (custom layer) 中使用 “bbappend” 文件进行覆盖或升级。 核心思路: 不要直接修改 “meta-openembedded” 层的 recipe ( “*.bb”…

【软件系统架构】系列四:嵌入式软件-NPU(神经网络处理器)系统及模板

目录 一、什么是 NPU? 二、NPU 与 CPU/GPU/DSP 对比 三、NPU 的工作原理 核心结构: 数据流架构: 四、NPU 芯片架构(简化图) 五、NPU 的优势 六、NPU 应用场景 视觉识别 语音识别 自动驾驶 智能监控 AIoT 设备 七、主流 NPU 芯片/架构实例 八、开发者工具生…

【NLP】使用 LangGraph 构建 RAG 的Research Multi-Agent

本文中&#xff0c;我们介绍了一个使用LangGraph开发的RAG的Research Multi-Agent工具的实际项目。该工具旨在解决需要多个来源和迭代步骤才能得出最终答案的复杂问题。它使用混合搜索和rerank步骤来检索文档&#xff0c;还结合了自我纠正机制&#xff0c;包括幻觉检查过程&…

【Docker基础】Docker容器管理:docker restart详解

目录 1 docker restart命令概述 1.1 命令作用 1.2 与相关命令对比 2 命令语法详解 2.1 基础语法 2.2 核心参数说明 3 核心原理深度解析 3.1 信号传递机制 3.2 状态转换 4 典型应用场景 4.1 服务配置更新 4.2 故障恢复流程 5 进阶使用技巧 5.1 组合命令应用 5.2 …

mongoDB服务本地化部署

mongoDB服务本地化部署 前言mongoDB下载选择版本安装 前言 mongoDB数据库解释 MongoDB 是由C语言编写的&#xff0c;是一个基于分布式文件存储的开源数据库系统&#xff1b;在高负载的情况下&#xff0c;添加更多的节点&#xff0c;可以保证服务器性能&#xff1b;MongoDB 旨在…

YOLOv10tensorRT推理代码C++

最近实现了YOLOv10的tensorRT推理代码除了后处理部分只适合YOLOv10之外&#xff0c;其余部分基本可以在yolo系列通用~学习记录~。 #include <fstream> #include <iostream> #include <vector> #include <opencv2/opencv.hpp> #include "NvInfer.…

软件定时器详解:RTOS 中的“软时钟”机制与源码解析

在嵌入式实时系统开发中&#xff0c;定时器是不可或缺的工具。软件定时器&#xff08;Software Timer&#xff09; 提供了一种无需创建独立任务、便可在特定延时后执行回调函数的机制。它适用于那些不要求高精度、但需要周期性或一次性延时执行操作的场景。 一、什么是软件定时…

从Yocto中获取源码用传统的方式单独编译

要获取 Yocto 构建后的 Linux 内核和 U-Boot 源码,并进行独立编译,需获取完整的源码树(包含所有应用补丁和配置)及原始配置信息。以下是具体步骤: 获取最终源码路径确定构建目录位置: 内核工作目录 KERNEL_WORKDIR=$(bitbake -e virtual/kernel | grep ^WORKDIR= | cut…

【记录】服务器|常见的八种硬盘接口的简介和清晰的接口图片(2025年6月)

硬盘接口很多&#xff0c;在管服务器的时候总是要买&#xff0c;但是偶尔会忘记自己的服务器支持什么接口&#xff0c;此时就需要看引脚。 如果没插满&#xff0c;就可以直接拍接口的图片&#xff0c;与下面这些图片对照一下【文字介绍是AI直接生成的&#xff0c;图片是我到处…

在一个成熟产品中,如何设计数据库架构以应对客户字段多样化,确保系统的可维护性、可扩展性和高性能。

在SaaS系统、平台型应用或高度可配置的企业级软件中&#xff0c;我们常常会遇到一个现实问题&#xff1a;不同客户对同一个业务表存在差异化字段需求。例如&#xff0c;A客户需要一个“业务员等级”字段&#xff0c;B客户不需要&#xff1b;C客户希望订单表中增加“海外仓编码”…