赞
踩
CogVLM 是一个强大的开源视觉语言模型(VLM),支持490*490分辨率的图像理解和多轮对话。这次我要对其微调的是单轮对话功能。关于模型的部署网络上已经有了详细的步骤,就不再赘述,官方网址:https://github.com/THUDM/CogVLM
先打开下载的代码,找到CogVLM
/finetune_demo/finetune_cogvlm_lora.sh文件,根据实际情况修改其中的内容,以下是我修改了的地方:
# 训练用的GPU数量,这里我用的两张4090
NUM_GPUS_PER_WORKER=2
# 设置模型并行数
MP_SIZE=2
# 修改要微调的模型类型,这里我用的是cogvlm-chat-v1.1
MODEL_TYPE="cogvlm-chat-v1.1"
VERSION="base"
# 修改local_tokenizer的地址,可以直接到huggingface去下载然后放到自己的目录,也可以如官方配置下这样写,之后微调时会自动下载到lmsys/vicuna-7b-v1.5目录下(但是很大概率下载失败)
MODEL_ARGS="--from_pretrained $MODEL_TYPE \
--max_length 1288 \
--lora_rank 10 \
--use_lora \
--local_tokenizer lmsys/vicuna-7b-v1.5 \
--version $VERSION"
# 修改模型所在地址,这里有的Linux系统需要写为从HOME开始的绝对路径,否则会报错(踩坑)
OPTIONS_SAT="SAT_HOME=~/.sat_models"
#修改为自己的训练集与验证集地址,这个格式稍后会说
train_data="./archive_split/train"
valid_data="./archive_split/valid"
我们注意到最后有这样一行代码,发现其执行的文件为finetune_cogvlm_demo.py(稍后再看),并且使用了deepspeed,这是一个深度学习优化库,用来分布式训练。这里想要正确运行,需要对照官方给出的requirements.txt配置环境,尤其是transformers和torch(不要想着省事,否则后面会有很多报错都是因为版本不一致,这里最好直接新建一个conda环境)
run_cmd="${OPTIONS_NCCL} ${OPTIONS_SAT} deepspeed --master_port 16666 --hostfile ${HOST_FILE_PATH} finetune_cogvlm_demo.py ${gpt_options}"
在用slurm提交作业时若出现以下错误:
!! WARNING !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Your compiler (c++ 4.8.5) may be ABI-incompatible with PyTorch! Please use a compiler that is ABI-compatible with GCC 5.0 and above. See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html. See https://gist.github.com/goldsborough/d466f43e8ffc948ff92de7486c5216d6 for instructions on how to install GCC 5 or higher. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! WARNING !! warnings.warn(ABI_INCOMPATIBILITY_WARNING.format(compiler)) Detected CUDA files, patching ldflags ... Building extension module fused_adam...
编译器版本不对将导致后面报错:
RuntimeError: Error building extension 'fused_adam'
这里是因为编译器版本不对,需要在脚本中直接指定编译器的版本,我是这样写的:
module load anaconda/2022.10
export PYTHONUNBUFFERED=1
module load cuda/12.1
source activate cogvlm
module load complier/gcc/12.2.0
在尝试运行第一次后发现报错ModuleNotFoundError: No module named 'utils.utils'
,
寻找这个模块,发现它是存在的,并且里面的__init__.py文件都是完整的,所以推测可能是因为模块重名了,这里将github上下载的utils文件名改为util,并修改导入语句,成功解决该问题。
from util.models import FineTuneTrainCogVLMModel
from util.utils import llama2_text_processor, llama2_text_processor_inference, get_image_processor
from util.utils import ItemDataset
from util.utils import llama2_tokenizer
网上查到说若想显存不够,报错CUDA out of memory
,修改batch_size=1
可以降低显存需求(当然效果会变差),发现代码里已经是1了,不需要修改,这里只是写下做记录。
由这行代码可以看出之后生成的结果所存储的位置,之后可以根据需要修改地址。
args.save = "checkpoints/merged_lora_cogvlm{}".format(args.eva_args["image_size"][0])
# 由这行代码,推测出数据集的格式应该在util.utils的某个函数中,我们可以去寻找一下。
from util.utils import ItemDataset
def create_dataset_function(image_processor, text_processor, path, args):
dataset = ItemDataset(image_processor, text_processor, args, path)
return dataset
到这里需要先看一下数据读入的格式,我们先来看官方给出的代码(部分关键地方)
class ItemDataset(Dataset): def __init__(self, image_processor, text_processor, args, data_dirs, cross_image_processor=None, **kwargs): super().__init__() self.data = self.load_data(data_dirs) self.image_processor, self.text_processor, self.cross_image_processor = image_processor, text_processor, cross_image_processor def process_text(self, answer, prompt): return self.text_processor(answer, prompt) def __getitem__(self, index): data = self.data[index] # img try: img = Image.open(data).convert('RGB') except Exception as e: print_rank0(e, level=logging.WARNING) return {} img_dict = self.process_img(img) # text label = data.split('/')[-1].split('.')[0] uni_key = label text_dict = self.process_text(label, "CAPTCHA:") if text_dict is None: print_rank0(f"Process text failed. Please check the max_target_length & max_source_length.\n The data is {data}", level=logging.WARNING) return {} # other attr ret = {**img_dict, **text_dict, "question_id": uni_key} return ret
分析发现,该数据读入的过程为:
(1)先通过self.load_data(data_dirs)
读入所有图片地址。
(2)然后交由__getitem__()
函数逐个分析。
(3)图片由img = Image.open(data).convert('RGB')
直接读取图片。
(4)文字部分由label = data.split('/')[-1].split('.')[0]
处理出这个图片的文件名,用来做后续训练时模型应该给出的回答。
(5)这里的uni_key = label
之所以可以直接写成文件名的内容,是因为官方给出的数据集是验证码的图片,没有重复的。如果是用自己的数据集且希望模型输出的内容有可能重复,这里最好给每个问题设置一个唯一的id。
(6)由 process_text()
函数中的self.text_processor(answer, prompt)
推测 text_dict = self.process_text(label, "CAPTCHA:")
中label就是模型的回答,"CAPTCHA:"就是prompt。
于是我根据自己的数据集格式,对dataset.py进行了修改,修改后的代码如下:
import os import logging import random import logging import jsonlines from io import BytesIO from PIL import Image from torch.utils.data import Dataset from sat.helpers import print_rank0 import json class ItemDataset(Dataset): def __init__(self, image_processor, text_processor, args, data_dirs, cross_image_processor=None, **kwargs): super().__init__() self.data = [] with open(data_dirs, 'r', encoding='UTF-8') as f:#训练集json的路径 for line in f.readlines(): dic = json.loads(line) self.data.append(dic) if(self.data): self.image_processor, self.text_processor, self.cross_image_processor = image_processor, text_processor, cross_image_processor else: print_rank0(f"There are no datasets",level=logging.WARNING) def process_img(self, img): img_dict = {'vision': self.image_processor(img)} if self.cross_image_processor: img_dict.update({'cross': self.cross_image_processor(img)}) return img_dict def process_text(self, answer, prompt): return self.text_processor(answer, prompt) def __len__(self): return len(self.data) def __getitem__(self, index): imgs=self.data[index]['image'] # data = self.data[index] try: img = Image.open(imgs).convert('RGB') except Exception as e: print_rank0(e, level=logging.WARNING) return {} img_dict = self.process_img(img) # text ques=self.data[index]['question'] ans=self.data[index]['answer'] uni_key = self.data[index]['question_id'] text_dict = self.process_text(ans, ques) if text_dict is None: print_rank0(f"Process text failed. Please check the max_target_length & max_source_length.\n The data is {imgs}", level=logging.WARNING) return {} # other attr ret = {**img_dict, **text_dict, "question_id": uni_key} return ret
我自己的数据集格式为:
# trains.json
{"image": "(图片路径)", "question_id": 1, "question": "图中有几个人?", "answer": "8个"}
{"image": "(图片路径)", "question_id": 2, "question": "桌子上的苹果是什么颜色的?", "answer": "绿色的"}
如果有需要的话,可以根据实际情况对其中的参数进行调整
提交作业语句:sbatch -p gpu_4090 --gpus=2 ./finetune_cogvlm_lora.sh
提交之后会生成作业号,若想实时查看日志文件,可以用tail -f slurm-作业号.out
查看。
运行成功后会有生成finetune_demo/checkpoints/merged_lora_cogvlm490,其中文件包括:
按照官方给出的指令,创建以下脚本并执行:
#! /bin/bash
# export PATH=/usr/local/cuda/bin:$PATH
# export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
module load anaconda/2022.10
export PYTHONUNBUFFERED=1
module load cuda/12.1
source activate cogvlm
module load complier/gcc/12.2.0
torchrun --standalone --nnodes=1 --nproc-per-node=2 utils/merge_model.py --version base --bf16 --from_pretrained ./checkpoints/merged_lora_cogvlm490
其中因为我在开始设置了MP_SIZE=2,所以这里的nproc-per-node也设置为2。合并完后生成:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。