VisualGLM
VisualGLM是由清华大学的GLM团队推出的一个新的多模态对话语言模型,支持图像、中文和英文的输入和输出。VisualGLM大幅度地提升了多模态对话的SOTA水平,创造了令人惊叹的效果,能够根据图像和文本的内容生成符合人类偏好的回答,成为了多模态领域的新时代引领者。 VisualGLM完全开源可商用,基于 Transformer 结构,语言模型部分基于 ChatGLM-6B ,具有 62 亿参数;图像部分通过训练 BLIP2-Qformer 构建起视觉模型与语言模型的桥梁,整体模型共 78 亿参数。
VisualGLM-6B
VisualGLM-6B 依靠来自于 CogView 数据集的30M高质量中文图文对,与300M经过筛选的英文图文对进行预训练,中英文权重相同。该训练方式较好地将视觉信息对齐到ChatGLM的语义空间;之后的微调阶段,模型在长视觉问答数据上训练,以生成符合人类偏好的答案。
前期准备
安装mindformers
参考README "mindformers安装" 安装mindformers。
环境要求
- 硬件: Atlas 800T A2
- MindSpore: 2.2.10
- MindSpore Lite: 2.2.10
- MindFormers: dev
- Mindpet: 1.0.2
注: VisualGLM-6B推理可以在单卡上完成部署,全量微调至少需要8卡。
生成RANK_TABLE_FILE(多卡运行必须环节)
运行mindformers/tools/hccl_tools.py生成RANK_TABLE_FILE的json文件
# 运行如下命令,生成当前机器的RANK_TABLE_FILE的json文件
python mindformers/tools/hccl_tools.py --device_num "[0,8)"
注: 若使用ModelArts的notebook环境,可从 /user/config/jobstart_hccl.json
路径下直接获取rank table,无需手动生成
VisualGLM-6B 预训练权重下载和转换
1. 从huggingface下载tokenizer权重后转换
从HuggingFace网站下载visualglm 6b词库的文件 ice_text.model。
下载地址:https://huggingface.co/THUDM/visualglm-6b/tree/main
2. 从SAT仓库下载visualglm权重
推荐使用rclone工具下载模型
步骤
-
下载rclone工具
下载地址:https://rclone.org/downloads/
根据服务器的类型和处理器,选择对应的文件。
下载完以后解压,把其中的脚本拷贝出来,放到执行目录下:
cp rclone*/rclone /usr/local/bin/
-
创建rclone配置文件
在home目录创建rclone.conf文件
- Windows系统对于的目录:C:\Users\用户名.config\rclone\rclone.conf;
- linux系统对应的目录:~/.config/rclone/rclone.conf
配置内容,这里的配置不需要修改:
[r2]
type = s3
provider = Cloudflare
access_key_id = eb4d69e273848089c7f9b9599cdcd983
secret_access_key = 367e9b21fef313f187026320016962b47b74ca4ada7d64d551c43c51e195d7a5
endpoint = https://c8a00746a80e06c4632028e37de24d6e.r2.cloudflarestorage.com
acl = private
- 使用rclone脚本来下载权重文件
参数说明
- THREAD_COUNT:下载的线程数量,可以根据实际带宽来调整。
cd 模型下载路径/
rclone copy -P --multi-thread-streams THREAD_COUNT --no-check-certificate -vv --size-only r2:/sat/visualglm-6b.zip ./
- 执行权重转换脚本
cd research/visualglm
python convert_weight.py --torch_path TORCH_CKPT_DIR --vit_mindspore_path VIT_CKPT_PATH --qformer_mindspore_path QFORMER_CKPT_PATH --glm_mindspore_path GLM_CKPT_PATH
参数说明
- TORCH_CKPT_DIR: huggingface VisualGLM-6B权重保存目录路径,路径要指定到文件;
- VIT_CKPT_PATH: vit模型mindspore权重文件保存路径,路径要指定到文件;
- QFORMER_CKPT_PATH: qformer模型mindspore权重文件保存路径,路径要指定到文件;
- GLM_CKPT_PATH: glm模型mindspore权重文件保存路径和名称,路径要指定到文件。
注意:
- 请安装torch=2.0.1和transformers=4.33.2版本,cuda版本11.6及以上
- 该脚本会在glm模型的路径下生成glm_6b_for_lite.ckpt文件,该权重是用于lite推理的。
MindSpore推理
接口说明请参考API接口
注意
- 图片路径:推理用的参考图片在代码仓库的examples路径下
- 提示词:每张图片都有一个对应的参考提示词,可以在example_inputs.jsonl文件找到
由于visualglm模型的权重需要用户自行下载,因此在启动前,请先行在配置文件中将权重的路径配置完成。
打开配置文件 research/visualglm/run_visualglm_6b_image_to_text_generation.yaml,修改如下:
- 替换/path/to/visualglm_qformer.ckpt为上面转换的qformer权重的实际路径
- 替换/path/to/visualglm_vit.ckpt为上面转换的vit权重的实际路径
- 替换/path/to/glm_6b.ckpt为上面转换的glm权重的实际路径
- 替换/path/to/ice_text.model为上面下载的ice_text.model的实际路径
model:
model_config:
type: VisualGLMConfig
#...
checkpoint_name_or_path: "/path/to/visualglm_qformer.ckpt" # visualglm qformer weight
vision_config:
#...
checkpoint_name_or_path: "/path/to/visualglm_vit.ckpt" # visualglm vit weight
text_config:
type: GLMConfig
#...
checkpoint_name_or_path: "/path/to/glm_6b.ckpt" # visualglm glm weight
processor:
type: VisualGLMProcessor
image_processor:
type: VisualGLMImageProcessor
image_size: 224 # input image size
tokenizer:
#...
checkpoint_name_or_path: "/path/to/ice_text.model"
如果使用增量推理,需要在配置文件中use_past值设置为True。
visualglm的generate接口使用脚本已集成在run_visualglm.py脚本中,运行此脚本命令:
cd research/visualglm
python run_visualglm.py --config CONFIG_PATH --image_path=IMAGE_PATH --prompt=PROMPT --device_id DEVICE_ID
#运行结果:
#['<img> </img>问:描述这张图片。\n答: 泰坦尼克号 电影截图']
# 运行结果
参数说明
- CONFIG_PATH:yaml配置的路径,默认是run_visualglm_6b_image_to_text_generation.yaml
- IMAGE_PATH:推理图片的路径,比如examples/titanic.jpg
- PROMPT:提示词,比如"描述这张图片。",注意要加引号
- DEVICE_ID:NPU卡的编号,默认是0
visualglm的pipeline接口推理已集成在run_visualglm_pipeline.py脚本中,运行此脚本命令示例:
cd research/visualglm
python run_visualglm_pipeline.py --config CONFIG_PATH --device_id DEVICE_ID --batch_size BATCH_SIZE --use_past True --seq_length SEQ_LENGTH \
--image_path IMAGE_PATH --prompt PROMPT
# 运行结果
#['<img> </img>问:描述这张图片。\n答: 泰坦尼克号 电影截图']
参数说明
- CONFIG_PATH:yaml配置的路径,默认是run_visualglm_6b_image_to_text_generation.yaml
- IMAGE_PATH:推理图片的路径,比如examples/titanic.jpg
- PROMPT:提示词,比如"描述这张图片。",注意要加引号
- BATCH_SIZE: 图片批次的大小,默认是1
- SEQ_LENGTH: token的长度,默认是32
- DEVICE_ID:NPU卡的编号,默认是0
MindSpore Lite推理
ckpt转换为mindir
# 如果需要使用增量推理,配置文件中use_past设置为True
cd research/visualglm
python export_lite_model.py --mode export --use_past True --device_id DEVICE_ID --seq_length SEQ_LENGTH --batch_size BATCH_SIZE
参数说明
- DEVICE_ID:NPU卡的编号,默认是0
- SEQ_LENGTH:token长度,默认是32,建议设置512
- BATCH_SIZE:批量推理的批次大小,默认是1
注意
- 设置use_past=True后会生成两个mindir的模型,分别在output/mindir_full_checkpoint和output/mindir_inc_checkpoint目录下。
- 反之不设置use_past或者use_past=False,则只生成mindir_full_checkpoint一个目录,且后续无法使用增量推理。
lite推理
[ascend_context]
plugin_custom_ops=All
provider=ge
[ge_session_options]
ge.exec.formatMode=1
ge.exec.precision_mode=must_keep_origin_dtype
ge.externalWeight=1
ge.exec.atomicCleanPolicy=1
替换/path/to/glm_6b_for_lite.ckpt为实际的glm for lite模型的路径
model:
model_config:
#...
text_config:
type: GLMConfig
#...
checkpoint_name_or_path: "/path/to/glm_6b_for_lite.ckpt" # visualglm glm weight
visualglm的lite推理已集成在run_visualglm_infer_lite脚本中,运行此脚本命令示例:
# 如果需要增量推理,使用inc_model_path指定路径,否则不需要
cd research
python run_visualglm_infer_lite.py --full_model_path FULL_MODEL_PATH --inc_model_path INC_MODEL_PATH \
--seq_length SEQ_LENGTH --ge_config context.cfg --device_id DEVICE_ID --image_path IMAGE_PATH --prompt PROMPT
# 运行结果:
#['<img> </img>问:描述这张图片。\n答: 泰坦尼克号 电影截图']
参数说明
- FULL_MODEL_PATH:上面生成的全量模型的路径,比如:output/mindir_full_checkpoint_bs_1_512/rank_0_graph.mindir,这里中间路径会带上
batch_size和seq_length的后缀,比如batch_size为1,seq_length为512下路径为output/mindir_full_checkpoint_bs_1_512/rank_0_graph.mindir
- INC_MODEL_PATH:上面生成的全量模型的路径,比如:output/mindir_inc_checkpoint_bs_1_512/rank_0_graph.mindir,这里中间路径会带上
batch_size和seq_length的后缀,比如batch_size为1,seq_length为512下路径为output/mindir_inc_checkpoint_bs_1_512/rank_0_graph.mindir
- DEVICE_ID:NPU卡的编号,默认是0
- SEQ_LENGTH:token长度,默认是32,建议设置512
- IMAGE_PATH:推理的图片路径,比如examples/titanic.jpg
- PROMPT:提示词,要加上引号
注意
- 推理设置的batch_size和seq_length要跟上面导出命令的batch_size和seq_length保持一致,否则会出错
MindSpore 微调
注意:目前lora微调只支持数据并行,不支持半自动并行和自动并行
数据集路径:
https://github.com/THUDM/VisualGLM-6B/blob/main/fewshot-data.zip
下载完以后传到服务器,解压到research/visualglm下面
记录下fewhot-data/dataset.json文件的路径
修改/research/visualglm/run_visualglm_lora.yaml文件:
- 修改所有path_to_vocab为ice_text.model词库文件的路径
- 修改所有path_to_dataset为上面数据集dataset.json文件的路径
- 修改path_to_qformer为上面转换的qformer权重文件visualglm_qformer.ckpt的路径
- 修改path_to_vit为上面转换的vit权重文件visualglm_vit.ckpt的路径
- 修改path_to_glm为上面转换的glm权重文件glm_6b.ckpt的路径
train_dataset: &train_dataset
tokenizer:
type: ChatGLMTokenizer
max_length: 2048
vocab_file: "/path_to_vocab/ice_text.model"
data_loader:
type: VisualGLMDataLoader
dataset_dir: "/path_to_dataset/dataset.json"
shuffle: False
file_format: json
random_mapping: True # if true enlarge original dataset "scale" times
scale: 1
model:
model_config:
type: VisualGLMConfig
#...
checkpoint_name_or_path: "/path_to_qformer/visualglm_qformer.ckpt"
vision_config:
type: ViTConfig
#...
checkpoint_name_or_path: "/path_to_vit/visualglm_vit.ckpt"
text_config:
type: GLMConfig
#...
checkpoint_name_or_path: "/path_to_glm/glm_6b.ckpt"
processor:
type: VisualGLMProcessor
image_processor:
type: VisualGLMImageProcessor
image_size: 224 # input image size
tokenizer:
type: ChatGLMTokenizer
max_length: 2048
vocab_file: "/path_to_vocab/ice_text.model"
调整learning rate和warmup超参,修改/research/visualglm/run_visualglm_lora.yaml文件,根据实际业务调整下面的超参:
- learning_rate: 微调的模型学习率不宜设置过大
- warmup_steps:预热步数,表示在训练开始时逐渐增加学习率的步数。这样做可以避免模型在初始阶段受到过大的梯度干扰,提高模型的泛化能力。
- num_iters:迭代次数,表示模型在一个epoch中处理数据的次数。一个epoch表示模型遍历整个数据集一次。
- total_steps:总步数,表示模型在整个训练过程中处理数据的次数。总步数等于epoch数乘以迭代次数。如果设置为-1,表示不限制总步数,只根据epoch数来决定训练的终止条件4。
# lr sechdule
lr_schedule:
type: AnnealingLR
learning_rate: 0.00001
warmup_steps: 100
num_iters: 5000
total_steps: -1 # -1 means it will load the total steps of the dataset
layer_scale: False
layer_decay: 0.65
调用下面的脚本启动微调:
cd research/visualglm
python run_visualglm_finetune.py --config CONFIG_PATH --graph_mode GRAPH_MODE --batch_size BATCH_SIZE --device_id DEVICE_ID
参数说明
- CONFIG_PATH:微调配置,默认是run_visualglm_lora.yaml
- GRAPH_MODE:图模式编号,默认是0。0:graph模式,1:pynative模式
- BATCH_SIZE:批次大小,默认是1
- DEVICE_ID:NPU卡的编号,默认是0
运行mindformers/tools/hccl_tools.py生成RANK_TABLE_FILE的json文件
这会生成一个名字为hccl_8p_01234567_XXXX.json的文件
# 运行如下命令,生成当前机器的RANK_TABLE_FILE的json文件
python mindformers/tools/hccl_tools.py --device_num "[START_ID, END_ID)"
参数说明:
- [START_ID, END_ID]: 表示卡的范围,START_ID是第一块卡的编号,END_ID是最后一块卡的编号,比如8卡为[0,8)
修改run_visualglm_lora.yaml中的并行参数
- use_parallel: 改为True
- parallel_mode:目前只支持数据并行,值为0
- data_parallel:改为上面卡的数量,比如8卡改成8
use_parallel: True
parallel:
parallel_mode: 0
parallel_config:
data_parallel: 8
model_parallel: 1
运行run_singlenode.sh脚本来执行多卡训练
- 把HCCL_JSON_PATH替换为上面生成的hccl json文件的路径
- [START_ID, END_ID]: 表示卡的范围,START_ID是第一块卡的编号,END_ID是最后一块卡的编号,要跟上面RANK_TABLE_FILE的配置保持一致;
- CARD_COUNT: 表示使用NPU卡的数量,要跟上面RANK_TABLE_FILE的配置保持一致
cd research/visualglm
bash ../run_singlenode.sh \
"python run_visualglm_finetune.py --config CONFIG_PATH --graph_mode GRAPH_MODE --batch_size BATCH_SIZE" \
HCCL_JSON_PATH [START_ID, END_ID] CARD_COUNT
参数说明
- CONFIG_PATH:微调配置,默认是run_visualglm_lora.yaml
- GRAPH_MODE:图模式编号,默认是0。0:graph模式,1:pynative模式
- BATCH_SIZE:批次大小,默认是1
- HCCL_JSON_PATH: 多机多卡HCCL通信的配置,使用上面生成的RANK_TABLE_FILE的路径
- [START_ID, END_ID]: 表示卡的范围,START_ID是第一块卡的编号,END_ID是最后一块卡的编号
- CARD_COUNT:表示使用NPU卡的数量
注意
- 这里START_ID,END_ID和CARD_COUNT要跟上面RANK_TABLE_FILE的配置保持一致
注意
- 图片路径:微调推理用的参考图片在代码仓库的finetune路径下
- 提示词:每张图片都有一个对应的参考提示词,可以在finetune_inputs.jsonl文件找到
调用预先开发好的脚本run_visualglm_with_lora.py,传入相关的图片和提示词,会得到相关的文本。
python run_visualglm_with_lora.py --lora_checkpoint CHECKPOINT_PATH --config CONFIG_PATH --image_path=IMAGE_PATH --prompt=PROMPT --device_id DEVICE_ID
#运行结果:
#['这张图片是雨天的。']
说明:
- CHECKPOINT_PATH:训练完以后生成的checkpiont的绝对路径,checkpoint一般会保存在下面的路径下output/checkpoint_trainable/rank_[id]/
- CONFIG_PATH: 表示yaml配置的路径,默认使用run_visualglm_lora.yaml
- IMAGE_PATH:表示图片的路径,比如finetune/ghost.jpg
- PROMPT:表示提示词,比如"这张图片的背景里有什么内容?",注意外面要加引号
- DEVICE_ID: 表示NPU卡的编号,默认是0