Qwen-VL-Chat进行Qlora微调全过程讲解

序言

        Qwen-VL-Chat的Qlora微调有很多博主都讲的非常好,我也是参考的几个博主的文章之后,成功进行了微调。由于我是一个小白,在微调过程中,遇到了一些问题,然而许多博主并没有提及到这些问题,所以写这篇文章有两个目的:(1)记录自己的学习;(2)如果有向我一样刚接触这一块的小白,根据这篇文章可以更好,更方便的去动手微调。

        情况说明:本微调教程是在AutoDL平台上租用容器实例进行的,如果有小伙伴是用自己的电脑进行本地微调,那么需要自行根据需求做出相应调整(我没试过,所以不做评价)。

建议及推荐:

  • 显存至少需要10GB以上,低于10GB微调无法进行。
  • python 3.8及以上版本
  • pytorch 1.12及以上版本,推荐2.0及以上版本
  • 建议使用CUDA 11.4及以上(GPU用户需考虑此选项)

目录

序言

模型部署

1.1 下载Qwen-VL-chat模型

1.2 下载Qwen-VL-Chat所需要的依赖环境

1.3 使用Qwen-VL-Chat进行推理

数据准备

2.1 数据格式要求

2.2 数据预处理

模型微调

3.1 Qwen-VL-Chat微调相关文件下载

3.2 Qwen-VL-Chat微调所需的依赖环境

3.3 Qwen-VL-Chat开始微调

3.4 将微调好的Qwen-VL-Chat与原模型进行合并

3.5 对新Qwen-VL-Chat模型进行推理测试


模型部署

1.1 下载Qwen-VL-chat模型

        首先在AutoDL平台上租一台容器实例,我租用的配置如下图所示:

        我下载模型用的是modelscope,官网为:魔搭社区。(使用modelscope下载速度更快)

# 创建虚拟环境
conda create --name qwenvl python=3.10 -y
# 激活虚拟环境
source activate qwenvl

# 下载modelscope工具,用于下载qwen-vl-chat模型
pip install modelscope

# 下载qwen-vl-chat模型
# --cache_dir xxpathxx (xxpathxx可更换你的存储文件夹路径) 
modelscope download --model qwen/qwen-vl-chat --cache_dir '/root/autodl-tmp'


1.2 下载Qwen-VL-Chat所需要的依赖环境

pip install modelscope -U
pip install transformers accelerate tiktoken -U
pip install einops transformers_stream_generator -U
pip install "pillow==9.*" -U
pip install torchvision
pip install matplotlib -U
pip install bitsandbytes

1.3 使用Qwen-VL-Chat进行推理

        准备一张图片,我们一起去试一下利用Qwen-Vl-Chat进行推理,看模型能不能跑起来。运行代码如下(modelscope上有两种调用方式,这里选取一种)

import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
from modelscope import (
    snapshot_download, AutoModelForCausalLM, AutoTokenizer, GenerationConfig,
)
from transformers import BitsAndBytesConfig
import torch
#model_id = 'qwen/Qwen-VL-Chat'
#revision = 'v1.1.0'

#model_dir = snapshot_download(model_id, revision=revision)
# model_dir为大模型路径,你可以根据自己的需求进行修改
model_dir = '/root/autodl-tmp/qwen/qwen-vl-chat'
torch.manual_seed(1234)
quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_quant_type='nf4',
    bnb_4bit_use_double_quant=True,
    llm_int8_skip_modules=['lm_head', 'attn_pool.attn'])

tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="auto", 
                                             trust_remote_code=True, fp16=True,
                                             quantization_config=quantization_config).eval()
model.generation_config = GenerationConfig.from_pretrained(model_dir, trust_remote_code=True)

query = tokenizer.from_list_format([
    {'image': '/root/autodl-tmp/Data/image.jpg'},
    {'text': '这是什么'},
])
response, history = model.chat(tokenizer, query=query, history=None)
print(response)

response, history = model.chat(tokenizer, '输出检测框', history=history)
print(response)
image = tokenizer.draw_bbox_on_latest_picture(response, history)
image.save('/root/autodl-tmp/Data/output_chat2.jpg')

        正常运行该代码后,可以看到如下结果,说明模型跑通了,及Qwen-VL-Chat部署成功,及可以进行后续操作了。


数据准备

2.1 数据格式要求

        你的图片数据集需要转换成Json数据形式,才能进行训练。Json数据格式如下所示:

[
  {
    "id": "identity_0",
    "conversations": [
      {
        "from": "user",
        "value": "Picture 1: <img>/root/autodl-tmp/check/ImageInput/部件数据/0001201905261055369600000110202000200000000000000000000000.jpg</img>\n描述下图片的主要对象以及他们的位置信息"
      },
      {
        "from": "assistant",
        "value": "<ref>复合绝缘子</ref><box>(760,414),(844,892)</box> <ref>防震锤</ref><box>(341,814),(446,873)</box> <ref>防震锤</ref><box>(337,911),(441,971)</box> <ref>挂点金具</ref><box>(731,941),(805,1080)</box> <ref>均压环</ref><box>(737,873),(820,941)</box> <ref>均压环</ref><box>(784,371),(865,431)</box>"
      }
    ]
  },
  {
    "id": "identity_1",
    "conversations": [
      {
        "from": "user",
        "value": "Picture 2: <img>/root/autodl-tmp/check/ImageInput/部件数据/0001201905261057013250000120202000200000000000000000000000.jpg</img>\n描述下图片的主要对象以及他们的位置信息"
      },
      {
        "from": "assistant",
        "value": "<ref>复合绝缘子</ref><box>(671,748),(768,1080)</box> <ref>复合绝缘子</ref><box>(844,735),(948,1080)</box> <ref>避雷器</ref><box>(529,522),(645,1072)</box> <ref>均压环</ref><box>(641,677),(769,787)</box> <ref>均压环</ref><box>(811,666),(940,773)</box>"
      }
    ]
  }
]

2.2 数据预处理

       如果你准备的图谱数据集如下图所示,同时有.jpg图片和对应的.xml信息,那接下来我教您如何将数据集转换成所需要的json数据集。

        将你的数据集,进行预处理,转换成符合要求的数据格式。转换代码如下:

import json
import os
import xml.etree.ElementTree as ET

# 初始化JSON数据结构

all_data = []

# 指定XML文件所在的目录
xml_directory = '/root/autodl-tmp/Data/ImageInput/部件数据'
i = 0
# 遍历目录下的所有XML文件
for filename in os.listdir(xml_directory):

    if filename.endswith('.xml'):
        # 构建完整的文件路径
        file_path = os.path.join(xml_directory, filename)
        i = i + 1
        # 解析XML文件
        tree = ET.parse(file_path)
        root = tree.getroot()
        file_name = root.find('filename').text
        json_data = {
            "id": f"identity_{i - 1}",
            "conversations": []
        }

        conversation1 = {
            "from": "user",
            "value": f"Picture {i}: <img>/root/autodl-tmp/Data/ImageInput/部件数据/{file_name}</img>\n描述下图片的主要对象以及他们的位置信息"
        }
        json_data['conversations'].append(conversation1)
        conversation = {
            "from": "assistant",
            "value": ""
        }
        for obj_elem in root.findall('object'):
            # 提取必要的数据
            object_name = obj_elem.find('name').text
            bbox = obj_elem.find('bndbox')
            if bbox is not None:  # 确保bndbox元素存在
                xmin = int(bbox.find('xmin').text)
                ymin = int(bbox.find('ymin').text)
                xmax = int(bbox.find('xmax').text)
                ymax = int(bbox.find('ymax').text)
            else:
                # 处理bndbox不存在的情况
                print(f"Warning: No bounding box found in {filename}")
                continue
            a = f'<ref>{object_name}</ref><box>({xmin},{ymin}),({xmax},{ymax})</box>'
            # 创建单个conversation对象并添加到JSON数组中
            if conversation["value"] == "":
                conversation["value"] = a
            else:
                conversation["value"] += " " + a

        json_data['conversations'].append(conversation)

        all_data.append(json_data)

# 将JSON对象转换为字符串并保存到文件中
with open('/root/autodl-tmp/Data/ImageJsonData', 'w', encoding='utf-8') as f:
    json.dump(all_data, f, ensure_ascii=False, indent=2)

print("转换完成,所有XML文件的数据已整合到json文件中。")

该代码中的注意事项:在代码处该三处位置,根据自己的需求进行修改。

(1)

(2)

(3)

        正常运行该代码后,会有如下结果和json数据:


模型微调

3.1 Qwen-VL-Chat微调相关文件下载

        Qwen-VL-Chat模型部署好,而且json数据集准备好之后,还需要下载一个用于模型微调的文件。

git clone https://github.com/QwenLM/Qwen-VL.git

        如果使用git clone 失败或者报错,你可以直接进入该网站,下载ZIP文件,解压缩后导入Autodl中的容器实例。

        文件内容如下:


3.2 Qwen-VL-Chat微调所需的依赖环境

# 在终端窗口输入指令,进入到刚刚下载得Qwen-VL文件
cd autodl-tmp/qwen/Qwen-VL

# 根据requierment下载依赖环境
pip install -r requirements.txt
pip install -r requirements_openai_api.txt
pip install -r requirements_web_demo.txt
pip install peft

3.3 Qwen-VL-Chat开始微调

        在Qwen-VL文件夹下,创建.sh文件,命名为qlora.sh。并将下列指令复制到qlora.sh文件中。指令如下:

python3 /root/autodl-tmp/qwen/Qwen-VL/finetune.py     --model_name_or_path /root/autodl-tmp/qwen/qwen-vl-chat      --data_path /root/autodl-tmp/Data/ImageJsonData/data.json     --bf16 True     --fix_vit True     --output_dir output_qwen     --num_train_epochs 5     --per_device_train_batch_size 1     --per_device_eval_batch_size 1     --gradient_accumulation_steps 8     --evaluation_strategy "no"     --save_strategy "steps"     --save_steps 1000     --save_total_limit 10     --learning_rate 1e-5     --weight_decay 0.1     --adam_beta2 0.95     --warmup_ratio 0.01     --lr_scheduler_type "cosine"     --logging_steps 1     --report_to "none"     --model_max_length 600     --lazy_preprocess True     --gradient_checkpointing true     --use_lora

 注意事项:该指令必须是一行,不能有手动换行。如下图片是为了方便大家查看。

然后在终端执行如下命令:

sh qlora.sh

如果出现权限问题,请自行查询解决方法。

正确运行开始微调时如图所示(如遇到报错,此步骤后有详细讲解,请参考):

如果遇到如下错误,可以根据下列操作进行解决。

错误一:运行 sh qlora.sh时,出现如下报错。且执行pip install deepspeed后同样报错。

解决方法:

# 添加下载源
conda config --add channels https://anaconda.org

# 添加完之后在进行下载
pip install deepspeed

错误二:运行 sh qlora.sh时,出现如下错误。

解决方法:

        (1)在1.1步骤中下载的Qwen-VL-Chat模型中,找到modeling_qwen.py文件。

        (2)打开modeling_qwen.py文件,在代码654行处,作出如下修改。


3.4 将微调好的Qwen-VL-Chat与原模型进行合并

        在微调好之后,需要将微调好的Qwen-VL-Chat模型与原大模型进行合并,合并之后就可以正常推理了。合并代码如下:

from peft import AutoPeftModelForCausalLM  # 确保导入所需的模块
from modelscope import (
     AutoTokenizer
)
 
path_to_adapter = "/root/autodl-tmp/qwen/Qwen-VL/output_qwen"
# 从预训练模型中加载自定义适配器模型
model = AutoPeftModelForCausalLM.from_pretrained(
    path_to_adapter,  # 适配器的路径
    device_map="auto",  # 自动映射设备
    trust_remote_code=True  # 信任远程代码
).eval()  # 设置为评估模式
new_model_directory = "/root/autodl-tmp/qwen/New-Model-qwenvl"
tokenizer = AutoTokenizer.from_pretrained(
    path_to_adapter, trust_remote_code=True,
)
tokenizer.save_pretrained(new_model_directory)
 
# 合并并卸载模型
merged_model = model.merge_and_unload()
# 保存合并后的模型
merged_model.save_pretrained(new_model_directory, max_shard_size="2048MB", safe_serialization=True)

注意事项:

如若出现错误:AttributeError: 'QWenTokenizer' object has no attribute 'IMAGE_ST'
请参考以下链接:

[BUG] <title>合并lora权重时遇到AttributeError: 'QWenTokenizer' object has no attribute 'IMAGE_ST'问题 · Issue #287 · QwenLM/Qwen-VL (github.com)


3.5 对新Qwen-VL-Chat模型进行推理测试

        推理测试的代码与1.3中的代码一致,只需将模型路径修改成微调后新模型的路径即可。测试输入的数据自己根据需求修改即可。

输出结果:

测试通过!!!!!!!!!!!!!

完结撒花!!!!!!!!!!!!!

参考文章:

通义千问(Qwen-VL)本地微调_qwen-vl 微调-CSDN博客

Qwen-VL Best Practice — swift 2.5.0.dev0 documentation

Qwen-VL大模型LoRA微调、融合及部署_qwen-vl微调-CSDN博客

通义千问-VL-Chat · 模型库 (modelscope.cn)

[BUG] <title>合并lora权重时遇到AttributeError: 'QWenTokenizer' object has no attribute 'IMAGE_ST'问题 · Issue #287 · QwenLM/Qwen-VL (github.com)

Update modeling_qwen.py · Qwen/Qwen-VL-Chat at e718765 (huggingface.co)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值