『大模型量化』Qwen3-VL + Lora监督微调 + 8bit量化 + 实践推理

本文记录分享Qwen3-VL使用Lora完成监督微调后,进行8bit量化的过程;

  • 首先使用LLaMA Factory,对Qwen3-VL-4b进行Lora监督微调,得到微调后的lora权重;
  • 然后合并 基础Qwen3-VL-4b权重 + lora权重 = 完整微调权重
  • 最后对"完整微调权重"进行8bit量化,进行模型推理,提供实践案例。

目录

[一、对Qwen3-VL进行Lora 监督微调](#一、对Qwen3-VL进行Lora 监督微调)

二、合并权重

三、使用bnb进行8bit量化

四、量化后的模型推理(实践案例1)

五、量化后的模型推理(实践案例2)


一、对Qwen3-VL进行Lora 监督微调

下面是LLaMA Factory 的微调训练配置,用于Qwen3-VL-4B-Instruct 多模态模型 的有监督微调:

配置值 作用 & 分析
模型 Qwen3-VL-4B-Instruct(HuggingFace 源) 选择了 4B 参量的多模态(图文)模型,适合轻量化多模态任务微调
微调方法 LoRA 低参数量微调(只训练适配器权重),显存占用低、训练速度快
量化策略 8bit(QLoRA)+ bnb 量化 进一步压缩显存(4bit 加载模型),结合 LoRA 可在普通消费级显卡(如 16G 显存)上训练
训练阶段 Supervised Fine-Tuning (SFT,有监督微调) 基于标注数据集训练模型生成符合要求的输出

微调界面:

核心超参数配置(仅供参考,需要根据自己的模型调整的)

配置值 作用 & 分析
学习率 3e-5 LoRA 微调的常用学习率(比全量微调高 1-2 个数量级)
训练轮数 3.0 训练轮次较少,适合快速收敛或小数据集(避免过拟合)
批处理 / 梯度累积 批大小 2 + 梯度累积 2 → 等效批大小 4 小批量 + 梯度累积平衡显存占用与训练稳定性
截断长度 2048 匹配 Qwen3-VL 的上下文长度上限,适配长文本 / 多模态输入
学习率调节器 cosine(余弦退火) 训练后期平滑降低学习率,帮助模型稳定收敛
计算类型 bf16 半精度计算(需显卡支持 Ampere 及以上架构),进一步节省显存并加速训练

微调完整后,得到lora权重,默认保存在LLaMA Factory目录下的./saves/Qwen3-VL-4B-Instruct/lora/xxxxxx

二、合并权重

这里的思路:基础Qwen3-VL-4b权重 + lora权重 = 完整微调权重

我们只需确定:基础权重的路径model_name_or_path、lora微调权重路径adapter_name_or_path

然后执行下面命令,进行合并:

bash 复制代码
llamafactory-cli export \
  --model_name_or_path "/home/user/.cache/huggingface/hub/models--Qwen--Qwen3-VL-4B-Instruct/snapshots/ebb281ec70b05090aa6165b016eac8ec08e71b17/" \
  --adapter_name_or_path ./saves/Qwen3-VL-4B-Instruct/lora/train_2025-11-13-13-37/ \
  --export_dir ./qwen3-vl-4b-merged-20251113-fp16-lora\
  --template qwen3_vl_nothink

等待合并完成~

bash 复制代码
[2025-11-15 08:49:02,636] [INFO] [real_accelerator.py:254:get_accelerator] Setting ds_accelerator to cuda (auto detect)
[INFO|2025-11-15 15:36:02] llamafactory.model.model_utils.kv_cache:143 >> KV cache is enabled for faster generation.
[INFO|2025-11-15 15:36:02] llamafactory.model.model_utils.attention:143 >> Using torch SDPA for faster training and inference.

INFO  ENV: Auto setting PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' for memory saving.

INFO  ENV: Auto setting CUDA_DEVICE_ORDER=PCI_BUS_ID for correctness.          
[INFO|2025-11-15 15:36:02] llamafactory.model.adapter:143 >> Merged 1 adapter(s).
[INFO|2025-11-15 15:36:02] llamafactory.model.adapter:143 >> Loaded adapter(s): ./saves/Qwen3-VL-4B-Instruct/lora/train_2025-11-13-13-37
[INFO|2025-11-15 15:36:02] llamafactory.model.loader:143 >> all params: 4,437,815,808
[INFO|2025-11-15 15:36:02] llamafactory.train.tuner:143 >> Convert model dtype to: torch.bfloat16.
[INFO|2025-11-15 15:36:02] llamafactory.train.tuner:143 >> Ollama modelfile saved in ./model_path/qwen3-vl-4b-merged-20251113-fp16-lora/Modelfile

合并后的权重大小:

8.3G model_path/qwen3-vl-4b-merged

三、使用bnb进行8bit量化

通过**BitsAndBytes工具** 对 Qwen3-VL 模型进行8 位量化,以降低模型显存占用,

同时保存量化后的模型、分词器(tokenizer)和处理器(processor),便于后续部署使用。

  • 使用transformers库中的模型加载类(Qwen3VLForConditionalGenerationAutoModel)、量化配置(BitsAndBytesConfig
  • 依赖torch进行张量计算和设备管理。

模型量化的实力代码,如下所示:

bash 复制代码
from transformers import Qwen3VLForConditionalGeneration, AutoModel, BitsAndBytesConfig, AutoTokenizer, AutoProcessor
import torch

input_merged_model = "./model_path/qwen3-vl-4b-merged-20251113-8bit-lora"
output_merged_model = "./model_path/qwen3-vl-4b-bnb-quantized-8bit-merged-20251113-int8"


'''
BitsAndBytes 仅支持:
 位量化: quant_type="fp4" 或 "nf4"(配合 load_in_4bit=True)
 位量化:必须用 quant_type="int8"(实际是 8 位整数,不是浮点)。
 fp8 是 NVIDIA 的 8 位浮点格式,不在 Hugging Face BitsAndBytes 支持范围内。
'''

def quantize_model():
    # 配置8位bnb量化
    quantization_config = BitsAndBytesConfig(
        load_in_8bit=True,  # 8位量化(必须)
        bnb_8bit_compute_dtype=torch.float16,  # 计算用float16
        bnb_8bit_use_double_quant=True,  # 双量化(压缩高效,可选)
        bnb_8bit_quant_type="int8"  # ✅ 修正:必须为 "int8"(8位整数量化)
    )

    try:
        # 尝试使用特定模型类
        print("尝试使用 Qwen3VLForConditionalGeneration 加载模型...")
        model = Qwen3VLForConditionalGeneration.from_pretrained(
            input_merged_model,
            quantization_config=quantization_config,
            device_map="auto",
            trust_remote_code=True
        )
    except Exception as e:
        print(f"使用特定类失败: {e}")
        print("尝试使用 AutoModel 加载模型...")
        
        # 回退到 AutoModel
        model = AutoModel.from_pretrained(
            input_merged_model,
            quantization_config=quantization_config,
            device_map="auto",
            trust_remote_code=True
        )

    # 加载并保存tokenizer和processor
    tokenizer = AutoTokenizer.from_pretrained(input_merged_model, trust_remote_code=True)
    processor = AutoProcessor.from_pretrained(input_merged_model, trust_remote_code=True)
    
    # 保存量化后的模型和相关组件
    model.save_pretrained(output_merged_model)
    tokenizer.save_pretrained(output_merged_model)
    processor.save_pretrained(output_merged_model)
    
    print("模型8位量化并保存完成!")

if __name__ == "__main__":
    quantize_model()

明确 8 位量化的关键参数:

  • load_in_8bit=True:启用 8 位量化
  • bnb_8bit_quant_type="int8":指定量化类型为 8 位整数(符合 BitsAndBytes 对 8 位量化的要求)
  • bnb_8bit_compute_dtype=torch.float16:量化过程中使用 float16 进行计算,平衡精度与性能
  • bnb_8bit_use_double_quant=True:启用双量化(可选优化,进一步压缩模型大小,减少内存占用)

量化后的权重大小:

4.6G model_path/qwen3-vl-4b-bnb-quantized-8bit-merged-20251113-int8

四、量化后的模型推理(实践案例1)

这个实践案例,基于 Qwen3-VL 量化模型的单张图像描述

  • 模型加载:使用AutoModelForImageTextToText加载多模态模型,适配 Qwen3-VL 的图文生成能力。
  • 核心参数:device_map="auto"自动分配模型到 CPU/GPU,trust_remote_code=True允许加载模型自定义代码。
  • 处理器配套:AutoProcessor用于统一处理文本提示和图像输入,确保格式符合模型要求。

特点:

  • 输入处理:用apply_chat_template格式化对话(用户 + 图像 + 文本提示),processor将文本和图像转为模型可识别的张量,并移至对应设备。
  • 生成配置:max_new_tokens=512限制描述长度,do_sample=False+num_beams=1采用确定性生成,保证结果稳定。
  • 结果提取:裁剪模型输出中冗余的输入部分,解码后去除特殊 token,得到纯净描述文本。

推理代码:

python 复制代码
import os
from PIL import Image
import torch
from transformers import AutoProcessor, AutoModelForImageTextToText


# 模型路径配置
QUANTIZED_MODEL_PATH = "./model_path/qwen3-vl-4b-bnb-quantized-8bit-merged-20251113-int8"
# 输入图片路径
input_image = "dataset/test_pic/350012.jpg"


# 初始化模型和处理器
print(f"正在加载模型:{QUANTIZED_MODEL_PATH}...")
model = AutoModelForImageTextToText.from_pretrained(
    QUANTIZED_MODEL_PATH,
    device_map="auto",
    trust_remote_code=True
)
processor = AutoProcessor.from_pretrained(QUANTIZED_MODEL_PATH)
print("模型和处理器加载成功!")


def describe_image(image_path):
    """生成图像的详细描述"""
    try:
        # 加载图像
        image = Image.open(image_path).convert("RGB")
        
        # 构建提示词(专注于图像整体描述)
        messages = [
            {
                "role": "user",
                "content": [
                    {"type": "image"},
                    {"type": "text", "text": "请详细描述这张图片的内容,包括场景、物体、颜色、布局等信息。"}
                ]
            }
        ]
        
        # 处理输入
        text = processor.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=True
        )
        inputs = processor(
            text=text,
            images=image,
            return_tensors="pt",
            padding=True
        ).to(model.device)
        
        # 生成描述
        with torch.no_grad():
            generated_ids = model.generate(
                **inputs,
                max_new_tokens=512,
                do_sample=False,
                num_beams=1
            )
        
        # 提取并返回生成的描述
        generated_ids_trimmed = generated_ids[:, inputs.input_ids.shape[1]:]
        description = processor.batch_decode(
            generated_ids_trimmed,
            skip_special_tokens=True,
            clean_up_tokenization_spaces=False
        )[0]
        return description
        
    except Exception as e:
        print(f"图像描述失败: {str(e)}")
        return ""


def process_single_image(image_path):
    """处理单张图像并打印描述"""
    if not os.path.exists(image_path):
        print(f"图像文件不存在: {image_path}")
        return
    
    print(f"\n--- 处理图像: {os.path.basename(image_path)} ---")
    description = describe_image(image_path)
    if description:
        print("\n图像描述:")
        print(description)
    print(f"\n--- 处理完成 ---")


if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description='图像描述工具')
    parser.add_argument('--image', type=str, default=input_image, help='需要描述的图像路径')
    args = parser.parse_args()

    process_single_image(args.image)

输入图片:

运行信息:

python 复制代码
正在加载模型:./model_path/qwen3-vl-4b-bnb-quantized-8bit-merged-20251113-int8...
模型和处理器加载成功!

--- 处理图像: 350012.jpg ---
The following generation flags are not valid and may be ignored: ['temperature', 'top_p', 'top_k']. Set `TRANSFORMERS_VERBOSITY=info` for more details.

图像描述:
这张图片展示了一个布置温馨、光线柔和的现代客厅。整个空间以中性色调为主,搭配暖色装饰,营造出舒适宜人的氛围。

**场景与布局:**
客厅布局清晰,以一张米色布艺沙发为中心,沙发靠墙放置,旁边是深色木质边桌。前方是一个深棕色木质咖啡桌,桌下铺着一块色彩丰富的几何图案地毯,地毯以蓝色、红色和米色为主,为整个空间增添活力。左侧是壁炉区域,右侧是边桌和台灯,墙上挂有艺术画作,整体布局平衡且富有层次感。

**主要物体与细节:**

1. **沙发:**
   - 一张米色布艺沙发,靠背和坐垫厚实,显得柔软舒适。
   - 沙发上摆放着多个靠垫,颜色丰富:深紫色、橙色、红色和橄榄绿,形成视觉焦点。
   - 沙发右侧有一个深色木质边桌,上面放着一盏白色台灯和一个酒杯。

2. **咖啡桌:**
   - 深棕色木质咖啡桌,带有开放式下层置物架。
   - 桌面上摆放着一个白色陶瓷花瓶,瓶身呈优雅的鹅颈状,旁边是一个透明玻璃花瓶,内有干花枝。
   - 一个小型绿色盆栽植物放在花瓶旁,增添自然气息。
   - 一个透明玻璃杯和一个酒杯也放在桌上。

3. **壁炉与镜子:**
   - 左侧是一个深色砖砌壁炉,壁炉上方悬挂着一个大尺寸的木框镜子,镜中反射出楼梯的一部分,暗示房屋结构。
   - 壁炉上方的架子上摆放着几个玻璃烛台和装饰品。

4. **墙面装饰:**
   - 墙上挂着一幅大型抽象画,画中描绘了城市景观,色调以暖橙、赭石和米白为主,与沙发靠垫颜色相呼应。
   - 墙角处有一扇白色窗框的小窗,透入自然光。

5. **灯光与家具:**
   - 墙角的台灯和壁炉旁的台灯都采用白色灯罩,与整体色调协调。
   - 墙边有一个白色书架,上面摆放着书籍和装饰品。

**颜色与氛围:**
整个空间以米

--- 处理完成 ---

五、量化后的模型推理(实践案例2)

这个模型推理示例,是 Qwen3-VL 量化模型的特定类别物体检测与可视化

核心用于批量处理图像提取目标物体多维度信息生成可视化结果

核心功能:

  • 支持指定任意类别的物体(table、cup、bottle 等),输出类别、边界框、颜色、形状、外观 5 类信息。
  • 支持批量读取图像目录,自动按自然排序处理文件。
  • 生成带边界框和类别的可视化图像,同时打印详细物体描述。
  • 包含异常处理和格式容错,确保流程稳定性。

推理代码:

python 复制代码
import os
import json
import glob
import re
import torch
import numpy as np
from PIL import Image
import cv2
import time
from transformers import AutoProcessor, AutoModelForImageTextToText



QUANTIZED_MODEL_PATH = "./model_path/qwen3-vl-4b-bnb-quantized-8bit-merged-20251113-int8" # lora微调后的8-bit量化模型
input_dir = "./dataset_label/01_waitingroom/"
output_dir = "./Output_DATA/01_waitingroom"

# 指定检测的物体类别,可以指定任意物体名称或在物体描述
NODE_SPACE = ['table', 'cup', 'bottle', 'chair', 'robot', 'garbage can', 'shelf', 'tissue box', 'potted plant']


# 全局初始化模型和处理器
print(f"正在加载8-bit量化后的Qwen3-VL-4B模型:{QUANTIZED_MODEL_PATH}...")
model = AutoModelForImageTextToText.from_pretrained(
    QUANTIZED_MODEL_PATH,
    device_map="auto",
    trust_remote_code=True
)
processor = AutoProcessor.from_pretrained(QUANTIZED_MODEL_PATH)
print("修复后的量化Qwen3-VL-4B模型和处理器加载成功!")
    

def call_vlm(image_path, prompt):
    """安全的VLM调用函数"""
    try:
        # >>>>> 修改点1:直接使用 PIL 加载图像 <<<<<
        # 假设模型能处理任意大小的有效图像,不再强制调整尺寸
        image = Image.open(image_path).convert("RGB") # 确保是 RGB 模式
        
        messages = [
            {
                "role": "user",
                "content": [
                    {"type": "image"},
                    {"type": "text", "text": prompt}
                ]
            }
        ]
        
        text = processor.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=True
        )
        
        inputs = processor(
            text=text,
            images=image,
            return_tensors="pt",
            padding=True
        ).to(model.device)
        
        # 生成参数
        with torch.no_grad():
            generated_ids = model.generate(
                **inputs,
                max_new_tokens=512,
                do_sample=False,
                num_beams=1,
                early_stopping=False
            )
        
        generated_ids_trimmed = generated_ids[:, inputs.input_ids.shape[1]:]
        output_text = processor.batch_decode(
            generated_ids_trimmed,
            skip_special_tokens=True,
            clean_up_tokenization_spaces=False
        )[0]
        return output_text
        
    except Exception as e:
        print(f"VLM调用失败: {str(e)}")
        return ""


def detect_and_describe_objects(image_path):
    """检测图像中的物体,获取其描述并反归一化bbox"""
    # 构建提示词
    prompt = f"""
    请严格检测图像中属于以下类别的所有物体:{NODE_SPACE}。
    对于每个检测到的物体,请提供:
    1. 类别 (category)
    2. 紧贴边缘的边界框 (bbox),格式为 [x1,y1,x2,y2](整数像素坐标)
    3. 颜色 (color)
    4. 形状 (shape)
    5. 外观描述 (appearance)
    
    输出要求:
    - 仅识别列表中的类别。
    - bbox必须精确且紧密贴合物体边缘。
    - 描述信息需与bbox内的区域一致。
    - 严格以 JSON 数组形式返回,不要包含任何额外的文本或代码块标记。

    输出示例(格式参考):
    [
      {{
        "category": "cup",
        "bbox": [97, 203, 176, 282],
        "color": "白色",
        "shape": "圆柱形",
        "appearance": "带把手陶瓷杯"
      }},
      {{
        "category": "table",
        "bbox": [10, 318, 639, 474],
        "color": "原木色",
        "shape": "长方形",
        "appearance": "四腿木质桌面"
      }}
    ]
    """

    try:
        # >>>>> 修改点2:直接使用 PIL 获取图像尺寸 <<<<<
        # 加载图像以获取尺寸用于坐标反归一化
        with Image.open(image_path) as img_pil:
             w_img, h_img = img_pil.convert("RGB").size # 确保获取到尺寸
        
        print("开始调用VLM进行物体检测...")
        time_start = time.time()
        response = call_vlm(image_path, prompt)
        time_end = time.time()
        print(f'VLM推理时间:{time_end - time_start:.2f}s')
        
        if not response:
            print("VLM返回空响应")
            return []

        # 解析JSON响应
        try:
            objects_data = json.loads(response.strip())
        except json.JSONDecodeError:
            # 尝试清理常见的格式问题
            cleaned_response = response.strip()
            if cleaned_response.startswith('```json'):
                cleaned_response = cleaned_response[7:]
            if cleaned_response.startswith('```'):
                cleaned_response = cleaned_response[3:]
            if cleaned_response.endswith('```'):
                cleaned_response = cleaned_response[:-3]
            cleaned_response = cleaned_response.strip()
            try:
                objects_data = json.loads(cleaned_response)
            except json.JSONDecodeError as e:
                print(f"JSON解析失败: {e}")
                print(f"原始响应内容: {response}")
                return []

        if not isinstance(objects_data, list):
            print(f"响应不是列表格式: {objects_data}")
            return []

        # 处理并验证每个检测到的物体
        valid_objects = []
        for i, obj in enumerate(objects_data):
            # 验证基本结构
            if not (isinstance(obj, dict) and 
                    obj.get('category') in NODE_SPACE and 
                    len(obj.get('bbox', [])) == 4):
                print(f"警告:跳过无效的检测结果 #{i+1}: {obj}")
                continue

            # 提取并转换bbox坐标
            try:
                coords = list(map(float, obj['bbox']))
                x1_norm, y1_norm, x2_norm, y2_norm = coords
            except (ValueError, TypeError) as e:
                print(f"警告:跳过坐标格式错误的物体 #{i+1}: {e}")
                continue

            # 反归一化 (假设模型输出的是0-1000范围的坐标)
            x1 = int(round(x1_norm / 1000 * w_img))
            y1 = int(round(y1_norm / 1000 * h_img))
            x2 = int(round(x2_norm / 1000 * w_img))
            y2 = int(round(y2_norm / 1000 * h_img))

            # 确保坐标在图像范围内
            x1 = max(0, min(x1, w_img - 1))
            y1 = max(0, min(y1, h_img - 1))
            x2 = max(x1 + 1, min(x2, w_img - 1))
            y2 = max(y1 + 1, min(y2, h_img - 1))

            # 更新对象字典
            obj['bbox'] = [x1, y1, x2, y2]
            valid_objects.append(obj)
            
        print(f"共检测并验证了 {len(valid_objects)} 个物体。")
        return valid_objects

    except Exception as e:
        print(f"物体检测过程失败: {str(e)}")
        return []


def visualize_detections(image_path, detected_objects, output_path):
    """可视化检测结果:绘制bbox和标签"""
    try:
        # 读取图像
        image_bgr = cv2.imread(image_path)
        if image_bgr is None:
            print(f"无法读取图像: {image_path}")
            return
        h_img, w_img = image_bgr.shape[:2] # 获取OpenCV图像的尺寸

        # 绘制每个检测到的物体
        for i, obj in enumerate(detected_objects):
            bbox = obj.get('bbox', [0, 0, 0, 0])
            category = obj.get('category', 'Unknown')
            
            # 确保坐标在有效范围内 (基于OpenCV图像尺寸)
            x1, y1, x2, y2 = bbox
            x1 = max(0, min(x1, w_img - 1))
            y1 = max(0, min(y1, h_img - 1))
            x2 = max(x1 + 1, min(x2, w_img - 1))
            y2 = max(y1 + 1, min(y2, h_img - 1))

            # 绘制边界框
            cv2.rectangle(image_bgr, (x1, y1), (x2, y2), (0, 255, 0), 2)
            
            # 准备标签文本
            label_parts = [category]
            
            label = " ".join(label_parts)

            # 计算标签位置(在框上方)
            (text_width, text_height), baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
            y_label = y1 - 10 if y1 - 10 > 10 else y1 + 10 + text_height

            # 绘制标签背景矩形
            cv2.rectangle(image_bgr, (x1, y_label - text_height - baseline), 
                          (x1 + text_width, y_label + baseline), (0, 255, 0), -1)
            
            # 绘制标签文字
            cv2.putText(image_bgr, label, (x1, y_label),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
        
        # 保存可视化图像
        cv2.imwrite(output_path, image_bgr)
        print(f"检测结果可视化已保存至: {output_path}")

    except Exception as e:
        print(f"可视化检测结果失败: {str(e)}")


def print_object_descriptions(detected_objects):
    """打印每个检测到的物体的详细描述信息"""
    if not detected_objects:
        print("未检测到任何物体。")
        return

    print("\n--- 检测到的物体描述 ---")
    for i, obj in enumerate(detected_objects):
        print(f"物体 #{i+1}:")
        print(f"  类别 (Category): {obj.get('category', 'N/A')}")
        print(f"  边界框 (Bbox): {obj.get('bbox', 'N/A')}")
        print(f"  颜色 (Color): {obj.get('color', 'N/A')}")
        print(f"  形状 (Shape): {obj.get('shape', 'N/A')}")
        print(f"  外观 (Appearance): {obj.get('appearance', 'N/A')}")
        print("-" * 20)
    print("--- 描述结束 ---\n")


def natural_sort_key(filename):
    """自然排序含数字的文件名"""
    return [int(t) if t.isdigit() else t.lower()
            for t in re.split(r'(\d+)', os.path.basename(filename))]


def process_images_simple(input_dir, output_dir):
    """批量处理图像目录,执行简化任务"""
    try:
        os.makedirs(output_dir, exist_ok=True)
        viz_output_dir = os.path.join(output_dir, "visualizations_simple")
        os.makedirs(viz_output_dir, exist_ok=True)
        
        image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.gif']
        image_files = []
        for ext in image_extensions:
            image_files.extend(glob.glob(os.path.join(input_dir, ext)))
        image_files = sorted(image_files, key=natural_sort_key)
        
        if not image_files:
            print(f"在目录 {input_dir} 中未找到图像文件")
            return
        
        for image_path in image_files:
            image_name = os.path.basename(image_path)
            print(f"\n---------- 处理图像: {image_name} ----------")
            
            # 1. 检测物体并获取描述
            detected_objects = detect_and_describe_objects(image_path)
            
            # 2. 打印物体描述
            print_object_descriptions(detected_objects)
            
            # 3. 可视化检测框
            viz_filename = os.path.splitext(image_name)[0] + "_simple_viz.jpg"
            viz_path = os.path.join(viz_output_dir, viz_filename)
            visualize_detections(image_path, detected_objects, viz_path)
            
            print(f"---------- 完成处理: {image_name} ----------\n")
        
        print("\n所有图像处理完成")

    except Exception as e:
        print(f"处理图像目录失败: {str(e)}")


if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description='简化版Qwen3-VL物体检测与可视化工具')
    parser.add_argument('--input_dir', type=str, default=input_dir, help='包含图像的输入目录')
    parser.add_argument('--output_dir', type=str, default=output_dir, help='输出结果的目录')
    args = parser.parse_args()

    process_images_simple(args.input_dir, args.output_dir)

初始化模型→2. 批量读取图像→3. 单图处理(检测物体→解析结果→打印描述→绘制可视化)→4. 输出所有结果。

核心逻辑是利用多模态模型实现特定类别物体的检测与描述,并通过可视化和文本输出直观呈现结果

运行效果:

打印信息:

---------- 处理图像: be158920.jpg ----------

开始调用VLM进行物体检测...

VLM推理时间:17.25s

共检测并验证了 2 个物体。

--- 检测到的物体描述 ---

物体 #1:

类别 (Category): table

边界框 (Bbox): [162, 304, 315, 401]

颜色 (Color): 原木色

形状 (Shape): 长方形

外观 (Appearance): 木质桌面,表面有纹理


物体 #2:

类别 (Category): potted plant

边界框 (Bbox): [402, 148, 495, 256]

颜色 (Color): 绿色

形状 (Shape): 灌木状

外观 (Appearance): 高大的盆栽植物,叶片茂密


--- 描述结束 ---

检测结果可视化已保存至: ./Output_DATA/01_waitingroom/visualizations_simple/be158920_simple_viz.jpg

---------- 完成处理: be158920.jpg ----------

其他案例:

能看到通过8-bit量化的Qwen3-VL-4b模型,还是能准确把物体检测出来,而且物体的框比较贴合

后面部署到NVIDIA Orin板子中,看看效果~

分享完成~

相关推荐
liulilittle4 天前
CPU亲和性深度实践:从基础原理到Intel大小核架构优化
c++·线程·进程·cpu·量化·高频·亲核性
喜欢吃豆7 天前
llama.cpp 全方位技术指南:从底层原理到实战部署
人工智能·语言模型·大模型·llama·量化·llama.cpp
闲人编程12 天前
使用Python进行量化交易入门
开发语言·python·统计分析·lambda·量化·codecapsule
oliveray12 天前
ATPrompt:基于属性的视觉提示
人工智能·prompt·vlm
俊俊谢13 天前
【第一章】金融数据的获取——金融量化学习入门笔记
笔记·python·学习·金融·量化·akshare
jiucaixiuyang17 天前
散户如何做手机T0程序化交易(上)
股票·量化·t0
nenchoumi311919 天前
LLM 论文精读(十二)DeepSeek-OCR: Contexts Optical Compression
人工智能·计算机视觉·llm·ocr·vlm·deepseek
滚雪球~25 天前
okx欧易注册与量化设置
量化·程序化交易
算家云1 个月前
阿里最新开源!轻量级视觉模型Qwen3-VL-4B&8B-Instruct本地部署教程:小参数媲美顶尖模型
通义千问·算家云·视觉模型·模型部署教程·镜像社区·qwen3-vl