InternVL2-1B成社区最喜爱的多模态大模型?低门槛微调教程来啦

在今年 7 月 4 日举行的 2024 WAIC 科学前沿主论坛上,书生·万象多模态大模型(InternVL 2.0)正式发布,并陆续开源了 1B、2B、4B、8B、26B、40B 以及 76B 共 7 个参数版本的模型。目前 IntenVL2 系列模型在 Hugging Face 平台上的下载量已超 160 万,其中 InternVL2-1B 下载量接近 100 万,可谓是“社区用户最喜爱的模型”!

开源链接:(欢迎 star)

https://github.com/OpenGVLab/InternVL

模型链接:(文末点击阅读原文可直达,欢迎使用)

https://huggingface.co/collections/OpenGVLab/internvl-20-667d3961ab5eb12c7ed1463e

今天就给大家重点介绍下如何使用 XTuner 微调 InternVL2 系列模型,以 微调 InternVL-1B 使之具有给表情包配上内涵梗文的能力 为例。无基础小白或者初入门的新手都适用,赶紧学习起来吧!

微调教程来自社区用户投稿,作者:小鲶鱼。

XTuner 是一个高效、灵活、全能的轻量化大模型微调工具库。高效支持大语言模型 LLM、多模态图文模型 VLM 的预训练及轻量级微调。

使用 XTuner 微调的总体步骤为:

  • 下载 XTuner 源码,安装源码所需依赖

  • 下载 InternVL2-1B 模型,下载或者制作 VLM 数据集

  • 修改微调训练的参数

  • 输入微调命令,开始微调

  • 可视化监控

  • 模型合并

  • 微调后模型的推理测试

微调流程图

1. 下载 XTuner 源码,安装源码所需依赖

下载源码:

# 创建一个目录,用来存放源代码``mkdir -p /root/code``   ``cd /root/code``git clone -b v0.1.23  https://github.com/InternLM/XTuner

安装所需依赖的时候,为了避免 XTuner 项目的包跟其他项目的包产生冲突,需要新建一个虚拟环境并激活。在虚拟的环境里安装依赖。

比如有的项目需要 Numpy<2.0,但是有的项目需要 Numpy>2.0,那么就冲突了。

创建虚拟环境:

conda create --name xtuner python=3.10 -y``   ``# 激活虚拟环境(注:后续的所有操作都需要在这个虚拟环境中进行)``conda activate xtuner``   ``# 安装torch和torch系列的库,torch是深度学习框架,AI项目大部分都是基于这个框架的``conda install pytorch==2.1.2 torchvision==0.16.2 torchaudio==2.1.2 pytorch-cuda=12.1 -c pytorch -c nvidia -y``# 安装其他依赖``apt install libaio-dev -y``pip install transformers==4.39.3 streamlit==1.36.0 lmdeploy==0.5.3

安装 XTuner 所需依赖:

cd /root/code/XTuner``pip install -e '.[deepspeed]'

安装完毕后,测试 XTuner 相关的命令能否生效:

xtuner version # 查看xtuner的版本``   ``xtuner help # 查看xtuner的一系列命令

2. 下载 InternVL2-1B 模型,

下载或者制作 VLM 数据集

下载 InternVL2-1B 模型

cd /root``mkdir -p model``   ``# 安装huggingface-hub库,方便从hf里下载或上传文件``pip install huggingface-hub``# 从hf的OpenGVLab/InternVL2-1B仓库里下载模型到本地的/root/model/InternVL2-1B文件夹``huggingface-cli download OpenGVLab/InternVL2-1B --local-dir /root/model/InternVL2-1B

如果下载失败,则设置环境变量 HF_ENDPOINT,从 hf 的镜像站下载模型:

export HF_ENDPOINT=https://hf-mirror.com``huggingface-cli download OpenGVLab/InternVL2-1B --local-dir /root/model/InternVL2-1B

下载或者制作 VLM 数据集

这一步比较重要,如果用现成的数据集,也需要把格式准备正确了。如果自己去爬取图片,然后自己人工标注,则爬取和标注过程都比较花时间,是注入“智能”的过程。

InternVL2 系列模型所需的数据集格式为:图片文件夹+索引目录 json 文件。

其中 json 文件的格式如下:

[`  `{`    `"id": "11", # id也可以不设置`    `"image": "ex_images/MjxjVcrFf9ReweWibHwWQsM2iaiajJd0GQvFPsMPic79PnZGffSNfdVTQV9Z5J4FW1XciaIibRoosGuIfSPaFBuADCQA.jpg",`    `"conversations": [`      `{`        `"from": "human",`        `"value": "<image>\n请你用中文描述一下这张图片"`      `},`      `{`        `"from": "gpt",`        `"value": "你嘴里啃的是什么!我问你嘴里啃的是什么!"`      `}`    `]`  `},`  `{`    `"id": "22",`    `"image": "ex_images/007aPnLRgy1hb39z0im50j30ci0el0wm.jpg",`    `"conversations": [`      `{`        `"from": "human",`        `"value": "<image>\nDescribe this image in English"`      `},`      `{`        `"from": "gpt",`        `"value": "Ok Doge"`      `}`    `]`  `},` `]

如果要自己构建数据集,可以先爬取符合自己要求的图片,然后标注的内容保存为同名的 txt 文件。之后写一个 python 脚本把这些图文数据对给创建一个 json 格式的索引目录。如果是代码小白,可以写好要求,让 GPT 来帮自己完成这个脚本。

好在 Hugging Face 上的 zhongshsh/CLoT-Oogiri-GO 数据集恰好符合 InternVL2-1B 的格式,因此可以直接下载后使用。CLoT-Oogiri-GO 数据集是一个表情包和梗文的配套数据集,有2000多张图。

数据集地址:https://huggingface.co/datasets/zhongshsh/CLoT-Oogiri-GO

由于里面的数据包含 Text2Text、Image2Text 等多种类型,图片数量一共有 3 万多张,数据量有些大。作为轻量型的 demo,需要对其进行筛选,筛选出 2000 多张图片。筛选后的数据集,我上传到了 modelscope 数据集里:https://www.modelscope.cn/datasets/livehouse/CLoT_cn_2000/files

下载 CLoT_cn_2000 数据集的代码为:

cd /root/code``mkdir datasets``   ``pip install modelscope # modelscope包,用来往modelscope下载上传文件``   ``# 把数据集放在/root/code/datasets里``modelscope download livehouse/CLoT_cn_2000 --repo-type dataset --local_dir /root/code/datasets

下载完成后需要解压:

cd /root/code/datasets``unzip ./CLoT_cn_2000.zip -d ./

笔者测试的时候`unzip ./CLoT_cn_2000.zip -d ./CLoT_cn_2000`多加了个 CLoT_cn_2000,导致嵌套了

测试 InternVL2-1B 的推理效果

下载完数据集了,先从里面找一张图来用 InternVL2-1B 模型推理一下看看效果:

随机选择了 /root/code/datasets/CLoT_cn_2000/ex_images/007aPnLRgy1hb39z0im50j30ci0el0wm.jpg 图片,对它进行 Image-to-Text 的推理。

from lmdeploy import pipeline``from lmdeploy.vl import load_image``   ``pipe = pipeline('/root/model/InternVL2-1B')``   ``image = load_image('/root/code/datasets/CLoT_cn_2000/ex_images/007aPnLRgy1hb39z0im50j30ci0el0wm.jpg')``response = pipe(('请你根据这张图片,讲一个脑洞大开的梗', image))``print(response.text)

上面的代码是使用 InternVL2-1B 模型对目标图片进行 Image2Text 单图推理的代码。

用下面的代码创建一个空的 test.py 文件,然后把上面代码粘贴进去,再执行这个代码即可。

touch /root/code/test.py # 创建空文件

粘贴后,再 Ctrl+S 保存一下 test.py 文件,之后运行这个代码:

python /root/code/test.py

图片和图片推理的结果如下:

下方是表情包数据集里的测试图,上方是 InternVL2-1B 对该图的推理结果。从文字回答可以看出 InternVL2-1B 目前还没法胜任"表情包配梗"的任务。

正是因为这个特定的“表情包配梗”任务无法被 VLM 实现,所以才要构建表情包数据集,之后用它来微调 VLM 模型。

3. 修改微调训练的参数

/root/code/XTuner/xtuner/configs/internvl/v2 文件夹里,有一个 internvl_v2_internlm2_2b_qlora_finetune.py 文件,这个文件是用 qlora 方法去微调 InternVL2 系列模型的一个脚本。

这里有训练的参数,按要求修改之后,再执行这个脚本,就可以进行微调训练了!

这个训练脚本要修改的参数有:

  • 模型路径

  • 训练集路径

  • 批次大小

  • 训练轮数

  • 学习率

  • 保存间隔

  • 可视化

  • 甚至调度器 scheduler、优化器 optimizer、返回点 resume

基础部分就只介绍前7个参数。

3.1 修改模型路径与训练集的路径

配置文件里的模型路径与训练集路径

这个 internvl_v2_internlm2_2b_qlora_finetune.py 的脚本文件虽然文件名上包含 2b,实际上对于 InternVL2 系列的任意参数量的模型都能用,只需要 path 进行正确赋值即可。

#######################################################################``#                          PART 1  Settings                           #``#######################################################################``# Model``# path = 'OpenGVLab/InternVL2-2B'``path = '/root/model/InternVL2-1B'``   ``# Data``# data_root = './data/llava_data/'``# data_path = data_root + 'LLaVA-Instruct-150K/llava_v1_5_mix665k.json'``# image_folder = data_root + 'llava_images'``# prompt_template = PROMPT_TEMPLATE.internlm2_chat``# max_length = 8192``data_root = '/root/code/datasets/CLoT_cn_2000/'``data_path = data_root + 'ex_cn.json'``image_folder = data_root``prompt_template = PROMPT_TEMPLATE.internlm2_chat``max_length = 6656

3.2 修改批次大小、训练轮数、学习率和保存间隔

对应的参数分别是 batch_size 和 accumulative_counts、max_epochs、 lr 以及 save_steps。

# Scheduler & Optimizer``batch_size = 4  # per_device``accumulative_counts = 2``dataloader_num_workers = 4``max_epochs = 4``optim_type = AdamW``# official 1024 -> 4e-5``# lr = 1e-5``lr = 2e-5``betas = (0.9, 0.999)``weight_decay = 0.05``max_norm = 1  # grad clip``warmup_ratio = 0.03``   ``# Save``# save_steps = 1000``save_steps = 100``# save_total_limit = 1  # Maximum checkpoints to keep (-1 means unlimited)``save_total_limit = -1

批次大小

真实的批次大小是 per_device_batch_size = batch_size*accumulative_counts,这里暂时设置batch_size = 4、accumulative_counts = 2。

训练脚本文件 /root/code/XTuner/xtuner/configs/internvl/v2 /internvl_v2_internlm2_2b_qlora_finetune.py 里,这两个参数跟批次大小相关的内容为:

# Scheduler & Optimizer``# batch_size = 8  # per_device``batch_size = 4``accumulative_counts = 2
  • 训练脚本里 batch_size 默认值是 8,太大了。后面实测,batch_size = 4、 accumulative_counts = 2 的时候已经吃了 40G 显存了。

  • 如果是 8G 显存,建议这两个参数都赋值为 1。否则会 CUDA Out of Memory。

训练轮数

max_epochs 这个参数是最大训练轮数,/root/code/XTuner/xtuner/configs/internvl/v2 /internvl_v2_internlm2_2b_qlora_finetune.py 里,这个参数默认值是 1:

# max_epochs = 1``max_epochs = 4

这个只训练 1 轮基本是不足的,因为图片本身就 2000 张,不够多。还是要多训练几轮,确保训练的过程更加充分。训练的过程是拟合的过程,一个训练集来回多训练几遍,确保拟合到位。

训练轮数(max_epochs)越高,拟合程度就越高。

  • 训练轮数太低,不拟合(模型回答问题不准确)。

  • 训练轮数太高,则会过拟合(模型只能正确回答训练集里的问题;用其他的问题去提问,也只能得到训练集里写好的标注)。

如果图片有上百万张,那么 max_epochs = 1 也没问题。

对于拟合的举例解释:拟合的意思是让模型的预测值更加靠近真实值。

比如用面积x预测房价y的模型,训练集里有很多个(x1,y1),(x2,y2),…,(xk,yk),…,(xn,yn)的坐标点,这些坐标组成散点图,拟合出一条曲线或者直线,拟合的解析式为y=f(x),这就是一次拟合

那么这个y=f(x)可以看成是模型。对于面积xk,模型预测的值是f(xk),真实值是yk,如果f(xk)离yk很近,那么从这个xk的数据来看,拟合程度就很高。

这时候对于 xk 定义一个 loss_xk = f(xk) - yk,那么对于 x1,x2,…,xk,…,xn,就有loss_x1, loss_x2, …, loss_xk, …, loss_xn,这 n 个 loss。给他们取平均值,得到Loss。如果这个 Loss 最小化,就说明这个拟合最到位。因此拟合的过程又演变为 Loss 下降的过程

学习率

lr 是 learning rate 的缩写,也就是学习率。在训练脚本 /root/code/XTuner/xtuner/configs/internvl/v2 /internvl_v2_internlm2_2b_qlora_finetune.py 里,这个参数默认值是 lr = 1e-6。这个1e-6的值可以先用着,如果发现跑完一轮训练,Loss 下降得不明显,那么需要给 lr 提高。

Loss 下降从哪里观察:

  • 进行训练的时候命令行的日志里会显示 Loss 值

  • 训练过程的可视化图表也会有 Loss 的变化曲线

我还是习惯用 10^-4 数量级的 lr 作为开局的 lr:

# official 1024 -> 4e-5``# lr = 1e-6``lr = 2e-4

学习率可以理解为训练过程(拟合过程、Loss 下降过程)的拟合速度。学习率越高,拟合得越快(相当于 Loss 下降的越快)。

  • lr 比较大,可以快速拟合,往往几轮训练就拟合到位了。

  • lr 比较小,拟合的速度比较慢,但是拟合比较精细,一些个体数据的特征也能照顾到位。

保存间隔

save_steps,这个参数的意思是保存间隔,每多少步保存一个 lora 模型。训练脚本 /root/code/XTuner/xtuner/configs/internvl/v2 /internvl_v2_internlm2_2b_qlora_finetune.py 里设置的默认值是 1000:

# Save``# save_steps = 1000``save_steps = 100``# save_total_limit = 1  # Maximum checkpoints to keep (-1 means unlimited)``save_total_limit = -1

我这里修改为 save_steps = 100 和 save_total_limit = -1。

  • save_total_limit要从 1 改为 -1。如果这个值为 1,那么 iter_200.pth 生成之后,iter_100.pth 就会删掉,后面依此类推。

  • 保存间隔 save_steps 的设置,建议新手设置为 save_steps = 100,然后看训练过程中,保存的“训练结果”的次数是高频还是低频。如果保存的频率太高,那再手动删掉一些即可。

这个保存间隔,需要计算本次训练一共要训练多少步。假如某次训练一共训练 1000 步,这个保存间隔如果是 1000 的话,就不合适了。因为训练过程的中间状态就无法保存,无法达到“间隔”的目的。

  • 训练结果,就是训练出来的 lora 模型。这些结果会保存在 /root/code/qlora_output 里,是一系列 iter_100.pth, …, iter_1000.pth,iter_2000.pth 这种的后缀为 pth 的文件。这些训练结果可以称为 lora 模型,它的学名叫做适应层(Adaptation)。这些适应层只有跟大模型合并了,才是完整的模型。

  • 如果训练了一个小时,一下子出来了 100 多个 iter_x00.pth 模型,那就跳着删掉一些。如果出来了 10 多个 lora 模型,那 save_steps = 100 就是一个合理的保存间隔。

这里做一个总训练步数的计算示例,感兴趣的可以读一下:

我的训练集里有 2000 个图文对的数据,由于显存的限制,无法 2000 张图和标注同时进入模型进行拟合。拟合,可以用之前用面积 x 预测房价 y 的模型组成散点图拟合曲线的那个例子去理解。

于是要拆开,分批进去。如果批次大小是 8,那么 2000 个图文就拆分成 250 批,每批 8 个图文对。每一批数据进入模型后,都要进行一次拟合(散点图新增8个点,修正一次拟合曲线,就是一次新的拟合)。

这一次拟合就是一次迭代。迭代是 iteration,训练的过程就是一次次拟合(迭代)的过程,所以保存文件有个 iter 的前缀。

当 250 批数据全部进入模型,拟合一遍之后,才算完成了一轮训练

上面两段介绍了一步迭代和一轮训练的过程。那么假如我现在的训练集里的图文数据是 2000 个,batch_size =4、accumulative_counts = 2、max_epochs = 4。对于一轮训练来说,2000个数据,拆分为 250 批,每批 8 个图文对进入模型进行拟合。每一批都是一次迭代,因此 250 批就是 250 个迭代。所以一轮训练要迭代 250 次。一共 4 轮训练,所以整个训练需要迭代 1000 次。

3.3 修改可视化参数

在脚本里 Ctrl+F 检索 visualizer,可以看到 visualizer = None 的一行代码,把它替换为下面的代码:

# visualizer = None``   ``visualizer = dict(`    `type=Visualizer,`    `vis_backends=[`        `dict(type=WandbVisBackend, init_kwargs=dict(project='internvl2-example'))])

启用 Wandb 可视化,需要开一个终端在 XTuner 的虚拟环境里安装 wandb 库,并登陆 wandb 账号:

conda activate xtuner``pip install wandb``wandb login

在 wandb login 那一步,需要用 wandb 的登陆密钥,需要进入 https://wandb.ai/ 注册一下,注册后进入https://wandb.ai/authorize,复制登陆密钥后,回到命令行里,粘贴并回车即可登陆到 wandb。

之后运行脚本的时候,可视化部分的 wandb 才能正常工作。

这样训练过程的数据就会传输到 wandb 这个网站,开启训练后,命令行会提示查看训练曲线的网址:

wandb 可视化的 project 地址

绘制得到可视化曲线,比如下面这样:

wandb 的可视化效果

有了可视化曲线,可以便捷地观察 lr、loss 曲线,还有训练总步数也能一目了然。如果不需要这个可视化曲线,可以保持 visualizer = None 不变。

4. 输入微调命令,开始微调

使用 xtuner train 命令开始微调:

conda activate xtuner``NPROC_PER_NODE=1 xtuner train /root/code/XTuner/xtuner/configs/internvl/v2/internvl_v2_internlm2_2b_qlora_finetune.py  --work-dir /root/code/qlora_output  --deepspeed deepspeed_zero1

1B 模型跟 2B 的 Language Part 不一样,直接执行微调命令,会有一个 NotImplementedError 的报错:

after_test:``(VERY_HIGH   ) RuntimeInfoHook`                     `--------------------` `after_run:``(BELOW_NORMAL) LoggerHook`                        `--------------------` `Traceback (most recent call last):`  `File "/root/InternLM/code/XTuner/xtuner/tools/train.py", line 360, in <module>`    `main()`  `File "/root/InternLM/code/XTuner/xtuner/tools/train.py", line 356, in main`    `runner.train()`  `File "/root/.conda/envs/xtuner/lib/python3.10/site-packages/mmengine/runner/_flexible_runner.py", line 1160, in train`    `self._train_loop = self.build_train_loop(`  `File "/root/.conda/envs/xtuner/lib/python3.10/site-packages/mmengine/runner/_flexible_runner.py", line 958, in build_train_loop`    `loop = LOOPS.build(`  `File "/root/.conda/envs/xtuner/lib/python3.10/site-packages/mmengine/registry/registry.py", line 570, in build`    `return self.build_func(cfg, *args, **kwargs, registry=self)`  `File "/root/.conda/envs/xtuner/lib/python3.10/site-packages/mmengine/registry/build_functions.py", line 121, in build_from_cfg`    `obj = obj_cls(**args)  # type: ignore`  `File "/root/InternLM/code/XTuner/xtuner/engine/runner/loops.py", line 32, in __init__`    `dataloader = runner.build_dataloader(`  `File "/root/.conda/envs/xtuner/lib/python3.10/site-packages/mmengine/runner/_flexible_runner.py", line 824, in build_dataloader`    `dataset = DATASETS.build(dataset_cfg)`  `File "/root/.conda/envs/xtuner/lib/python3.10/site-packages/mmengine/registry/registry.py", line 570, in build`    `return self.build_func(cfg, *args, **kwargs, registry=self)`  `File "/root/.conda/envs/xtuner/lib/python3.10/site-packages/mmengine/registry/build_functions.py", line 121, in build_from_cfg`    `obj = obj_cls(**args)  # type: ignore`  `File "/root/InternLM/code/XTuner/xtuner/dataset/internvl_dataset.py", line 160, in __init__`    `raise NotImplementedError``NotImplementedError

找到 /root/code/XTuner/xtuner/dataset/internvl_dataset.py 的 160 行,其内容为:

        `if self.cfg.llm_config.architectures[0] == 'Phi3ForCausalLM':`            `self._system = 'You are an AI assistant whose name is Phi-3.'`            `self.template[`                `'INSTRUCTION'] = '<|user|>\n{input}<|end|><|assistant|>\n'`        `elif self.cfg.llm_config.architectures[0] == 'InternLM2ForCausalLM':`            `self._system = 'You are an AI assistant whose name ' \`                           `'is InternLM (书生·浦语).'`            `self.template['SYSTEM'] = '<|im_start|>system\n{system}<|im_end|>'`            `self.template[`                `'INSTRUCTION'] = '<|im_start|>user\n{input}' \`                                 `'<|im_end|><|im_start|>assistant\n'`        `else:`            `raise NotImplementedError`

由于 InternVL2-1B 的 Language Part 是 Qwen2-0.5B-Instruct,既不是 Phi3,也不是InternLM2,所以要为这个 Qwen2 新增一个判断条件:

+        elif self.cfg.llm_config.architectures[0] == 'Qwen2ForCausalLM':``+            self._system = 'You are an AI assistant whose name is Qwen2.'``+            self.template[``+                'INSTRUCTION'] = '<|user|>\n{input}<|end|><|assistant|>\n'`        `else:`            `raise NotImplementedError

备注:

  • InternVL2-2B 的 Language Part 是 internlm2-chat-1_8b

  • InternVL2-4B 的 Language Part 是 Phi-3-mini-128k-instruct

  • InternVL2-8B 的 Language Part 是 internlm2_5-7b-chat

  • InternVL2-26B 的 Language Part 是 internlm2-chat-20b

  • InternVL2-40B 的 Language Part 是 Nous-Hermes-2-Yi-34B

  • InternVL2-Llama3-76B 的 Language Part 是 Hermes-2-Theta-Llama-3-70B

因此微调 2B、4B、8B、26B 模型时,不会遇到这个 NotImplementedError,但是微调 1B、40B 和 76B 的模型时,会遇到这个 NotImplementedError

修改完成后,运行微调的命令:

conda activate xtuner``NPROC_PER_NODE=1 xtuner train /root/code/XTuner/xtuner/configs/internvl/v2/internvl_v2_internlm2_2b_qlora_finetune.py  --work-dir /root/code/qlora_output  --deepspeed deepspeed_zero1

执行这个运行命令,有时候命令行会沉默几秒,卡住不动。只加载了配置 configs,一直不出现下面这种加载模型的日志:

Loading checkpoint shards: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:05<00:00,  1.47s/it]

那就耐心等待 5 分钟,或者关掉 wandb 可视化功能(给配置文件里的 visualizer = None 取消注释)。

训练开始后,发现 batch size 是 4 * 2 = 8 的时候,占用 40G 显存。

训练开始,显存占用 40G

训练完成后得到的一系列模型如下:

训练中间过程的 20 个 lora (每隔 100 步保存一次 lora)

一共是 2000 步迭代,并不是 之前计算的1000 步迭代。可能是代码里计算总步数的时候,使用的实际 batch size,错误地用了 batch_size 这个变量。而没有用 per_device_batch_size=batch_size * accumulative_counts) 这个变量。

在上一节保存间隔里有一个延伸计算,那里介绍了一个训练所需的迭代步数多少。从那里得到的经验,可以进行下面的计算:

如果 真实的批次大小 错误地用了 batch_size=4 这个变量,那么 2000 张图片跑完一轮,就是 500 步。那么一个 epoch 就是 500 个 iteration。我设置了 max_epochs = 4,因此跑完 4 轮,就需要 2000 个 iteration。

实际上这个 2000 个 iteration 是错误的数据,因为漏掉了 accumulative_counts 这个变量。可能是 XTuner 源代码里漏掉了accumulative_counts ,不过无伤大雅。

5. 可视化监控

打开访问命令行里的那个 wandb 的 project 网址,在训练完成后,会看到训练过程的 2000 个迭代的过程被各种曲线可视化了:

训练过程的可视化

第 1 张图 lr 是用的余弦调度器,因此 lr 从 2e- 5逐渐衰减到 0。

第 6 张图 loss 逐步下降,loss 下降到最后是第 2000 步迭代,那时候保存了 iter_2000.pth。

并非 loss 越低,拟合的效果越好。只需要基本拟合,然后修改 lr 进行进一步的细致训练。由于这里是个简单的 demo,就直接用 iter_2000.pth 进行微调后的推理测试了。

6. 模型合并

使用 /root/code/XTuner/xtuner/configs/internvl/v1_5 里的 convert_to_official.py 脚本进行合并,把 /root/code/qlora_output/iter_2000.pth 这个 lora 跟大模型合并为 /root/model 文件夹里的 InternVL2-1B-iter2000 模型:

conda activate xtuner``   ``# transfer weights``python /root/code/XTuner/xtuner/configs/internvl/v1_5/convert_to_official.py /root/code/XTuner/xtuner/configs/internvl/v2/internvl_v2_internlm2_2b_qlora_finetune.py /root/code/qlora_output/iter_2000.pth /root/model/InternVL2-1B-iter2000/

python ./convert_to_official.py 后面需要加入三个参数,分别是配置文件路径、lora 文件路径和保存路径,缺一不可。因为在 merge 脚本 convert_to_official.py 里需要这三个参数:

convert_to_official.py 脚本里的三个参数

运行 merge 模型的命令,得到微调后的 InternVL2-1B,命名为 InternVL2-1B-iter2000:

左边是合并后的模型,右边是合并脚本运行时的日志

7. 微调后模型的推理测试

把 Step3 里的 /root/code/test.py 里的模型路径修改一下:

from lmdeploy import pipeline``from lmdeploy.vl import load_image``   ``pipe = pipeline('/root/model/InternVL2-1B-iter2000')``   ``image = load_image('/root/code/datasets/CLoT_cn_2000/ex_images/007aPnLRgy1hb39z0im50j30ci0el0wm.jpg')``response = pipe(('请你根据这张图片,讲一个脑洞大开的梗', image))``print(response.text)

之后 python /root/code/test.py 进行测试:

微调后 InternVL2-1B-iter2000 的推理结果

图片推理的输出语气基本到位了,但是准确度还差一些。

训练集里这张图的标注是“『住手!现在的你还不是那家伙的对手!先撤吧!!”

微调后的模型进行这张图的推理,输出结果至少有“住手,先撤吧”的含义,才能说明微调过程达到拟合。现在是不拟合的状态,还需要继续训练,直到拟合。

训练集里的图片和标注

为了解决不拟合的问题,需要在 iter2000 这个 2000 步迭代的状态上继续训练,有两种继续训练的方案:

1. 使用 path 参数,用合并后的大模型文件进行继续训练

在配置文件(训练脚本, /root/code/XTuner/xtuner/configs/internvl/v2 /internvl_v2_internlm2_2b_qlora_finetune.py)里,把 path = “/root/model/InternVL2-1B” 修改为 “/root/model/InternVL2-1B-iter2000”:

# Model``# path = 'OpenGVLab/InternVL2-2B'``path = '/root/model/InternVL2-1B-iter2000'

之后其他参数不需要改,跳到 Step4 执行训练命令,在InternVL2-1B-iter2000模型的基础上继续训练,进一步进行拟合。

2. 使用 reumse 和 load_from 参数,用合并前的 lora 文件进行继续训练。配置文件(训练脚本)里还有 reumse 和 load_from 的参数,修改一下它们,即可进行 resume:

# load from which checkpoint``# load_from = None``load_from = "/root/code/qlora_output/iter_2000.pth"``   ``# whether to resume training from the loaded checkpoint``# resume = False``resume = True

用了这两个参数,跳到 Step4 执行训练命令,就可以在 iter2000.pth 基础上继续训练。

结语

经过上面 7 个步骤,就完成 InternVL2-1B 的微调啦,掌握了工具,就可以开动脑筋构建合适的数据集,为 VLM 注入自己的智能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值